chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
API Conventions
|
||||
===============
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
This document describes conventions and assumptions common to ESP-IDF Application Programming Interfaces (APIs).
|
||||
|
||||
ESP-IDF provides several kinds of programming interfaces:
|
||||
|
||||
* C functions, structures, enums, type definitions, and preprocessor macros declared in public header files of ESP-IDF components. Various pages in the API Reference section of the programming guide contain descriptions of these functions, structures, and types.
|
||||
* Build system functions, predefined variables, and options. These are documented in the :ref:`ESP-IDF CMake Build System API <cmake_buildsystem_api>`.
|
||||
* :ref:`Kconfig options <configs-in-C-Cmake>` can be used in code and in the build system (``CMakeLists.txt``) files.
|
||||
* :doc:`Host tools <../api-guides/tools/index>` and their command line parameters are also part of the ESP-IDF interfaces.
|
||||
|
||||
ESP-IDF is made up of multiple components where these components either contain code specifically written for ESP chips, or contain a third-party library (i.e., a third-party component). In some cases, third-party components contain an "ESP-IDF specific" wrapper in order to provide an interface that is either simpler or better integrated with the rest of ESP-IDF's features. In other cases, third-party components present the original API of the underlying library directly.
|
||||
|
||||
The following sections explain some of the aspects of ESP-IDF APIs and their usage.
|
||||
|
||||
Error Handling
|
||||
--------------
|
||||
|
||||
Most ESP-IDF APIs return error codes defined with the :cpp:type:`esp_err_t` type. See :doc:`Error Handling <../api-guides/error-handling>` section for more information about error handling approaches. :doc:`Error Codes Reference <error-codes>` contains the list of error codes returned by ESP-IDF components.
|
||||
|
||||
.. _api_reference_config_structures:
|
||||
|
||||
Configuration Structures
|
||||
------------------------
|
||||
|
||||
.. important:: Correct initialization of configuration structures is an important part of making the application compatible with future versions of ESP-IDF.
|
||||
|
||||
Most initialization, configuration, and installation functions in ESP-IDF (typically named ``..._init()``, ``..._config()``, and ``..._install()``) take a configuration structure pointer as an argument. For example::
|
||||
|
||||
const esp_timer_create_args_t my_timer_args = {
|
||||
.callback = &my_timer_callback,
|
||||
.arg = callback_arg,
|
||||
.name = "my_timer"
|
||||
};
|
||||
esp_timer_handle_t my_timer;
|
||||
esp_err_t err = esp_timer_create(&my_timer_args, &my_timer);
|
||||
|
||||
These functions never store the pointer to the configuration structure, so it is safe to allocate the structure on the stack.
|
||||
|
||||
The application must initialize all fields of the structure. The following is incorrect::
|
||||
|
||||
esp_timer_create_args_t my_timer_args;
|
||||
my_timer_args.callback = &my_timer_callback;
|
||||
/* Incorrect! Fields .arg and .name are not initialized */
|
||||
esp_timer_create(&my_timer_args, &my_timer);
|
||||
|
||||
Most ESP-IDF examples use C99 `designated initializers`_ for structure initialization since they provide a concise way of setting a subset of fields, and zero-initializing the remaining fields::
|
||||
|
||||
const esp_timer_create_args_t my_timer_args = {
|
||||
.callback = &my_timer_callback,
|
||||
/* Correct, fields .arg and .name are zero-initialized */
|
||||
};
|
||||
|
||||
The C++ language supports designated initializer syntax, too, but the initializers must be in the order of declaration. When using ESP-IDF APIs in C++ code, you may consider using the following pattern::
|
||||
|
||||
/* Correct, fields .dispatch_method, .name and .skip_unhandled_events are zero-initialized */
|
||||
const esp_timer_create_args_t my_timer_args = {
|
||||
.callback = &my_timer_callback,
|
||||
.arg = &my_arg,
|
||||
};
|
||||
|
||||
///* Incorrect, .arg is declared after .callback in esp_timer_create_args_t */
|
||||
//const esp_timer_create_args_t my_timer_args = {
|
||||
// .arg = &my_arg,
|
||||
// .callback = &my_timer_callback,
|
||||
//};
|
||||
|
||||
For more information on designated initializers, see :ref:`cplusplus_designated_initializers`. Note that C++ language versions older than C++20, which are not the default in the current version of ESP-IDF, do not support designated initializers. If you have to compile code with an older C++ standard than C++20, you may use GCC extensions to produce the following pattern::
|
||||
|
||||
esp_timer_create_args_t my_timer_args = {};
|
||||
/* All the fields are zero-initialized */
|
||||
my_timer_args.callback = &my_timer_callback;
|
||||
|
||||
Default Initializers
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For some configuration structures, ESP-IDF provides macros for setting default values of fields::
|
||||
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
/* HTTPD_DEFAULT_CONFIG expands to a designated initializer. Now all fields are set to the default values, and any field can still be modified: */
|
||||
config.server_port = 8081;
|
||||
httpd_handle_t server;
|
||||
esp_err_t err = httpd_start(&server, &config);
|
||||
|
||||
It is recommended to use default initializer macros whenever they are provided for a particular configuration structure.
|
||||
|
||||
.. _api_reference_private_apis:
|
||||
|
||||
Private APIs
|
||||
------------
|
||||
|
||||
Certain header files in ESP-IDF contain APIs intended to be used only in ESP-IDF source code rather than by the applications. Such header files often contain ``private`` or ``esp_private`` in their name or path. Certain components, such as :doc:`hal <../api-guides/hardware-abstraction>` only contain private APIs.
|
||||
|
||||
Private APIs may be removed or changed in an incompatible way between minor or patch releases.
|
||||
|
||||
.. _api_reference_example_components:
|
||||
|
||||
Components in Example Projects
|
||||
------------------------------
|
||||
|
||||
ESP-IDF examples contain a variety of projects demonstrating the usage of ESP-IDF APIs. In order to reduce code duplication in the examples, a few common helpers are defined inside components that are used by multiple examples. This includes components located in :example:`common_components` directory, as well as some of the components located in the examples themselves. These components are not considered to be part of the ESP-IDF API.
|
||||
|
||||
It is not recommended to reference these components directly in custom projects (via ``EXTRA_COMPONENT_DIRS`` build system variable), as they may change significantly between ESP-IDF versions. When starting a new project based on an ESP-IDF example, copy both the project and the common components it depends on out of ESP-IDF, and treat the common components as part of the project. Note that the common components are written with examples in mind, and might not include all the error handling required for production applications. Before using, take time to read the code and understand if it is applicable to your use case.
|
||||
|
||||
API Stability
|
||||
-------------
|
||||
|
||||
ESP-IDF uses `Semantic Versioning <https://semver.org/>`_ as explained in the :ref:`Versioning Scheme <versioning-scheme>`.
|
||||
|
||||
Minor and bugfix releases of ESP-IDF guarantee compatibility with previous releases. The sections below explain different aspects and limitations to compatibility.
|
||||
|
||||
Source-level Compatibility
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
ESP-IDF guarantees source-level compatibility of C functions, structures, enums, type definitions, and preprocessor macros declared in public header files of ESP-IDF components. Source-level compatibility implies that the application source code can be recompiled with the newer version of ESP-IDF without changes.
|
||||
|
||||
The following changes are allowed between minor versions and do not break source-level compatibility:
|
||||
|
||||
* Deprecating functions (using the ``deprecated`` attribute) and header files (using a preprocessor ``#warning``). Deprecations are listed in ESP-IDF release notes. It is recommended to update the source code to use the newer functions or files that replace the deprecated ones, however, this is not mandatory. Deprecated functions and files can be removed from major versions of ESP-IDF.
|
||||
* Renaming components, moving source and header files between components — provided that the build system ensures that correct files are still found.
|
||||
* Renaming Kconfig options. Kconfig system's :ref:`backward compatibility <configuration-options-compatibility>` ensures that the original Kconfig option names can still be used by the application in ``sdkconfig`` file, CMake files, and source code.
|
||||
|
||||
Lack of Binary Compatibility
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
ESP-IDF does not guarantee binary compatibility between releases. This means that if a precompiled library is built with one ESP-IDF version, it is not guaranteed to work the same way with the next minor or bugfix release. The following are the possible changes that keep source-level compatibility but not binary compatibility:
|
||||
|
||||
* Changing numerical values for C enum members.
|
||||
* Adding new structure members or changing the order of members. See :ref:`api_reference_config_structures` for tips that help ensure compatibility.
|
||||
* Replacing an ``extern`` function with a ``static inline`` one with the same signature, or vice versa.
|
||||
* Replacing a function-like macro with a compatible C function.
|
||||
|
||||
Other Exceptions from Compatibility
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
While we try to make upgrading to a new ESP-IDF version easy, there are parts of ESP-IDF that may change between minor versions in an incompatible way. We appreciate issuing reports about any unintended breaking changes that do not fall into the categories below.
|
||||
|
||||
* :ref:`api_reference_private_apis`.
|
||||
* :ref:`api_reference_example_components`.
|
||||
* Features clearly marked as "beta", "preview", or "experimental".
|
||||
* Changes made to mitigate security issues or to replace insecure default behaviors with secure ones.
|
||||
* Features that were never functional. For example, if it was never possible to use a certain function or an enumeration value, it may get renamed (as part of fixing it) or removed. This includes software features that depend on non-functional chip hardware features.
|
||||
* Unexpected or undefined behavior that is not documented explicitly may be fixed/changed, such as due to missing validation of argument ranges.
|
||||
* Location of :ref:`Kconfig <project-configuration-guide>` options in menuconfig.
|
||||
* Location and names of example projects.
|
||||
|
||||
.. _designated initializers: https://en.cppreference.com/w/c/language/struct_initialization
|
||||
@@ -0,0 +1,35 @@
|
||||
Bluetooth\ :sup:`®` Common
|
||||
===============================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
**Bluetooth Common** provides shared definitions and APIs used by both Bluetooth Classic and Bluetooth Low Energy (Bluetooth LE). It serves as a foundation for shared functionality across Bluetooth components, offering unified interfaces for Bluetooth initialization, configuration, and device management.
|
||||
|
||||
The Bluetooth Common API is organized into the following parts:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Bluetooth Define <esp_bt_defs>
|
||||
|
||||
Provides shared definitions and data structures used by both Bluetooth Classic and Bluetooth LE.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Bluetooth Main <esp_bt_main>
|
||||
|
||||
Provides core APIs for initializing, enabling/disabling, and managing the Bluetooth stack.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Bluetooth Device <esp_bt_device>
|
||||
|
||||
Provides APIs for managing device-level properties such as address, name, visibility, and coexistence settings.
|
||||
|
||||
Each part typically includes the following sections:
|
||||
|
||||
- **Overview**: Summary of purpose, key functionality, and main interfaces
|
||||
- **Application Examples**: Example projects demonstrating typical usage
|
||||
- **API Reference**: Detailed API documentation, including header files, functions, structures, macros, type definitions, and enumerations
|
||||
@@ -0,0 +1,44 @@
|
||||
Bluetooth\ :sup:`®` Low Energy
|
||||
====================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
**Bluetooth Low Energy (Bluetooth LE)** provides low-power wireless communication for IoT devices such as wearables, sensors, and smart home products. This section documents the **Bluetooth LE API Reference**, covering APIs for device discovery, data exchange, and Wi-Fi provisioning over Bluetooth LE.
|
||||
|
||||
For usage concepts and tutorials, see :doc:`Bluetooth Low Energy <../../../api-guides/ble/index>` in **API Guides**.
|
||||
|
||||
The Bluetooth LE API in ESP-IDF is organized into the following parts:
|
||||
|
||||
- :doc:`Bluetooth Low Energy GAP <esp_gap_ble>`
|
||||
|
||||
Handles advertising, scanning, connection management, and security operations
|
||||
|
||||
- :doc:`Bluetooth Low Energy GATT Define <esp_gatt_defs>`
|
||||
|
||||
Common data types and constants for attributes, characteristics, and UUIDs used in GATT operations
|
||||
|
||||
- :doc:`Bluetooth Low Energy GATT Server <esp_gatts>`
|
||||
|
||||
Exposes services and characteristics to remote clients (peripheral role)
|
||||
|
||||
- :doc:`Bluetooth Low Energy GATT Client <esp_gattc>`
|
||||
|
||||
Discovers and accesses services on remote servers (central role)
|
||||
|
||||
.. only:: SOC_BLUFI_SUPPORTED
|
||||
|
||||
- :doc:`Bluetooth Low Energy BluFi <esp_blufi>`
|
||||
|
||||
Enables Wi-Fi provisioning and configuration via Bluetooth LE
|
||||
|
||||
Each part typically includes an **Overview**, **Application Examples**, and **API Reference**, covering purpose, main functionality, sample usage, and detailed API documentation.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:hidden:
|
||||
|
||||
Bluetooth Low Energy GAP <esp_gap_ble>
|
||||
Bluetooth Low Energy GATT Define <esp_gatt_defs>
|
||||
Bluetooth Low Energy GATT Server <esp_gatts>
|
||||
Bluetooth Low Energy GATT Client <esp_gattc>
|
||||
:SOC_BLUFI_SUPPORTED: Bluetooth Low Energy BluFi <esp_blufi>
|
||||
@@ -0,0 +1,380 @@
|
||||
:orphan:
|
||||
|
||||
HCI Vendor-specific (VS) Commands
|
||||
==========================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
HCI VS Commands for Espressif's Bluetooth Host
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following HCI VS commands are exclusively for Espressif's Bluetooth Host (ESP-Bluedroid Host or ESP-NimBLE Host). If you are using a non-ESP host or HCI UART, these commands will remain disabled unless the initialization function is explicitly called from the application. Note, these init functions as well as these additional HCI VS commands are intended for Espressif's Bluetooth Host use only. Application developers **should not** call the init functions in their applications.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_COMMON_ECHO_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_COEX_STATUS_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CONFIG_DUP_EXC_LIST_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_ADV_REPORT_FLOW_CTRL_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_UPD_ADV_REPORT_FLOW_CTRL_NUM_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CLR_LEGACY_ADV_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_MIN_ENC_KEY_SIZE_OCF
|
||||
|
||||
|
||||
.. only:: esp32c3 or esp32s3
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_COMMON_ECHO_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CONFIG_DUP_EXC_LIST_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_ADV_REPORT_FLOW_CTRL_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_UPD_ADV_REPORT_FLOW_CTRL_NUM_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CLR_LEGACY_ADV_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_ENABLE_CSA2_OCF
|
||||
|
||||
|
||||
.. only:: esp32c2
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_SET_ADV_REPORT_FLOW_CTRL_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_UPD_ADV_REPORT_FLOW_CTRL_NUM_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CLR_LEGACY_ADV_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_ENABLE_CSA2_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_LE_VENDOR_EVTS_MASK_OCF
|
||||
|
||||
|
||||
.. only:: esp32c5 or esp32c6 or esp32h2 or esp32h21 or esp32c61 or esp32h4 or esp32s31
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_CONFIG_DUP_EXC_LIST_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_ADV_REPORT_FLOW_CTRL_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_UPD_ADV_REPORT_FLOW_CTRL_NUM_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CLR_LEGACY_ADV_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_DUP_LIST_PARAMS_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_ENABLE_DUP_EXC_LIST_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_ENABLE_ARRANGEMENT_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_SCHED_ROLE_LEN_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_PCL_RSSI_THRESH_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_ENABLE_CSA2_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_LOG_PARAMS_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_LE_VENDOR_EVTS_MASK_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_CONST_PEER_SCA_OCF
|
||||
|
||||
|
||||
HCI VS Events for Espressif's Bluetooth Host
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following HCI VS events are exclusively for Espressif's Bluetooth Host (ESP-Bluedroid Host or ESP-NimBLE Host). If you are using a non-ESP host or HCI UART, these events will remain disabled unless the initialization function is explicitly called from the application. Note, these init functions as well as these additional HCI VS events are intended for Espressif's Bluetooth Host use only. Application developers **should not** call the init functions in their applications.
|
||||
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_LE_ADV_LOST_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_LEGACY_REM_AUTH_EVT_SUBCODE
|
||||
|
||||
|
||||
.. only:: esp32c3 or esp32s3
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_LE_ADV_LOST_EVT_SUBCODE
|
||||
|
||||
|
||||
.. only:: esp32c2
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_LE_CONN_SCAN_REQ_RXED_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_LE_CHAN_UPDATE_COMP_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_LE_SLEEP_WAKEUP_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_LE_ADV_LOST_EVT_SUBCODE
|
||||
|
||||
|
||||
.. only:: esp32c6 or esp32h2 or esp32h21 or esp32c5 or esp32c61 or esp32h4
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_LE_CONN_SCAN_REQ_RXED_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_LE_CHAN_UPDATE_COMP_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_LE_SLEEP_WAKEUP_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_LE_ADV_LOST_EVT_SUBCODE
|
||||
|
||||
.. only:: esp32s31
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_LE_CONN_SCAN_REQ_RXED_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_LE_CHAN_UPDATE_COMP_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_LE_SLEEP_WAKEUP_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_LE_ADV_LOST_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_LEGACY_REM_AUTH_EVT_SUBCODE
|
||||
|
||||
|
||||
HCI VS Commands for Espressif's Internal-Use Debugging
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following HCI VS debugging commands are implemented in Bluetooth Low Energy controller pre-compiled libraries. These commands are not linked into the application binary, unless the function `esp_ble_internalTestFeaturesEnable(true)` is called from the application. They are intended for Espressif's internal use only. Application developers **should not** call `esp_ble_internalTestFeaturesEnable(true)` in their applications.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RELATED_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_ADV_DELAY_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_FOREVER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_EXPECTED_PEER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_ADV_TXED_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_SCAN_RXED_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_TXPWR_LVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_LVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_CLEAR_RAND_ADDR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_MAX_TXPWR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_RANGE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_AA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_ADV_AA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_CHAN_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CTRL_STATUS_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CTRL_COMPILE_VER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RELATED_SUBCMD_MAX
|
||||
|
||||
The following HCI VS debugging commands are implemented in Bluetooth Classic controller pre-compiled libraries. These commands are not linked into the application binary, unless the corresponding initialization function is explicitly called from the application. They are intended for Espressif's internal use only. Application developers **should not** call in their applications.
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_WR_DM1_ENABLE_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CLK_UPDATE_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_AFH_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_EVT_MASK_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_AFH_REPORTING_MODE_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_MASK_RMT_CHANNEL_CLASSIFICATION_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_WR_AUTO_RATE_INIT_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_RD_ACL_REAL_RSSI_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_RD_NEW_CONN_TX_PWR_LVL_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_WR_NEW_CONN_TX_PWR_LVL_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_RD_PAGE_TX_PWR_LVL_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_WR_PAGE_TX_PWR_LVL_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_RD_PSCAN_TX_PWR_LVL_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_WR_PSCAN_TX_PWR_LVL_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_RD_INQ_TX_PWR_LVL_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_WR_ISCAN_TX_PWR_LVL_OCF
|
||||
|
||||
|
||||
.. only:: esp32c3 or esp32s3
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RELATED_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_ADV_DELAY_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_PREF_CODED_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_DEFAULT_PRIV_MODE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_FOREVER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_EXPECTED_PEER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_ADV_TXED_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_SCAN_RXED_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_TXPWR_LVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_LVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_TXPWR_LVL_ENH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_LVL_ENH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_CCA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_CLEAR_RAND_ADDR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_MAX_TXPWR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_RANGE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_AA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_ADV_AA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_CHAN_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CTRL_STATUS_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CTRL_COMPILE_VER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_AUX_ADV_OFFSET_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_AUX_OFFSET_THRESHOLD_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RELATED_SUBCMD_MAX
|
||||
|
||||
.. only:: esp32c2
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RELATED_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_ADV_DELAY_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_PREF_CODED_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_DEFAULT_PRIV_MODE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_FOREVER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_EXPECTED_PEER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_ADV_TXED_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_SCAN_RXED_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_TXPWR_LVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_LVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_TXPWR_LVL_ENH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_LVL_ENH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_IGNORE_WL_FOR_DIR_ADV_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_ADV_RXED_RSSI_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_CCA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_CCA_WIN_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_READ_CCA_DATA_SUBCM
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_CLEAR_RAND_ADDR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_MAX_TXPWR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_RANGE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_AA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_ADV_AA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_CHAN_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SKIP_LIGHT_SLEEP_CHECK_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_WAKEUP_OVERHEAD_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_ADV_MIN_ITVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CTRL_STATUS_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_RECODE_RX_STATE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_RECODE_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_CLR_RECODE_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CTRL_COMPILE_VER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_AUX_ADV_OFFSET_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_BACKOFF_UPLIMIT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_RXED_ADV_ADI_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_RX_SENS_THRESH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_AGC_MAX_GAIN_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RELATED_SUBCMD_MAX
|
||||
|
||||
|
||||
.. only:: esp32c6 or esp32h2 or esp32h21 or esp32c5 or esp32c61 or esp32h4
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RELATED_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_ADV_DELAY_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_PREF_CODED_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_DEFAULT_PRIV_MODE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_FOREVER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_EXPECTED_PEER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_ADV_TXED_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_SCAN_RXED_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_TXPWR_LVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_LVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_TXPWR_LVL_ENH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_LVL_ENH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_IGNORE_WL_FOR_DIR_ADV_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_ADV_RXED_RSSI_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_CCA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_CCA_WIN_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_READ_CCA_DATA_SUBCM
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_CLEAR_RAND_ADDR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_MAX_TXPWR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_RANGE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_AA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_ADV_AA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_CHAN_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SKIP_LIGHT_SLEEP_CHECK_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_WAKEUP_OVERHEAD_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_ADV_MIN_ITVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CTRL_STATUS_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_CONN_PHY_TXPWR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CONN_PHY_TXPWR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_RXBUF_EMPTY_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RESTART_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_RECODE_RX_STATE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_RECODE_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_CLR_RECODE_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CTRL_COMPILE_VER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_AUX_ADV_OFFSET_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_INIT_FLEXIBLE_MODE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_FLEXIBLE_MODE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_FLEXIBLE_CONN_ERR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_FLEXIBLE_ADV_ERR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_FLEXIBLE_SCAN_ERR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXED_CRCERR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_BACKOFF_UPLIMIT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_RXED_ADV_ADI_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCH_RAND_MODE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_RX_SENS_THRESH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_CHECK_MSYS_BUF_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_UPDATE_BLE_TIMER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_UPDATE_BLE_RTC_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_LOCKED_MEM_NUM_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ALLOW_MEM_ALLOC_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCH_RAND_INFO_PTR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_DIAG_IO_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_AGC_MAX_GAIN_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_CHAN_ASSESS_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_BACKOFF_UPLIMIT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_CONN_TOP_PRIO_RESV_THRESH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_TEST_EVT_MSK_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_WAKEUP_TIMEOUT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RELATED_SUBCMD_MAX
|
||||
|
||||
.. only:: esp32s31
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RELATED_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_ADV_DELAY_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_PREF_CODED_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_DEFAULT_PRIV_MODE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_FOREVER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_EXPECTED_PEER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_ADV_TXED_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_SCAN_RXED_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_TXPWR_LVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_LVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_TXPWR_LVL_ENH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_LVL_ENH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_IGNORE_WL_FOR_DIR_ADV_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_ADV_RXED_RSSI_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_CCA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_CCA_WIN_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_READ_CCA_DATA_SUBCM
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_CLEAR_RAND_ADDR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_MAX_TXPWR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXPWR_RANGE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_AA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_ADV_AA_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCAN_CHAN_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SKIP_LIGHT_SLEEP_CHECK_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_WAKEUP_OVERHEAD_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_ADV_MIN_ITVL_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CTRL_STATUS_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_CONN_PHY_TXPWR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CONN_PHY_TXPWR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_RXBUF_EMPTY_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RESTART_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_RECODE_RX_STATE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_RECODE_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_CLR_RECODE_CNT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_CTRL_COMPILE_VER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_AUX_ADV_OFFSET_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_INIT_FLEXIBLE_MODE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_FLEXIBLE_MODE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_FLEXIBLE_CONN_ERR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_FLEXIBLE_ADV_ERR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_FLEXIBLE_SCAN_ERR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_TXED_CRCERR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_BACKOFF_UPLIMIT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_RXED_ADV_ADI_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCH_RAND_MODE_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_RX_SENS_THRESH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_CHECK_MSYS_BUF_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_UPDATE_BLE_TIMER_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_UPDATE_BLE_RTC_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_LOCKED_MEM_NUM_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ALLOW_MEM_ALLOC_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_SCH_RAND_INFO_PTR_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_DIAG_IO_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_AGC_MAX_GAIN_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_ENABLE_CHAN_ASSESS_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_BACKOFF_UPLIMIT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_CONN_TOP_PRIO_RESV_THRESH_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_SET_TEST_EVT_MSK_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_GET_WAKEUP_TIMEOUT_SUBCMD
|
||||
.. doxygendefine:: ESP_BT_VS_CFG_TEST_RELATED_SUBCMD_MAX
|
||||
|
||||
The following HCI VS debugging commands are implemented in Bluetooth Classic controller pre-compiled libraries. These commands are not linked into the application binary, unless the corresponding initialization function is explicitly called from the application. They are intended for Espressif's internal use only. Application developers **should not** call in their applications.
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_WR_DM1_ENABLE_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_CLK_UPDATE_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_PCA_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_AFH_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_EVT_MASK_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_SET_AFH_REPORTING_MODE_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_MASK_RMT_CHANNEL_CLASSIFICATION_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_DTM_TX_TEST_START_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_DTM_RX_TEST_START_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_DTM_TX_TEST_END_OCF
|
||||
.. doxygendefine:: ESP_BT_VS_DTM_RX_TEST_END_OCF
|
||||
|
||||
HCI VS Events for Espressif's Internal-Use Debugging
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following HCI VS debugging events are implemented in Bluetooth controller pre-compiled libraries. These events are not linked into the application binary and are intended for Espressif's internal use only. Application developers **should not** call the corresponding initialization function in their applications.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_AFH_CHG_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_CH_CLASSIFICATION_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_CH_CLASSIFICATION_REPORTING_MODE_EVT_SUBCODE
|
||||
|
||||
|
||||
.. only:: esp32c5 or esp32c6 or esp32c61 or esp32h2 or esp32h21 or esp32h4
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_LE_RUNNING_STATUS_EVT_SUBCODE
|
||||
|
||||
.. only:: esp32s31
|
||||
|
||||
.. doxygendefine:: ESP_BT_VS_LE_RUNNING_STATUS_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_AFH_CHG_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_CH_CLASSIFICATION_EVT_SUBCODE
|
||||
.. doxygendefine:: ESP_BT_VS_CH_CLASSIFICATION_REPORTING_MODE_EVT_SUBCODE
|
||||
@@ -0,0 +1,79 @@
|
||||
Bluetooth\ :sup:`®` Classic
|
||||
===============================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
**Bluetooth Classic** provides APIs for implementing traditional Bluetooth functionalities, including audio streaming, device communication, and data exchange over the Serial Port Profile. It supports multiple Bluetooth profiles, allowing ESP devices to act as source or sink in scenarios like wireless audio, remote control, and data transmission.
|
||||
|
||||
The Bluetooth Classic API provides the following main features:
|
||||
|
||||
- Core protocol support (**GAP**, **L2CAP**, and **SDP**)
|
||||
- Serial data communication (**SPP**)
|
||||
- High-quality audio streaming (**A2DP**)
|
||||
- Media playback control (**AVRCP**)
|
||||
- Hands-free calling support (**HFP**)
|
||||
- Input device connectivity (**HID** host and device roles)
|
||||
|
||||
----
|
||||
|
||||
The Bluetooth Classic API in ESP-IDF is organized into the following parts:
|
||||
|
||||
**Core Protocols**
|
||||
|
||||
- :doc:`Bluetooth GAP <esp_gap_bt>`
|
||||
|
||||
**Generic Access Profile (GAP):** Device discovery, pairing, and security management
|
||||
|
||||
- :doc:`Bluetooth L2CAP <esp_l2cap_bt>`
|
||||
|
||||
**Logical Link Control and Adaptation Protocol (L2CAP):** Data multiplexing and channel management
|
||||
|
||||
- :doc:`Bluetooth SDP <esp_sdp>`
|
||||
|
||||
**Service Discovery Protocol (SDP):** Discovers remote device services and attributes
|
||||
|
||||
**Communication Profile**
|
||||
|
||||
- :doc:`Bluetooth SPP <esp_spp>`
|
||||
|
||||
**Serial Port Profile (SPP):** Emulates a serial communication channel over Bluetooth for data exchange
|
||||
|
||||
**Audio and Media Profiles**
|
||||
|
||||
- :doc:`Bluetooth A2DP <esp_a2dp>`
|
||||
|
||||
**Advanced Audio Distribution Profile (A2DP):** High-quality audio streaming (source and sink)
|
||||
|
||||
- :doc:`Bluetooth AVRCP <esp_avrc>`
|
||||
|
||||
**Audio/Video Remote Control Profile (AVRCP):** Media playback control (play, pause, volume)
|
||||
|
||||
**Hands-Free Profile (HFP)**
|
||||
|
||||
- :doc:`Bluetooth HFP Define <esp_hf_defs>`: Core definitions shared by HFP roles
|
||||
- :doc:`Bluetooth HFP Client <esp_hf_client>`: Implements the hands-free unit role (e.g., headset, car kit)
|
||||
- :doc:`Bluetooth HFP AG <esp_hf_ag>`: Implements the audio gateway role (e.g., mobile phone)
|
||||
|
||||
**Human Interface Device (HID)**
|
||||
|
||||
- :doc:`Bluetooth HID Device <esp_hidd>`: Implements peripheral roles such as keyboard, mouse, or game controller
|
||||
- :doc:`Bluetooth HID Host <esp_hidh>`: Implements the host role for connecting to remote HID devices
|
||||
|
||||
Each part typically includes an **Overview**, **Application Examples**, and **API Reference**, covering purpose, main functionality, sample usage, and detailed API documentation.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:hidden:
|
||||
|
||||
Bluetooth GAP <esp_gap_bt>
|
||||
Bluetooth L2CAP <esp_l2cap_bt>
|
||||
Bluetooth SDP <esp_sdp>
|
||||
Bluetooth SPP <esp_spp>
|
||||
Bluetooth A2DP <esp_a2dp>
|
||||
Bluetooth AVRCP <esp_avrc>
|
||||
Bluetooth HFP Define <esp_hf_defs>
|
||||
Bluetooth HFP Client <esp_hf_client>
|
||||
Bluetooth HFP AG <esp_hf_ag>
|
||||
Bluetooth HID Device <esp_hidd>
|
||||
Bluetooth HID Host <esp_hidh>
|
||||
@@ -0,0 +1,32 @@
|
||||
Controller & HCI
|
||||
==================================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
.. only:: esp32 or esp32c3 or esp32s3
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
- :example:`bluetooth/hci/ble_adv_scan_combined` demonstrates how to use Bluetooth capabilities for advertising and scanning with a virtual Host Controller Interface (HCI). This example shows how to implement some host functionalities without a host and displays scanned advertising reports from other devices.
|
||||
|
||||
- :example:`bluetooth/hci/controller_hci_uart_esp32` demonstrates how to configure the Bluetooth LE Controller's HCI to communicate over UART on {IDF_TARGET_NAME}, enabling communication with an external Bluetooth host stack.
|
||||
|
||||
- :example:`bluetooth/hci/controller_vhci_ble_adv` demonstrates how to use the ESP-IDF VHCI ble_advertising app to perform advertising without a host and display received HCI events from the controller.
|
||||
|
||||
.. only:: esp32c3 or esp32s3
|
||||
|
||||
- :example:`bluetooth/hci/controller_hci_uart_esp32c3_and_esp32s3` demonstrates how to configure the Bluetooth LE Controller's HCI to communicate over UART on {IDF_TARGET_NAME}, enabling communication with an external Bluetooth host stack.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_bt.inc
|
||||
|
||||
|
||||
HCI Vendor-specific (VS) Commands
|
||||
--------------------------------------
|
||||
|
||||
Espressif's HCI VS commands are exclusively designed for use with Espressif's Bluetooth Host stack or internal debugging purposes. Application developers **should not** initialize or invoke these VS commands in their applications. Please refer to :doc:`bt_vhci` for detailed information.
|
||||
@@ -0,0 +1,243 @@
|
||||
ESP-BLE-AUDIO
|
||||
=============
|
||||
|
||||
ESP-BLE-AUDIO provides APIs for Bluetooth LE Audio, enabling time-synchronized audio over BLE using the Bluetooth Core Specification LE Audio architecture. Applications can implement unicast (one-to-one) and broadcast audio scenarios, including hearing aids, headsets, speakers, and broadcast sources/sinks.
|
||||
|
||||
The LE Audio stack is built on the **Bluetooth Core** and the **LC3** codec. The **generic audio framework** includes the Common Audio Profile (CAP), Common Audio Service (CAS), and the profiles and services below. **Use case specific profiles** (HAP, GMAP, TMAP, PBP) sit on top of the generic framework. LE Audio runs over the LE Isochronous Channels (CIS/BIS) supported by :doc:`esp-ble-iso`.
|
||||
|
||||
For conceptual background and architectural details, see the :doc:`Bluetooth LE Audio documentation <../../api-guides/esp-ble-audio/ble-audio-index>`, in particular the :doc:`ESP-BLE-AUDIO architecture <../../api-guides/esp-ble-audio/ble-audio-architecture-lea>`.
|
||||
|
||||
.. warning::
|
||||
|
||||
This is a **preview release**. The ESP-BLE-AUDIO APIs, data structures, and configuration parameters are subject to change in future releases. Breaking changes — such as renamed or restructured types, modified function signatures, or removed fields — may be introduced without prior notice.
|
||||
|
||||
**Generic audio framework**
|
||||
|
||||
* **Common Audio Profile (CAP)** — Top-level profile for coordinating audio procedures across single or multiple devices.
|
||||
* **Common Audio Service (CAS)** — GATT service companion to CAP; used for coordinated set member announcement.
|
||||
* **Basic Audio Profile (BAP)** — Unicast and broadcast stream setup, codec configuration, and stream control.
|
||||
* **Published Audio Capabilities Service (PACS)** — Advertising and negotiating audio capabilities.
|
||||
* **Audio Stream Control Service (ASCS)** — Exposes sink/source audio endpoints on a BAP Unicast Server; used by the Unicast Client to configure and control streams.
|
||||
* **Broadcast Audio Scan Service (BASS)** — Allows a Broadcast Assistant to offload BIS scanning from a low-power Scan Delegator.
|
||||
* **Volume Control Profile (VCP)** — Volume control (VCS, VOCS, AICS).
|
||||
* **Volume Offset Control Service (VOCS)** — Per-output volume offset.
|
||||
* **Audio Input Control Service (AICS)** — Audio input (microphone) state and gain.
|
||||
* **Microphone Control Profile (MICP)** — Microphone mute and gain (MICS).
|
||||
* **Coordinated Set Identification Profile (CSIP)** — Set member identification for coordinated sets (e.g., left/right earbuds).
|
||||
* **Media Control Profile (MCP)** — Media control (MCS).
|
||||
* **Media Control Service (MCS)** — Media control service.
|
||||
* **Call Control Profile (CCP)** — Call/telephony control (TBS).
|
||||
* **Telephone Bearer Service (TBS)** — Telephony bearer and in-call control.
|
||||
* **Media Proxy** — Media proxy service (Zephyr implementation component; not a Bluetooth SIG specification).
|
||||
|
||||
**Use case specific profiles**
|
||||
|
||||
* **Hearing Access Profile (HAP)** — Hearing aid presets and presets control.
|
||||
* **Gaming Audio Profile (GMAP)** — Gaming audio (e.g. Unicast Game Gateway/Terminal, Broadcast Game Sender/Receiver).
|
||||
* **Telephony and Media Audio Profile (TMAP)** — Interoperability configurations for telephony and media audio use cases.
|
||||
* **Public Broadcast Profile (PBP)** — Discovery and subscription to public broadcast streams.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
* **BAP (Basic Audio Profile)**
|
||||
|
||||
* :example:`bluetooth/esp_ble_audio/bap/broadcast_sink` demonstrates how to act as a BAP Broadcast Sink that synchronizes to a broadcast source and receives BIS audio streams.
|
||||
* :example:`bluetooth/esp_ble_audio/bap/broadcast_source` demonstrates how to act as a BAP Broadcast Source that creates a BIG and sends broadcast audio over BIS.
|
||||
* :example:`bluetooth/esp_ble_audio/bap/unicast_client` demonstrates how to discover and connect to a unicast server and establish BAP unicast streams.
|
||||
* :example:`bluetooth/esp_ble_audio/bap/unicast_server` demonstrates how to advertise and accept unicast connections and serve BAP unicast streams.
|
||||
|
||||
* **CAP (Common Audio Profile)**
|
||||
|
||||
* :example:`bluetooth/esp_ble_audio/cap/acceptor` demonstrates how to act as a CAP Acceptor for unicast and broadcast flows.
|
||||
* :example:`bluetooth/esp_ble_audio/cap/initiator` demonstrates how to act as a CAP Initiator for unicast and broadcast flows.
|
||||
|
||||
* **TMAP (Telephony and Media Audio Profile)**
|
||||
|
||||
* :example:`bluetooth/esp_ble_audio/tmap/bmr` demonstrates the TMAP Broadcast Media Receiver (BMR) role that receives broadcast audio from a BMS.
|
||||
* :example:`bluetooth/esp_ble_audio/tmap/bms` demonstrates the TMAP Broadcast Media Sender (BMS) role that sends broadcast audio.
|
||||
* :example:`bluetooth/esp_ble_audio/tmap/central` demonstrates the TMAP Call Gateway (CG) and Unicast Media Sender (UMS) roles.
|
||||
* :example:`bluetooth/esp_ble_audio/tmap/peripheral` demonstrates the TMAP Call Terminal (CT) and Unicast Media Receiver (UMR) roles.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
ESP-BLE-AUDIO APIs are divided into the following parts:
|
||||
|
||||
* `Definitions <ESP-BLE-AUDIO Definitions_>`_
|
||||
|
||||
* `General Definitions`_
|
||||
* `LC3 Codec Definitions`_
|
||||
* `MCS Definitions`_
|
||||
* `BAP LC3 Preset Definitions`_
|
||||
* `GMAP LC3 Preset Definitions`_
|
||||
|
||||
* `API <ESP-BLE-AUDIO API_>`_
|
||||
|
||||
* `Common API`_
|
||||
* `Codec API`_
|
||||
* `Common Audio Profile (CAP)`_
|
||||
* `Call Control Profile (CCP)`_
|
||||
* `Basic Audio Profile (BAP)`_
|
||||
* `Published Audio Capabilities (PACS)`_
|
||||
* `Gaming Audio Profile (GMAP)`_
|
||||
* `Volume Control Profile (VCP)`_
|
||||
* `Volume Offset Control Service (VOCS)`_
|
||||
* `Audio Input Control Service (AICS)`_
|
||||
* `Microphone Control Profile (MICP)`_
|
||||
* `Hearing Access Profile (HAP)`_
|
||||
* `Coordinated Set Identification Profile (CSIP)`_
|
||||
* `Telephone Bearer Service (TBS)`_
|
||||
* `Media Control Client (MCC)`_
|
||||
* `Media Proxy`_
|
||||
* `Telephony and Media Audio Profile (TMAP)`_
|
||||
* `Public Broadcast Profile (PBP)`_
|
||||
|
||||
ESP-BLE-AUDIO Definitions
|
||||
-------------------------
|
||||
|
||||
This section contains common definitions, constants, and types used across LE Audio.
|
||||
|
||||
|
||||
General Definitions
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_defs.inc
|
||||
|
||||
|
||||
LC3 Codec Definitions
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_lc3_defs.inc
|
||||
|
||||
|
||||
MCS Definitions
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_mcs_defs.inc
|
||||
|
||||
|
||||
BAP LC3 Preset Definitions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_bap_lc3_preset_defs.inc
|
||||
|
||||
|
||||
GMAP LC3 Preset Definitions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_gmap_lc3_preset_defs.inc
|
||||
|
||||
|
||||
ESP-BLE-AUDIO API
|
||||
-----------------
|
||||
|
||||
This section contains LE Audio APIs: common helpers, codec, Common Audio Profile (CAP), Call Control Profile (CCP), and all profile/service APIs.
|
||||
|
||||
|
||||
Common API
|
||||
^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_common_api.inc
|
||||
|
||||
|
||||
Codec API
|
||||
^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_codec_api.inc
|
||||
|
||||
|
||||
Common Audio Profile (CAP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_cap_api.inc
|
||||
|
||||
|
||||
Call Control Profile (CCP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_ccp_api.inc
|
||||
|
||||
|
||||
Basic Audio Profile (BAP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_bap_api.inc
|
||||
|
||||
|
||||
Published Audio Capabilities (PACS)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_pacs_api.inc
|
||||
|
||||
|
||||
Gaming Audio Profile (GMAP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_gmap_api.inc
|
||||
|
||||
|
||||
Volume Control Profile (VCP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_vcp_api.inc
|
||||
|
||||
|
||||
Volume Offset Control Service (VOCS)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_vocs_api.inc
|
||||
|
||||
|
||||
Audio Input Control Service (AICS)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_aics_api.inc
|
||||
|
||||
|
||||
Microphone Control Profile (MICP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_micp_api.inc
|
||||
|
||||
|
||||
Hearing Access Profile (HAP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_has_api.inc
|
||||
|
||||
|
||||
Coordinated Set Identification Profile (CSIP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_csip_api.inc
|
||||
|
||||
|
||||
Telephone Bearer Service (TBS)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_tbs_api.inc
|
||||
|
||||
|
||||
Media Control Client (MCC)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_mcc_api.inc
|
||||
|
||||
|
||||
Media Proxy
|
||||
^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_media_proxy_api.inc
|
||||
|
||||
|
||||
Telephony and Media Audio Profile (TMAP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_tmap_api.inc
|
||||
|
||||
|
||||
Public Broadcast Profile (PBP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_audio_pbp_api.inc
|
||||
@@ -0,0 +1,43 @@
|
||||
ESP-BLE-ISO
|
||||
===========
|
||||
|
||||
ESP-BLE-ISO provides APIs for Bluetooth LE Isochronous Channels, enabling time-synchronized, connection-oriented (CIS) and connectionless (BIS) streaming. The implementation is built on the Bluetooth controller ISO support and is intended for use with the :doc:`esp-ble-audio` component.
|
||||
|
||||
**Channel types**
|
||||
|
||||
* **Connected Isochronous Streams (CIS)** — Bidirectional isochronous links between two devices; requires a prior ACL connection to be established. Used for time-synchronized unicast audio (e.g., LE Audio).
|
||||
* **Broadcast Isochronous Streams (BIS)** — Unidirectional isochronous streams from a Broadcaster to one or more Synchronized Receivers, without a prior connection. Used for broadcast audio and group listening.
|
||||
|
||||
With these APIs you can create or join Connected ISO Groups (CIG), create or synchronize to Broadcast ISO Groups (BIG), set up ISO data paths, and send or receive isochronous data (e.g., LC3 audio). For higher-level LE Audio profiles and codec APIs, see :doc:`esp-ble-audio`.
|
||||
|
||||
For an architectural overview and integration details, see the :doc:`Bluetooth LE Audio documentation <../../api-guides/esp-ble-audio/ble-audio-index>`, in particular the :doc:`ESP-BLE-ISO architecture <../../api-guides/esp-ble-audio/ble-audio-architecture-iso>`.
|
||||
|
||||
.. warning::
|
||||
|
||||
This is a **preview release**. The ESP-BLE-ISO APIs, data structures, and configuration parameters are subject to change in future releases. Breaking changes — such as renamed or restructured types, modified function signatures, or removed fields — may be introduced without prior notice.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
* **BIG (Broadcast Isochronous Group)**
|
||||
- :example:`bluetooth/esp_ble_iso/big_broadcaster` demonstrates how to create a BIG and send isochronous data over BIS.
|
||||
- :example:`bluetooth/esp_ble_iso/big_receiver` demonstrates how to synchronize to a BIG and receive isochronous data from BIS.
|
||||
* **CIS (Connected Isochronous Stream)**
|
||||
- :example:`bluetooth/esp_ble_iso/cis_central` demonstrates how to act as a CIS Central that creates a CIG/CIS and sends isochronous data.
|
||||
- :example:`bluetooth/esp_ble_iso/cis_peripheral` demonstrates how to act as a CIS Peripheral that accepts a CIS and receives isochronous data.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
ESP-BLE-ISO APIs are divided into the following parts:
|
||||
|
||||
* `ESP-BLE-ISO Common API`_
|
||||
|
||||
|
||||
ESP-BLE-ISO Common API
|
||||
----------------------
|
||||
|
||||
This section contains macros, type definitions, and functions from ``esp_ble_iso_common_api.h``: BIS index and data path macros, controller delay and interval limits, CIG/BIG parameters and structures, ISO server and channel operations, data path setup, BIG create/sync/terminate, and GAP event types and ISO common initialization.
|
||||
|
||||
.. include-build-file:: inc/esp_ble_iso_common_api.inc
|
||||
@@ -0,0 +1,239 @@
|
||||
ESP-BLE-MESH
|
||||
============
|
||||
|
||||
.. note::
|
||||
|
||||
The current ESP-BLE-MESH v1.1 related code is a preview version, so the Mesh Protocol v1.1 related Structures, MACROs, and APIs involved in the code may be changed.
|
||||
|
||||
With various features of ESP-BLE-MESH, users can create a managed flooding mesh network for several scenarios, such as lighting, sensor and etc.
|
||||
|
||||
For an ESP32 to join and work on a ESP-BLE-MESH network, it must be provisioned firstly. By provisioning, the ESP32, as an unprovisioned device, will join the ESP-BLE-MESH network and become a ESP-BLE-MESH node, communicating with other nodes within or beyond the radio range.
|
||||
|
||||
Apart from ESP-BLE-MESH nodes, inside ESP-BLE-MESH network, there is also ESP32 that works as ESP-BLE-MESH provisioner, which could provision unprovisioned devices into ESP-BLE-MESH nodes and configure the nodes with various features.
|
||||
|
||||
For information how to start using ESP32 and ESP-BLE-MESH, please see the Section :ref:`getting-started-with-ble-mesh`. If you are interested in information on ESP-BLE-MESH architecture, including some details of software implementation, please see Section :doc:`../../api-guides/esp-ble-mesh/ble-mesh-architecture`.
|
||||
|
||||
|
||||
Application Examples and Demos
|
||||
------------------------------
|
||||
|
||||
Please refer to Sections :ref:`esp-ble-mesh-examples` and :ref:`esp-ble-mesh-demo-videos`.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
ESP-BLE-MESH APIs are divided into the following parts:
|
||||
|
||||
* `ESP-BLE-MESH Definitions`_
|
||||
* `ESP-BLE-MESH Core API Reference`_
|
||||
* `ESP-BLE-MESH Models API Reference`_
|
||||
* `ESP-BLE-MESH (v1.1) Core API Reference`_
|
||||
|
||||
|
||||
ESP-BLE-MESH Definitions
|
||||
------------------------
|
||||
|
||||
This section contains only one header file, which lists the following items of ESP-BLE-MESH.
|
||||
|
||||
* ID of all the models and related message opcodes
|
||||
* Structs of model, element and Composition Data
|
||||
* Structs of used by ESP-BLE-MESH Node/Provisioner for provisioning
|
||||
* Structs used to transmit/receive messages
|
||||
* Event types and related event parameters
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_defs.inc
|
||||
|
||||
|
||||
ESP-BLE-MESH Core API Reference
|
||||
-------------------------------
|
||||
|
||||
This section contains ESP-BLE-MESH Core related APIs, which can be used to initialize ESP-BLE-MESH stack, provision, send/publish messages, etc.
|
||||
|
||||
This API reference covers six components:
|
||||
|
||||
* `ESP-BLE-MESH Stack Initialization`_
|
||||
* `Reading of Local Data Information`_
|
||||
* `Low Power Operation (Updating)`_
|
||||
* `Send/Publish Messages, add Local AppKey, etc.`_
|
||||
* `ESP-BLE-MESH Node/Provisioner Provisioning`_
|
||||
* `ESP-BLE-MESH GATT Proxy Server`_
|
||||
|
||||
|
||||
ESP-BLE-MESH Stack Initialization
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_common_api.inc
|
||||
|
||||
|
||||
Reading of Local Data Information
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_local_data_operation_api.inc
|
||||
|
||||
Coexist with BLE
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_ble_api.inc
|
||||
|
||||
Low Power Operation (Updating)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_low_power_api.inc
|
||||
|
||||
|
||||
Send/Publish Messages, Add Local AppKey, Etc.
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_networking_api.inc
|
||||
|
||||
|
||||
ESP-BLE-MESH Node/Provisioner Provisioning
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_provisioning_api.inc
|
||||
|
||||
|
||||
ESP-BLE-MESH GATT Proxy Server
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_proxy_api.inc
|
||||
|
||||
|
||||
ESP-BLE-MESH Models API Reference
|
||||
---------------------------------
|
||||
|
||||
This section contains ESP-BLE-MESH Model related APIs, event types, event parameters, etc.
|
||||
|
||||
There are six categories of models:
|
||||
|
||||
* `Configuration Client/Server Models`_
|
||||
* `Health Client/Server Models`_
|
||||
* `Generic Client/Server Models`_
|
||||
* `Sensor Client/Server Models`_
|
||||
* `Time and Scenes Client/Server Models`_
|
||||
* `Lighting Client/Server Models`_
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Definitions related to Server Models are being updated, and will be released soon.
|
||||
|
||||
|
||||
Configuration Client/Server Models
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_config_model_api.inc
|
||||
|
||||
|
||||
Health Client/Server Models
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_health_model_api.inc
|
||||
|
||||
|
||||
Generic Client/Server Models
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_generic_model_api.inc
|
||||
|
||||
|
||||
Sensor Client/Server Models
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_sensor_model_api.inc
|
||||
|
||||
|
||||
Time and Scenes Client/Server Models
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_time_scene_model_api.inc
|
||||
|
||||
|
||||
Lighting Client/Server Models
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_lighting_model_api.inc
|
||||
|
||||
|
||||
ESP-BLE-MESH (v1.1) Core API Reference
|
||||
--------------------------------------
|
||||
|
||||
.. note::
|
||||
|
||||
This section is a preview version, so the related structures, macros, and APIs may be changed.
|
||||
|
||||
This section contains ESP-BLE-MESH v1.1 Core related APIs, event types, event parameters, etc.
|
||||
|
||||
This API reference covers 10 components:
|
||||
|
||||
* `Remote Provisioning`_
|
||||
* `Directed Forwarding`_
|
||||
* `Subnet Bridge Configuration`_
|
||||
* `Mesh Private Beacon`_
|
||||
* `On-Demand Private Proxy`_
|
||||
* `SAR Configuration`_
|
||||
* `Solicitation PDU RPL Configuration`_
|
||||
* `Opcodes Aggregator`_
|
||||
* `Large Composition Data`_
|
||||
* `Composition and Metadata`_
|
||||
|
||||
|
||||
Remote Provisioning
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_rpr_model_api.inc
|
||||
|
||||
|
||||
Directed Forwarding
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_df_model_api.inc
|
||||
|
||||
Subnet Bridge Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_brc_model_api.inc
|
||||
|
||||
Mesh Private Beacon
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_prb_model_api.inc
|
||||
|
||||
On-Demand Private Proxy
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_odp_model_api.inc
|
||||
|
||||
SAR Configuration
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_sar_model_api.inc
|
||||
|
||||
Solicitation PDU RPL Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_srpl_model_api.inc
|
||||
|
||||
Opcodes Aggregator
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_agg_model_api.inc
|
||||
|
||||
Large Composition Data
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_lcd_model_api.inc
|
||||
|
||||
Composition and Metadata
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_cm_data_api.inc
|
||||
|
||||
Device Firmware Update
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_dfu_model_api.inc
|
||||
|
||||
Device Firmware Slots
|
||||
|
||||
.. include-build-file:: inc/esp_ble_mesh_dfu_slot_api.inc
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
Bluetooth® A2DP API
|
||||
===================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
A2DP (Advanced Audio Distribution Profile) enables high-quality audio streaming from one device to another over Bluetooth. It is primarily used for streaming audio from source devices such as smartphones, computers, and media players to sink devices such as Bluetooth speakers, headphones, and car audio systems. Users can use the A2DP APIs to transmit or receive audio streams.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/a2dp_sink_stream` demonstrates how to implement an audio sink device using the Advanced Audio Distribution Profile (A2DP) to receive audio streams. This example also shows how to use I2S for audio stream output.
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/a2dp_source` demonstrates how to use A2DP APIs to transmit audio streams.
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/a2dp_sink_stream_aac` demonstrates how to use the AAC codec in an A2DP sink.
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/a2dp_source_aac` demonstrates how to use the AAC codec in an A2DP source.
|
||||
|
||||
- :example:`bluetooth/bluedroid/coex/a2dp_gatts_coex` demonstrates how to use the ESP-IDF A2DP-GATTS_COEX demo to create a GATT service and A2DP profile.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_a2dp_api.inc
|
||||
@@ -0,0 +1,23 @@
|
||||
Bluetooth® AVRCP API
|
||||
====================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
AVRCP (Audio/Video Remote Control Profile) enables remote control of audio and video devices over Bluetooth, allowing users to manage playback (play, pause, next/previous track), adjust volume, and retrieve media metadata.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/avrcp_absolute_volume` demonstrates how to implement Audio/Video Remote Control Profile to control absolute volume.
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/avrcp_ct_metadata` demonstrates how to implement Audio/Video Remote Control Profile to get metadata.
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/avrcp_ct_cover_art` demonstrates how to implement Audio/Video Remote Control Profile to get and display cover art image.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_avrc_api.inc
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
BluFi API
|
||||
=========
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
BluFi is a profile based GATT to config ESP32 Wi-Fi to connect/disconnect AP or setup a softap and etc.
|
||||
|
||||
Use should concern these things:
|
||||
|
||||
1. The event sent from profile. Then you need to do something as the event indicate.
|
||||
2. Security reference. You can write your own Security functions such as symmetrical encryption/decryption and checksum functions. Even you can define the "Key Exchange/Negotiation" procedure.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/blufi` how to use the Blufi function to configure the Wi-Fi connection to an AP via a Bluetooth channel on {IDF_TARGET_NAME}.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_blufi_api.inc
|
||||
@@ -0,0 +1,8 @@
|
||||
Bluetooth® Generic Defines
|
||||
==========================
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_bt_defs.inc
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Bluetooth® Device APIs
|
||||
======================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Bluetooth device reference APIs.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor` demonstrates how to use Serial Port Protocol (SPP) APIs to create an SPP acceptor that acts as a server and registers into the VFS. It also shows how to implement Secure Simple Pairing (SSP) in your own applications.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_bt_device.inc
|
||||
@@ -0,0 +1,9 @@
|
||||
Bluetooth® Main API
|
||||
===================
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_bt_main.inc
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
GAP API
|
||||
=======
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/ble/gatt_security_client` demonstrates how to use ESP BLE security APIs on {IDF_TARGET_NAME} to establish a secure connection and encrypt communication with peer devices while acting as a GATT client.
|
||||
|
||||
- :example:`bluetooth/bluedroid/ble/gatt_security_server` demonstrates how to use ESP BLE security APIs on {IDF_TARGET_NAME} to establish a secure connection and encrypt communication with peer devices while acting as a GATT server.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_gap_ble_api.inc
|
||||
@@ -0,0 +1,17 @@
|
||||
Bluetooth® Classic GAP API
|
||||
==========================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The Bluetooth Classic GAP (Generic Access Profile) API provides interfaces for device discovery, pairing, and security management, allowing applications to control visibility, initiate connections, and configure authentication and encryption for Bluetooth Classic links.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/bt_discovery` demonstrates how to use APIs to search for a target device with a Major device type of "Phone" or "Audio/Video" in the Class of Device field, and then perform a service discovery via the Service Discovery Protocol.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_gap_bt_api.inc
|
||||
@@ -0,0 +1,9 @@
|
||||
GATT Defines
|
||||
============
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_gatt_defs.inc
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
GATT Client API
|
||||
===============
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/ble/gatt_client` demonstrates how to create a GATT Client that connects to a GATT server, enabling the server's notification function to discover its service.
|
||||
|
||||
- :example:`bluetooth/bluedroid/ble/gattc_multi_connect` demonstrates how to create a GATT Client that connects to multiple GATT servers, enabling their notification functions to discover their services.
|
||||
|
||||
- :example:`bluetooth/bluedroid/coex/gattc_gatts_coex` demonstrates the coexistence of GATT client and GATT server by creating a GATT service, starting ADV, and exchanging data between a GATT client and device.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_gattc_api.inc
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
GATT Server API
|
||||
===============
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/ble/gatt_server_service_table` demonstrates how to create a GATT service using an attribute table, releasing the user from adding attributes individually.
|
||||
|
||||
- :example:`bluetooth/bluedroid/ble/gatt_server` demonstrates how to create a GATT service by adding attributes individually and then starts advertising so that a GATT client can connect and exchange data.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_gatts_api.inc
|
||||
@@ -0,0 +1,17 @@
|
||||
Bluetooth® HFP AG API
|
||||
=====================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
HFP (Hands-Free Profile) AG API provides functions for a Bluetooth device to act as an Audio Gateway (AG), enabling communication with HFP Client devices such as headsets or car kits. It supports connection management, call handling, volume control, and other hands-free operations.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/hfp_ag` demonstrates how to use the Hands-Free Audio Gateway (HF-AG) component to communicate with a device that implements Hands-Free Client Role, such as a headphone set. It provides commands for configuring the project, establishing connections, controlling volume, and answering or rejecting calls.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_hf_ag_api.inc
|
||||
@@ -0,0 +1,17 @@
|
||||
Bluetooth® HFP Client API
|
||||
=========================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
HFP (Hands-Free Profile) Client API provides functions to enable a Bluetooth device to act as an HFP Client, allowing communication with an Audio Gateway (AG) device, such as a smartphone, to handle voice calls, volume control, and other hands-free operations.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/hfp_hf` demonstrates how to use the Hands-Free Client Component to communicate with a device that implements Hands-Free Audio Gateway (HF-AG), such as a smartphone.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_hf_client_api.inc
|
||||
@@ -0,0 +1,21 @@
|
||||
Bluetooth® HFP Defines
|
||||
======================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
This file contains definitions for constants, enumerations, and structures used in the Bluetooth Hands-Free Profile (HFP), enabling features like call management, audio control, and network status reporting.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/hfp_hf` demonstrates how to use the Hands-Free Client Component to communicate with a device that implements Hands-Free Audio Gateway (HF-AG), such as a smartphone.
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/hfp_ag` demonstrates how to use the Hands-Free Audio Gateway (HF-AG) component to communicate with a device that implements Hands-Free Client Role, such as a headphone set. It provides commands for configuring the project, establishing connections, controlling volume, and answering or rejecting calls.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_hf_defs.inc
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Bluetooth® HID Device API
|
||||
=========================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
A Bluetooth HID device is a device providing the service of human or other data input and output to and from a Bluetooth HID Host. Users can use the Bluetooth HID Device APIs to make devices like keyboards, mice, joysticks and so on.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/bt_hid_mouse_device` demonstrates how to implement a Bluetooth HID device by simulating a Bluetooth HID mouse device that periodically sends reports to a remote Bluetooth HID host.
|
||||
|
||||
- :example:`bluetooth/esp_hid_device` demonstrates how to create a Bluetooth Classic or Bluetooth LE HID device, which can function as a mouse or a remote control, depending on the mode set by the user.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_hidd_api.inc
|
||||
@@ -0,0 +1,17 @@
|
||||
Bluetooth® HID Host API
|
||||
========================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
A Bluetooth HID host is a device or software that is capable of connecting and communicating with Bluetooth HID devices, such as keyboards, mice. Users can use the Bluetooth HID Host APIs to send output data or control commands to the HID devices, enabling them to control the behavior or settings of the devices.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/esp_hid_host` demonstrates how to use the ESP-IDF Bluetooth HID Host APIs to create a Bluetooth Classic, Bluetooth LE, or Bluetooth dual-mode HID host that scans and connects to the most recently discovered Bluetooth HID device, dumps the HID device information, and receives data sent by the HID device.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_hidh_api.inc
|
||||
@@ -0,0 +1,19 @@
|
||||
Bluetooth® Classic L2CAP API
|
||||
============================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Bluetooth Classic L2CAP (Logical Link Control and Adaptation Layer Protocol) API provides functions for data transmission between Bluetooth devices. It supports both client and server roles, allowing the creation of L2CAP connections for reliable, high-throughput communication.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/bt_l2cap_server` demonstrates how to use Logical Link Control and Adaptation Layer Protocol (L2CAP) APIs to create an L2CAP server, and wait to be discovered and connected by a remote device L2CAP client.
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/bt_l2cap_client` demonstrates how to use L2CAP APIs to create an L2CAP client.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_l2cap_bt_api.inc
|
||||
@@ -0,0 +1,21 @@
|
||||
Bluetooth® SDP API
|
||||
==================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
SDP (Service Discovery Protocol) API enables devices to discover services and service attributes offered by remote Bluetooth devices. It supports service search, service attribute retrieval, and the establishment of service connections.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/bt_l2cap_client` demonstrates how to use SDP APIs to search for services on remote Bluetooth devices. It shows how to register SDP callbacks, initialize SDP, perform service discovery using ``esp_sdp_search_record()``, and retrieve the L2CAP PSM (Protocol/Service Multiplexer) value from the search results to establish an L2CAP connection.
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/bt_l2cap_server` demonstrates how to use SDP APIs to create and publish service records. It shows how to register SDP callbacks, initialize SDP, create an SDP record using ``esp_sdp_create_record()`` with L2CAP PSM information, making the service discoverable by remote clients.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_sdp_api.inc
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
Bluetooth® SPP API
|
||||
==================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
SPP (Serial Port Profile) enables serial communication between Bluetooth devices, allowing them to exchange data over a virtual serial link. SPP API provides functionality to create both SPP initiators (clients) and acceptors (servers), enabling operation under different security requirements.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/bt_spp_acceptor` demonstrates how to use Bluetooth capabilities to create a Serial Port Protocol (SPP) acceptor that acts as a server and integrate Secure Simple Pairing (SSP). This example also includes a demo for communicating with an SPP initiator that acts as a client.
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/bt_spp_initiator` demonstrates how to use Bluetooth capabilities to create an SPP initiator that performs as a client and integrate SSP. This example also includes a demo for communicating with an SPP acceptor that acts as a server.
|
||||
|
||||
- :example:`bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator` demonstrates how to use SPP APIs to create an SPP initiator that acts as a client and communicates with an SPP acceptor, using Virtual File System (VFS) interface to send and receive data.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_spp_api.inc
|
||||
@@ -0,0 +1,114 @@
|
||||
Bluetooth\ :sup:`®` API
|
||||
*****************************
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
This section provides the API reference for Bluetooth components supported in ESP-IDF. ESP-IDF supports two host stacks: **Bluedroid** and **NimBLE**.
|
||||
|
||||
- **Bluedroid** (the default stack): Supports both Bluetooth Classic and Bluetooth LE. Recommended for applications that require both technologies.
|
||||
- **NimBLE**: A lightweight stack for Bluetooth LE only. Ideal for resource-constrained applications due to smaller code size and memory usage.
|
||||
|
||||
Use the navigation links below to explore API documentation and application examples.
|
||||
|
||||
----
|
||||
|
||||
**Controller Interface API**
|
||||
|
||||
The low-level interface between the Bluetooth host stack and the controller.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
controller_vhci
|
||||
|
||||
|
||||
**Bluedroid Stack API**
|
||||
|
||||
The default host stack in ESP-IDF, supporting both Bluetooth Classic and Bluetooth LE.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
bt_common
|
||||
:SOC_BT_CLASSIC_SUPPORTED: classic_bt
|
||||
bt_le
|
||||
|
||||
For architecture and feature overviews, refer to the following documents in API Guides:
|
||||
|
||||
.. only:: SOC_BT_CLASSIC_SUPPORTED
|
||||
|
||||
:doc:`../../api-guides/bt-architecture/index`, :doc:`../../api-guides/classic-bt/index`, :doc:`../../api-guides/ble/index`
|
||||
|
||||
.. only:: not SOC_BT_CLASSIC_SUPPORTED
|
||||
|
||||
:doc:`../../api-guides/bt-architecture/index`, :doc:`../../api-guides/ble/index`
|
||||
|
||||
**NimBLE Stack API**
|
||||
|
||||
A lightweight host stack for Bluetooth LE.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
nimble/index
|
||||
|
||||
For additional details and API reference from the upstream documentation, refer to `Apache Mynewt NimBLE User Guide <https://mynewt.apache.org/latest/network/index.html>`_.
|
||||
|
||||
.. only:: SOC_BLE_MESH_SUPPORTED
|
||||
|
||||
**ESP-BLE-MESH API**
|
||||
|
||||
Implements Bluetooth LE Mesh networking.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
esp-ble-mesh
|
||||
|
||||
.. only:: SOC_BLE_ISO_SUPPORTED
|
||||
|
||||
**ESP-BLE-ISO API**
|
||||
|
||||
Bluetooth LE Isochronous Channels (CIS/BIS) for LE Audio and time-synchronized streaming.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
esp-ble-iso
|
||||
|
||||
.. only:: SOC_BLE_AUDIO_SUPPORTED
|
||||
|
||||
**ESP-BLE-AUDIO API**
|
||||
|
||||
Bluetooth LE Audio profiles and services (BAP, PACS, VCP, HAS, CSIP, etc.).
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
esp-ble-audio
|
||||
|
||||
----
|
||||
|
||||
Examples and Tutorials
|
||||
----------------------
|
||||
|
||||
Explore examples and tutorials in the ESP-IDF examples directory:
|
||||
|
||||
- **Bluedroid**: :example:`bluetooth/bluedroid`
|
||||
- **NimBLE**: :example:`bluetooth/nimble`
|
||||
- **BLE UART Service** (turnkey serial-over-BLE peripheral on top of NimBLE or Bluedroid, exposing the BLE UART Service GATT layout): :example:`bluetooth/ble_uart_service`
|
||||
|
||||
Step-by-step tutorials for developing with the Bluedroid stack:
|
||||
|
||||
* :example_file:`GATT Client Example Walkthrough <bluetooth/bluedroid/ble/gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md>`
|
||||
* :example_file:`GATT Server Service Table Example Walkthrough <bluetooth/bluedroid/ble/gatt_server_service_table/tutorial/Gatt_Server_Service_Table_Example_Walkthrough.md>`
|
||||
* :example_file:`GATT Server Example Walkthrough <bluetooth/bluedroid/ble/gatt_server/tutorial/Gatt_Server_Example_Walkthrough.md>`
|
||||
* :example_file:`GATT Security Client Example Walkthrough <bluetooth/bluedroid/ble/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md>`
|
||||
* :example_file:`GATT Security Server Example Walkthrough <bluetooth/bluedroid/ble/gatt_security_server/tutorial/Gatt_Security_Server_Example_Walkthrough.md>`
|
||||
* :example_file:`GATT Client Multi-connection Example Walkthrough <bluetooth/bluedroid/ble/gattc_multi_connect/tutorial/Gatt_Client_Multi_Connection_Example_Walkthrough.md>`
|
||||
|
||||
Step-by-step tutorials for developing with the NimBLE stack:
|
||||
|
||||
* :example_file:`Bluetooth LE Central Example Walkthrough <bluetooth/nimble/blecent/tutorial/blecent_walkthrough.md>`
|
||||
* :example_file:`Bluetooth LE Heart Rate Example Walkthrough <bluetooth/nimble/blehr/tutorial/blehr_walkthrough.md>`
|
||||
* :example_file:`Bluetooth LE Peripheral Example Walkthrough <bluetooth/nimble/bleprph/tutorial/bleprph_walkthrough.md>`
|
||||
@@ -0,0 +1,44 @@
|
||||
NimBLE-based Host APIs
|
||||
**********************
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Apache MyNewt NimBLE is a highly configurable and Bluetooth® SIG qualifiable Bluetooth Low Energy (Bluetooth LE) stack providing both host and controller functionalities. ESP-IDF supports NimBLE host stack which is specifically ported for ESP32 platform and FreeRTOS. The underlying controller is still the same (as in case of Bluedroid) providing VHCI interface. Refer to `NimBLE user guide <https://mynewt.apache.org/latest/network/index.html>`_ for a complete list of features and additional information on NimBLE stack. Most features of NimBLE including Bluetooth Low Energy Mesh are supported by ESP-IDF. The porting layer is kept cleaner by maintaining all the existing APIs of NimBLE along with a single ESP-NimBLE API for initialization, making it simpler for the application developers.
|
||||
|
||||
Architecture
|
||||
============
|
||||
|
||||
Currently, NimBLE host and controller support different transports such as UART and RAM between them. However, RAM transport cannot be used as is in case of ESP as ESP controller supports VHCI interface and buffering schemes used by NimBLE host is incompatible with that used by ESP controller. Therefore, a new transport between NimBLE host and ESP controller has been added. This is depicted in the figure below. This layer is responsible for maintaining pool of transport buffers and formatting buffers exchanges between host and controller as per the requirements.
|
||||
|
||||
.. figure:: ../../../../_static/esp32_nimble_stack.png
|
||||
:align: center
|
||||
:alt: ESP NimBLE Stack.
|
||||
:scale: 50
|
||||
|
||||
ESP NimBLE Stack
|
||||
|
||||
|
||||
Threading Model
|
||||
===============
|
||||
|
||||
The NimBLE host can run inside the application thread or can have its own independent thread. This flexibility is inherently provided by NimBLE design. By default, a thread is spawned by the porting function ``nimble_port_freertos_init``. This behavior can be changed by overriding the same function. For Bluetooth Low Energy Mesh, additional thread (advertising thread) is used which keeps on feeding advertisement events to the main thread.
|
||||
|
||||
Programming Sequence
|
||||
====================
|
||||
|
||||
To begin with, make sure that the NimBLE stack is enabled from menuconfig :ref:`choose NimBLE for the Bluetooth host <CONFIG_BT_HOST>`.
|
||||
|
||||
Typical programming sequence with NimBLE stack consists of the following steps:
|
||||
* Initialize NVS flash using :cpp:func:`nvs_flash_init` API. This is because ESP controller uses NVS during initialization.
|
||||
* Initialize the host and controller stack using ``nimble_port_init``.
|
||||
* Initialize the required NimBLE host configuration parameters and callbacks
|
||||
* Perform application specific tasks/initialization
|
||||
* Run the thread for host stack using ``nimble_port_freertos_init``
|
||||
|
||||
This documentation does not cover NimBLE APIs. Refer to `NimBLE tutorial <https://mynewt.apache.org/latest/network/index.html#ble-user-guide>`_ for more details on the programming sequence/NimBLE APIs for different scenarios.
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
.. include-build-file:: inc/esp_nimble_hci.inc
|
||||
@@ -0,0 +1,10 @@
|
||||
Error Codes Reference
|
||||
=====================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
This section lists various error code constants defined in ESP-IDF.
|
||||
|
||||
For general information about error codes in ESP-IDF, see :doc:`Error Handling <../api-guides/error-handling>`.
|
||||
|
||||
.. include-build-file:: inc/esp_err_defs.inc
|
||||
@@ -0,0 +1,19 @@
|
||||
*************
|
||||
API Reference
|
||||
*************
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api-conventions
|
||||
protocols/index
|
||||
:SOC_BT_SUPPORTED: bluetooth/index
|
||||
error-codes
|
||||
network/index
|
||||
peripherals/index
|
||||
provisioning/index
|
||||
storage/index
|
||||
system/index
|
||||
kconfig-reference
|
||||
@@ -0,0 +1,11 @@
|
||||
Configuration Options Reference
|
||||
===============================
|
||||
|
||||
.. _configuration-options-reference:
|
||||
|
||||
Subsequent sections contain the list of available ESP-IDF options automatically generated from Kconfig files. Note that due to dependencies between options, some options listed here may not be visible by default in ``menuconfig``.
|
||||
|
||||
By convention, all option names are upper-case letters with underscores. When Kconfig generates ``sdkconfig`` and ``sdkconfig.h`` files, option names are prefixed with ``CONFIG_``. So if an option ``ENABLE_FOO`` is defined in a Kconfig file and selected in ``menuconfig``, then the ``sdkconfig`` and ``sdkconfig.h`` files will have ``CONFIG_ENABLE_FOO`` defined. In the following sections, option names are also prefixed with ``CONFIG_``, same as in the source code.
|
||||
|
||||
|
||||
.. include-build-file:: inc/kconfig.inc
|
||||
@@ -0,0 +1,318 @@
|
||||
ESP-WIFI-MESH Programming Guide
|
||||
===============================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
This is a programming guide for ESP-WIFI-MESH, including the API reference and coding examples. This guide is split into the following parts:
|
||||
|
||||
1. :ref:`mesh-programming-model`
|
||||
|
||||
2. :ref:`mesh-writing-mesh-application`
|
||||
|
||||
3. :ref:`mesh-self-organized-behavior`
|
||||
|
||||
4. :ref:`mesh-application-examples`
|
||||
|
||||
5. :ref:`mesh-api-reference`
|
||||
|
||||
For documentation regarding the ESP-WIFI-MESH protocol, please see the :doc:`ESP-WIFI-MESH API Guide <../../api-guides/esp-wifi-mesh>`. For more information about ESP-WIFI-MESH Development Framework, please see `ESP-WIFI-MESH Development Framework <https://github.com/espressif/esp-mdf>`_.
|
||||
|
||||
|
||||
.. ---------------------- ESP-WIFI-MESH Programming Model --------------------------
|
||||
|
||||
.. _mesh-programming-model:
|
||||
|
||||
ESP-WIFI-MESH Programming Model
|
||||
-------------------------------------
|
||||
|
||||
Software Stack
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The ESP-WIFI-MESH software stack is built atop the Wi-Fi Driver/FreeRTOS and may use the LwIP Stack in some instances (i.e., the root node). The following diagram illustrates the ESP-WIFI-MESH software stack.
|
||||
|
||||
.. _mesh-going-to-software-stack:
|
||||
|
||||
.. figure:: ../../../_static/mesh-software-stack.png
|
||||
:align: center
|
||||
:alt: ESP-WIFI-MESH Software Stack
|
||||
:figclass: align-center
|
||||
|
||||
ESP-WIFI-MESH Software Stack
|
||||
|
||||
.. _mesh-events:
|
||||
|
||||
System Events
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
An application interfaces with ESP-WIFI-MESH via **ESP-WIFI-MESH Events**. Since ESP-WIFI-MESH is built atop the Wi-Fi stack, it is also possible for the application to interface with the Wi-Fi driver via the **Wi-Fi Event Task**. The following diagram illustrates the interfaces for the various System Events in an ESP-WIFI-MESH application.
|
||||
|
||||
.. figure:: ../../../_static/mesh-events-delivery.png
|
||||
:align: center
|
||||
:alt: ESP-WIFI-MESH System Events Delivery
|
||||
:figclass: align-center
|
||||
|
||||
ESP-WIFI-MESH System Events Delivery
|
||||
|
||||
The :cpp:type:`mesh_event_id_t` defines all possible ESP-WIFI-MESH events and can indicate events such as the connection/disconnection of parent/child. Before ESP-WIFI-MESH events can be used, the application must register a **Mesh Events handler** via :cpp:func:`esp_event_handler_register` to the default event task. The Mesh Events handler that is registered contain handlers for each ESP-WIFI-MESH event relevant to the application.
|
||||
|
||||
Typical use cases of mesh events include using events such as :cpp:enumerator:`MESH_EVENT_PARENT_CONNECTED` and :cpp:enumerator:`MESH_EVENT_CHILD_CONNECTED` to indicate when a node can begin transmitting data upstream and downstream respectively. Likewise, :cpp:enumerator:`IP_EVENT_STA_GOT_IP` and :cpp:enumerator:`IP_EVENT_STA_LOST_IP` can be used to indicate when the root node can and cannot transmit data to the external IP network.
|
||||
|
||||
.. warning::
|
||||
|
||||
When using ESP-WIFI-MESH under self-organized mode, users must ensure that no calls to Wi-Fi API are made. This is due to the fact that the self-organizing mode will internally make Wi-Fi API calls to connect/disconnect/scan etc. **Any Wi-Fi calls from the application (including calls from callbacks and handlers of Wi-Fi events) may interfere with ESP-WIFI-MESH's self-organizing behavior**. Therefore, users should not call Wi-Fi APIs after :cpp:func:`esp_mesh_start` is called, and before :cpp:func:`esp_mesh_stop` is called.
|
||||
|
||||
LwIP & ESP-WIFI-MESH
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The application can access the ESP-WIFI-MESH stack directly without having to go through the LwIP stack. The LwIP stack is only required by the root node to transmit/receive data to/from an external IP network. However, since every node can potentially become the root node (due to automatic root node selection), each node must still initialize the LwIP stack.
|
||||
|
||||
**Each node that could become root is required to initialize LwIP by calling** :cpp:func:`esp_netif_init`. In order to prevent non-root node access to LwIP, the application should not create or register any network interfaces using esp_netif APIs.
|
||||
|
||||
|
||||
ESP-WIFI-MESH requires a root node to be connected with a router. Therefore, in the event that a node becomes the root, **the corresponding handler must start the DHCP client service and immediately obtain an IP address**. Doing so will allow other nodes to begin transmitting/receiving packets to/from the external IP network. However, this step is unnecessary if static IP settings are used.
|
||||
|
||||
|
||||
.. ---------------------- Writing a Mesh Application --------------------------
|
||||
|
||||
.. _mesh-writing-mesh-application:
|
||||
|
||||
Writing an ESP-WIFI-MESH Application
|
||||
-------------------------------------------
|
||||
|
||||
The prerequisites for starting ESP-WIFI-MESH is to initialize LwIP and Wi-Fi, The following code snippet demonstrates the necessary prerequisite steps before ESP-WIFI-MESH itself can be initialized.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
/* event initialization */
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* Wi-Fi initialization */
|
||||
wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&config));
|
||||
/* register IP events handler */
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
After initializing LwIP and Wi-Fi, the process of getting an ESP-WIFI-MESH network up and running can be summarized into the following three steps:
|
||||
|
||||
1. :ref:`mesh-initialize-mesh`
|
||||
2. :ref:`mesh-configuring-mesh`
|
||||
3. :ref:`mesh-start-mesh`
|
||||
|
||||
.. _mesh-initialize-mesh:
|
||||
|
||||
Initialize Mesh
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The following code snippet demonstrates how to initialize ESP-WIFI-MESH
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* mesh initialization */
|
||||
ESP_ERROR_CHECK(esp_mesh_init());
|
||||
/* register mesh events handler */
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(MESH_EVENT, ESP_EVENT_ANY_ID, &mesh_event_handler, NULL));
|
||||
|
||||
.. _mesh-configuring-mesh:
|
||||
|
||||
Configuring an ESP-WIFI-MESH Network
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. todo - Add note about unified configuration
|
||||
|
||||
ESP-WIFI-MESH is configured via :cpp:func:`esp_mesh_set_config` which receives its arguments using the :cpp:type:`mesh_cfg_t` structure. The structure contains the following parameters used to configure ESP-WIFI-MESH:
|
||||
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 15 25
|
||||
|
||||
* - Parameter
|
||||
- Description
|
||||
|
||||
* - Channel
|
||||
- Range from 1 to 14
|
||||
|
||||
* - Mesh ID
|
||||
- ID of ESP-WIFI-MESH Network, see :cpp:type:`mesh_addr_t`
|
||||
|
||||
* - Router
|
||||
- Router Configuration, see :cpp:type:`mesh_router_t`
|
||||
|
||||
* - Mesh AP
|
||||
- Mesh AP Configuration, see :cpp:type:`mesh_ap_cfg_t`
|
||||
|
||||
* - Crypto Functions
|
||||
- Crypto Functions for Mesh IE, see :cpp:type:`mesh_crypto_funcs_t`
|
||||
|
||||
|
||||
The following code snippet demonstrates how to configure ESP-WIFI-MESH.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Enable the Mesh IE encryption by default */
|
||||
mesh_cfg_t cfg = MESH_INIT_CONFIG_DEFAULT();
|
||||
/* mesh ID */
|
||||
memcpy((uint8_t *) &cfg.mesh_id, MESH_ID, 6);
|
||||
/* channel (must match the router's channel) */
|
||||
cfg.channel = CONFIG_MESH_CHANNEL;
|
||||
/* router */
|
||||
cfg.router.ssid_len = strlen(CONFIG_MESH_ROUTER_SSID);
|
||||
memcpy((uint8_t *) &cfg.router.ssid, CONFIG_MESH_ROUTER_SSID, cfg.router.ssid_len);
|
||||
memcpy((uint8_t *) &cfg.router.password, CONFIG_MESH_ROUTER_PASSWD,
|
||||
strlen(CONFIG_MESH_ROUTER_PASSWD));
|
||||
/* mesh softAP */
|
||||
cfg.mesh_ap.max_connection = CONFIG_MESH_AP_CONNECTIONS;
|
||||
memcpy((uint8_t *) &cfg.mesh_ap.password, CONFIG_MESH_AP_PASSWD,
|
||||
strlen(CONFIG_MESH_AP_PASSWD));
|
||||
ESP_ERROR_CHECK(esp_mesh_set_config(&cfg));
|
||||
|
||||
.. _mesh-start-mesh:
|
||||
|
||||
Start Mesh
|
||||
^^^^^^^^^^
|
||||
|
||||
The following code snippet demonstrates how to start ESP-WIFI-MESH.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* mesh start */
|
||||
ESP_ERROR_CHECK(esp_mesh_start());
|
||||
|
||||
After starting ESP-WIFI-MESH, the application should check for ESP-WIFI-MESH events to determine when it has connected to the network. After connecting, the application can start transmitting and receiving packets over the ESP-WIFI-MESH network using :cpp:func:`esp_mesh_send` and :cpp:func:`esp_mesh_recv`.
|
||||
|
||||
|
||||
.. --------------------- ESP-WIFI-MESH Application Examples ------------------------
|
||||
|
||||
.. _mesh-self-organized-behavior:
|
||||
|
||||
Self-Organized Networking
|
||||
-------------------------
|
||||
|
||||
Self-organized networking is a feature of ESP-WIFI-MESH where nodes can autonomously scan/select/connect/reconnect to other nodes and routers. This feature allows an ESP-WIFI-MESH network to operate with high degree of autonomy by making the network robust to dynamic network topologies and conditions. With self-organized networking enabled, nodes in an ESP-WIFI-MESH network are able to carry out the following actions without autonomously:
|
||||
|
||||
- Selection or election of the root node (see **Automatic Root Node Selection** in :ref:`mesh-building-a-network`)
|
||||
- Selection of a preferred parent node (see **Parent Node Selection** in :ref:`mesh-building-a-network`)
|
||||
- Automatic reconnection upon detecting a disconnection (see **Intermediate Parent Node Failure** in :ref:`mesh-managing-a-network`)
|
||||
|
||||
When self-organized networking is enabled, the ESP-WIFI-MESH stack will internally make calls to Wi-Fi APIs. Therefore, **the application layer should not make any calls to Wi-Fi APIs whilst self-organized networking is enabled as doing so would risk interfering with ESP-WIFI-MESH**.
|
||||
|
||||
Toggling Self-Organized Networking
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Self-organized networking can be enabled or disabled by the application at runtime by calling the :cpp:func:`esp_mesh_set_self_organized` function. The function has the two following parameters:
|
||||
|
||||
- ``bool enable`` specifies whether to enable or disable self-organized networking.
|
||||
|
||||
- ``bool select_parent`` specifies whether a new parent node should be selected when enabling self-organized networking. Selecting a new parent has different effects depending the node type and the node's current state. This parameter is unused when disabling self-organized networking.
|
||||
|
||||
Disabling Self-Organized Networking
|
||||
"""""""""""""""""""""""""""""""""""
|
||||
The following code snippet demonstrates how to disable self-organized networking.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
//Disable self-organized networking
|
||||
esp_mesh_set_self_organized(false, false);
|
||||
|
||||
ESP-WIFI-MESH will attempt to maintain the node's current Wi-Fi state when disabling self-organized networking.
|
||||
|
||||
- If the node was previously connected to other nodes, it will remain connected.
|
||||
- If the node was previously disconnected and was scanning for a parent node or router, it will stop scanning.
|
||||
- If the node was previously attempting to reconnect to a parent node or router, it will stop reconnecting.
|
||||
|
||||
Enabling Self-Organized Networking
|
||||
""""""""""""""""""""""""""""""""""
|
||||
|
||||
ESP-WIFI-MESH will attempt to maintain the node's current Wi-Fi state when enabling self-organized networking. However, depending on the node type and whether a new parent is selected, the Wi-Fi state of the node can change. The following table shows effects of enabling self-organized networking.
|
||||
|
||||
+---------------+--------------+------------------------------------------------------------------------------------------------------------------+
|
||||
| Select Parent | Is Root Node | Effects |
|
||||
+===============+==============+==================================================================================================================+
|
||||
| N | N | - Nodes already connected to a parent node will remain connected. |
|
||||
| | | - Nodes previously scanning for a parent nodes will stop scanning. Call :cpp:func:`esp_mesh_connect` to restart. |
|
||||
| +--------------+------------------------------------------------------------------------------------------------------------------+
|
||||
| | Y | - A root node already connected to router will stay connected. |
|
||||
| | | - A root node disconnected from router will need to call :cpp:func:`esp_mesh_connect` to reconnect. |
|
||||
+---------------+--------------+------------------------------------------------------------------------------------------------------------------+
|
||||
| Y | N | - Nodes without a parent node will automatically select a preferred parent and connect. |
|
||||
| | | - Nodes already connected to a parent node will disconnect, reselect a preferred parent node, and connect. |
|
||||
| +--------------+------------------------------------------------------------------------------------------------------------------+
|
||||
| | Y | - For a root node to connect to a parent node, it must give up it's role as root. Therefore, a root node will |
|
||||
| | | disconnect from the router and all child nodes, select a preferred parent node, and connect. |
|
||||
+---------------+--------------+------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
The following code snipping demonstrates how to enable self-organized networking.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
//Enable self-organized networking and select a new parent
|
||||
esp_mesh_set_self_organized(true, true);
|
||||
|
||||
...
|
||||
|
||||
//Enable self-organized networking and manually reconnect
|
||||
esp_mesh_set_self_organized(true, false);
|
||||
esp_mesh_connect();
|
||||
|
||||
|
||||
Calling Wi-Fi API
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
There can be instances in which an application may want to directly call Wi-Fi API whilst using ESP-WIFI-MESH. For example, an application may want to manually scan for neighboring APs. However, **self-organized networking must be disabled before the application calls any Wi-Fi APIs**. This will prevent the ESP-WIFI-MESH stack from attempting to call any Wi-Fi APIs and potentially interfering with the application's calls.
|
||||
|
||||
Therefore, application calls to Wi-Fi APIs should be placed in between calls of :cpp:func:`esp_mesh_set_self_organized` which disable and enable self-organized networking. The following code snippet demonstrates how an application can safely call :cpp:func:`esp_wifi_scan_start` whilst using ESP-WIFI-MESH.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
//Disable self-organized networking
|
||||
esp_mesh_set_self_organized(0, 0);
|
||||
|
||||
//Stop any scans already in progress
|
||||
esp_wifi_scan_stop();
|
||||
//Manually start scan. Will automatically stop when run to completion
|
||||
esp_wifi_scan_start();
|
||||
|
||||
//Process scan results
|
||||
|
||||
...
|
||||
|
||||
//Re-enable self-organized networking if still connected
|
||||
esp_mesh_set_self_organized(1, 0);
|
||||
|
||||
...
|
||||
|
||||
//Re-enable self-organized networking if non-root and disconnected
|
||||
esp_mesh_set_self_organized(1, 1);
|
||||
|
||||
...
|
||||
|
||||
//Re-enable self-organized networking if root and disconnected
|
||||
esp_mesh_set_self_organized(1, 0); //Do not select new parent
|
||||
esp_mesh_connect(); //Manually reconnect to router
|
||||
|
||||
|
||||
.. --------------------- ESP-WIFI-MESH Application Examples ------------------------
|
||||
|
||||
.. _mesh-application-examples:
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`mesh/internal_communication` demonstrates how to use the mesh APIs to establish a mesh network, configure it, start it, handle events, and send and receive messages across the network.
|
||||
|
||||
- :example:`mesh/ip_internal_network` demonstrates how to use mesh to create an IP capable sub-network where all nodes publish their IP and internal mesh layer to an MQTT broker while using internal communication.
|
||||
|
||||
- :example:`mesh/manual_networking` demonstrates how to manually configure a mesh network using ESP-MESH, including scanning for parent candidates, selecting a suitable parent for a node, and configuring network settings.
|
||||
|
||||
.. ------------------------- ESP-WIFI-MESH API Reference ---------------------------
|
||||
|
||||
.. _mesh-api-reference:
|
||||
|
||||
API Reference
|
||||
--------------
|
||||
|
||||
.. include-build-file:: inc/esp_mesh.inc
|
||||
@@ -0,0 +1,29 @@
|
||||
Wi-Fi Easy Connect\ :sup:`TM` (DPP)
|
||||
===================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Wi-Fi Easy Connect\ :sup:`TM`, also known as Device Provisioning Protocol (DPP) or Easy Connect, is a provisioning protocol certified by Wi-Fi Alliance. It is a secure and standardized provisioning protocol for configuration of Wi-Fi Devices. With Easy Connect, adding a new device to a network is as simple as scanning a QR Code. This reduces complexity and enhances user experience while onboarding devices without UI like Smart Home and IoT products. Unlike old protocols like Wi-Fi Protected Setup (WPS), Wi-Fi Easy Connect incorporates strong encryption through public key cryptography to ensure networks remain secure as new devices are added.
|
||||
|
||||
Easy Connect brings many benefits in the user experience:
|
||||
|
||||
- Simple and intuitive to use; no lengthy instructions to follow for new device setup
|
||||
- No need to remember and enter passwords into the device being provisioned
|
||||
- Works with electronic or printed QR codes, or human-readable strings
|
||||
- Supports both WPA2 and WPA3 networks
|
||||
|
||||
Please refer to Wi-Fi Alliance's official page on `Easy Connect <https://www.wi-fi.org/discover-wi-fi/wi-fi-easy-connect>`_ for more information.
|
||||
|
||||
{IDF_TARGET_NAME} supports Enrollee mode of Easy Connect with QR Code as the provisioning method. A display is required to display this QR Code. Users can scan this QR Code using their capable device and provision the {IDF_TARGET_NAME} to their Wi-Fi network. The provisioning device needs to be connected to the AP which need not support Wi-Fi Easy Connect\ :sup:`TM`.
|
||||
|
||||
Easy Connect is still an evolving protocol. Of known platforms that support the QR Code method are some Android smartphones with Android 10 or higher. To use Easy Connect, no additional App needs to be installed on the supported smartphone.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`wifi/wifi_easy_connect/dpp-enrollee` demonstrates how to configure {IDF_TARGET_NAME} as an enrollee using DPP to securely onboard ESP devices to a network with the help of a QR code and an Android 10+ device.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_dpp.inc
|
||||
@@ -0,0 +1,700 @@
|
||||
Ethernet
|
||||
========
|
||||
|
||||
{IDF_TARGET_SOC_DMA_DESC_SIZE:default="", esp32="32 bytes", esp32p4=" 32 bytes (64 bytes in fact due to the need for proper memory alignment)"}
|
||||
{IDF_TARGET_SOC_REF_CLK_IN_GPIO:default="", esp32="GPIO0", esp32p4="GPIO32, GPIO44 and GPIO50"}
|
||||
{IDF_TARGET_SOC_REF_CLK_OUT_GPIO:default="", esp32="GPIO0, GPIO16 and GPIO17", esp32p4="GPIO23 and GPIO39"}
|
||||
{IDF_TARGET_SOC_RMII_TX_EN:default="", esp32="GPIO21", esp32p4="GPIO33, GPIO40 and GPIO49"}
|
||||
{IDF_TARGET_SOC_RMII_TXD0:default="", esp32="GPIO19", esp32p4="GPIO34 and GPIO41"}
|
||||
{IDF_TARGET_SOC_RMII_TXD1:default="", esp32="GPIO22", esp32p4="GPIO35 and GPIO42"}
|
||||
{IDF_TARGET_SOC_RMII_CRS_DV:default="", esp32="GPIO27", esp32p4="GPIO28, GPIO45 and GPIO51"}
|
||||
{IDF_TARGET_SOC_RMII_RXD0:default="", esp32="GPIO25", esp32p4="GPIO29, GPIO46 and GPIO52"}
|
||||
{IDF_TARGET_SOC_RMII_RXD1:default="", esp32="GPIO26", esp32p4="GPIO30, GPIO47 and GPIO53"}
|
||||
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
.. -------------------------------- Overview -----------------------------------
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
.. only:: SOC_EMAC_SUPPORTED
|
||||
|
||||
ESP-IDF provides a set of consistent and flexible APIs to support both internal Ethernet MAC (EMAC) controller and external SPI-Ethernet modules.
|
||||
|
||||
.. only:: not SOC_EMAC_SUPPORTED
|
||||
|
||||
ESP-IDF provides a set of consistent and flexible APIs to support external SPI-Ethernet modules.
|
||||
|
||||
This programming guide is split into the following sections:
|
||||
|
||||
1. :ref:`basic-ethernet-concepts`
|
||||
2. :ref:`driver-configuration-and-installation`
|
||||
3. :ref:`connect-driver-to-stack`
|
||||
4. :ref:`misc-operation-of-driver`
|
||||
|
||||
.. --------------------------- Basic Ethernet Concepts ------------------------------
|
||||
|
||||
.. _basic-ethernet-concepts:
|
||||
|
||||
Basic Ethernet Concepts
|
||||
-----------------------
|
||||
|
||||
Ethernet is an asynchronous Carrier Sense Multiple Access with Collision Detect (CSMA/CD) protocol/interface. It is generally not well suited for low-power applications. However, with ubiquitous deployment, internet connectivity, high data rates, and limitless-range expandability, Ethernet can accommodate nearly all wired communications.
|
||||
|
||||
Normal IEEE 802.3 compliant Ethernet frames are between 64 and 1518 bytes in length. They are made up of five or six different fields: a destination MAC address (DA), a source MAC address (SA), a type/length field, a data payload, an optional padding field and a Cyclic Redundancy Check (CRC). Additionally, when transmitted on the Ethernet medium, a 7-byte preamble field and Start-of-Frame (SOF) delimiter byte are appended to the beginning of the Ethernet packet.
|
||||
|
||||
Thus the traffic on the twist-pair cabling appears as shown below:
|
||||
|
||||
.. rackdiag:: ../../../_static/diagrams/ethernet/data_frame_format.diag
|
||||
:caption: Ethernet Data Frame Format
|
||||
:align: center
|
||||
|
||||
Preamble and Start-of-Frame Delimiter
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The preamble contains seven bytes of ``55H``. It allows the receiver to lock onto the stream of data before the actual frame arrives.
|
||||
|
||||
The Start-of-Frame Delimiter (SFD) is a binary sequence ``10101011`` (as seen on the physical medium). It is sometimes considered to be part of the preamble.
|
||||
|
||||
When transmitting and receiving data, the preamble and SFD bytes will be automatically generated or stripped from the packets.
|
||||
|
||||
Destination Address
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The destination address field contains a 6-byte length MAC address of the device that the packet is directed to. If the Least Significant bit in the first byte of the MAC address is set, the address is a multicast destination. For example, 01-00-00-00-F0-00 and 33-45-67-89-AB-CD are multi-cast addresses, while 00-00-00-00-F0-00 and 32-45-67-89-AB-CD are not.
|
||||
|
||||
Packets with multi-cast destination addresses are designed to arrive and be important to a selected group of Ethernet nodes. If the destination address field is the reserved multicast address, i.e., FF-FF-FF-FF-FF-FF, the packet is a broadcast packet and it will be directed to everyone sharing the network. If the Least Significant bit in the first byte of the MAC address is clear, the address is a unicast address and will be designed for usage by only the addressed node.
|
||||
|
||||
Normally the EMAC controller incorporates receive filters which can be used to discard or accept packets with multi-cast, broadcast and/or unicast destination addresses. When transmitting packets, the host controller is responsible for writing the desired destination address into the transmit buffer.
|
||||
|
||||
Source Address
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The source address field contains a 6-byte length MAC address of the node which created the Ethernet packet. Users of Ethernet must generate a unique MAC address for each controller used. MAC addresses consist of two portions. The first three bytes are known as the Organizationally Unique Identifier (OUI). OUIs are distributed by the IEEE. The last three bytes are address bytes at the discretion of the company that purchased the OUI. For more information about MAC Address used in ESP-IDF, please see :ref:`MAC Address Allocation <MAC-Address-Allocation>`.
|
||||
|
||||
When transmitting packets, the assigned source MAC address must be written into the transmit buffer by the host controller.
|
||||
|
||||
Type/Length
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The type/length field is a 2-byte field. If the value in this field is <= 1500 (decimal), it is considered a length field and it specifies the amount of non-padding data which follows in the data field. If the value is >= 1536, it represents the protocol the following packet data belongs to. The following are the most common type values:
|
||||
|
||||
* IPv4 = 0800H
|
||||
* IPv6 = 86DDH
|
||||
* ARP = 0806H
|
||||
|
||||
Users implementing proprietary networks may choose to treat this field as a length field, while applications implementing protocols such as the Internet Protocol (IP) or Address Resolution Protocol (ARP), should program this field with the appropriate type defined by the protocol's specification when transmitting packets.
|
||||
|
||||
Payload
|
||||
^^^^^^^
|
||||
|
||||
The payload field is a variable length field, anywhere from 0 to 1500 bytes. Larger data packets violates Ethernet standards and will be dropped by most Ethernet nodes.
|
||||
|
||||
This field contains the client data, such as an IP datagram.
|
||||
|
||||
Padding and FCS
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The padding field is a variable length field added to meet the IEEE 802.3 specification requirements when small data payloads are used.
|
||||
|
||||
The DA, SA, type, payload, and padding of an Ethernet packet must be no smaller than 60 bytes in total. If the required 4-byte FCS field is added, packets must be no smaller than 64 bytes. If the payload field is less than 46-byte long, a padding field is required.
|
||||
|
||||
The FCS field is a 4-byte field that contains an industry-standard 32-bit CRC calculated with the data from the DA, SA, type, payload, and padding fields. Given the complexity of calculating a CRC, the hardware normally automatically generates a valid CRC and transmit it. Otherwise, the host controller must generate the CRC and place it in the transmit buffer.
|
||||
|
||||
Normally, the host controller does not need to concern itself with padding and the CRC which the hardware EMAC will also be able to automatically generate when transmitting and verify when receiving. However, the padding and CRC fields will be written into the receive buffer when packets arrive, so they may be evaluated by the host controller if needed.
|
||||
|
||||
.. note::
|
||||
Besides the basic data frame described above, there are two other common frame types in 10/100 Mbps Ethernet: control frames and VLAN-tagged frames. They are not supported in ESP-IDF.
|
||||
|
||||
.. ------------------------------ Driver Operation --------------------------------
|
||||
|
||||
.. _driver-configuration-and-installation:
|
||||
|
||||
Configure MAC and PHY
|
||||
---------------------
|
||||
|
||||
The Ethernet driver is composed of two parts: MAC and PHY.
|
||||
|
||||
.. only:: SOC_EMAC_SUPPORTED
|
||||
|
||||
The communication between MAC and PHY can have diverse choices: **MII** (Media Independent Interface), **RMII** (Reduced Media Independent Interface), etc.
|
||||
|
||||
.. figure:: ../../../_static/rmii-interface.png
|
||||
:scale: 80 %
|
||||
:alt: Ethernet RMII Interface
|
||||
:figclass: align-center
|
||||
|
||||
Ethernet RMII Interface
|
||||
|
||||
One of the obvious differences between MII and RMII is signal consumption. MII usually costs up to 18 signals, while the RMII interface can reduce the consumption to 9.
|
||||
|
||||
.. note::
|
||||
ESP-IDF only supports the RMII interface. Therefore, always set :cpp:member:`eth_esp32_emac_config_t::interface` to :cpp:enumerator:`eth_data_interface_t::EMAC_DATA_INTERFACE_RMII`.
|
||||
|
||||
In RMII mode, both the receiver and transmitter signals are referenced to the ``REF_CLK``. ``REF_CLK`` **must be stable during any access to PHY and MAC**. Generally, there are three ways to generate the ``REF_CLK`` depending on the PHY device in your design:
|
||||
|
||||
* Some PHY chips can derive the ``REF_CLK`` from its externally connected 25 MHz crystal oscillator (as seen the option **a** in the picture). In this case, you should configure :cpp:member:`eth_mac_clock_config_t::clock_mode` of :cpp:member:`eth_esp32_emac_config_t::clock_config` to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`.
|
||||
|
||||
* Some PHY chip uses an externally connected 50 MHz crystal oscillator or other clock sources, which can also be used as the ``REF_CLK`` for the MAC side (as seen the option **b** in the picture). In this case, you still need to configure :cpp:member:`eth_mac_clock_config_t::clock_mode` of :cpp:member:`eth_esp32_emac_config_t::clock_config` to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`.
|
||||
|
||||
* Some EMAC controllers can generate the ``REF_CLK`` using an internal high-precision PLL (as seen the option **c** in the picture). In this case, you should configure :cpp:member:`eth_mac_clock_config_t::clock_mode` of :cpp:member:`eth_esp32_emac_config_t::clock_config` to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. warning::
|
||||
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`, internal Audio PLL clock is used as a source of 50 MHz clock. Hence be sure it is not in collision with I2S bus configuration.
|
||||
|
||||
When internal clock is selected, then ``GPIO0`` can be used to output the ``REF_CLK`` signal. However, the clock is outputted directly to the GPIO in this particular case and so it does not have direct relationship with EMAC peripheral. Sometimes this configuration may not work well with your PHY chip. If you are not using PSRAM in your design, GPIO16 and GPIO17 are also available to output the reference clock signal. The source of clock is the same (APLL) but these signals are routed from EMAC peripheral.
|
||||
|
||||
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`, then ``GPIO0`` is the only choice to input the ``REF_CLK`` signal. Please note that ``GPIO0`` is also an important strapping GPIO on ESP32. If GPIO0 samples a low level during power-up, ESP32 will go into download mode. The system will get halted until a manually reset. The workaround for this issue is disabling the ``REF_CLK`` in hardware by default so that the strapping pin is not interfered by other signals in the boot stage. Then, re-enable the ``REF_CLK`` in the Ethernet driver installation stage.
|
||||
|
||||
The ways to disable the ``REF_CLK`` signal can be:
|
||||
|
||||
* Disable or power down the crystal oscillator (as the case **b** in the picture).
|
||||
|
||||
* Force the PHY device to reset status (as the case **a** in the picture). **This could fail for some PHY device** (i.e., it still outputs signals to GPIO0 even in reset state).
|
||||
|
||||
.. warning::
|
||||
|
||||
If you want the **Ethernet to work with Wi-Fi or Bluetooth**, don’t select ESP32 as source of ``REF_CLK`` as it would result in ``REF_CLK`` instability. Either disable Wi-Fi or use a PHY or an external oscillator as the ``REF_CLK`` source.
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
.. note::
|
||||
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`, {IDF_TARGET_SOC_REF_CLK_OUT_GPIO} can be selected as output pin of the ``REF_CLK`` signal via IO_MUX.
|
||||
|
||||
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`, {IDF_TARGET_SOC_REF_CLK_IN_GPIO} can be selected as input pin for the ``REF_CLK`` signal via IO_MUX.
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
.. warning::
|
||||
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`, the EMAC derives the 50 MHz RMII reference clock from the MPLL via an integer divider. When PSRAM is also enabled, both peripherals share the MPLL, and PSRAM locks it to a frequency determined by its speed configuration. If PSRAM speed is configured to 80 MHz (:ref:`CONFIG_SPIRAM_SPEED`), the MPLL runs at 320 MHz, and no integer divisor of 320 MHz can produce 50 MHz within the required ±50 ppm tolerance (the closest candidate is 320 / 6 ≈ 53.33 MHz). EMAC initialization will fail in this configuration.
|
||||
|
||||
If you must use 80 MHz PSRAM speed, provide the ``REF_CLK`` from an external source (PHY or oscillator) and configure :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN` instead.
|
||||
|
||||
.. only:: not SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK
|
||||
|
||||
.. warning::
|
||||
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`, the ``REF_CLK`` output signal must be looped back to the EMAC externally. You have to configure :cpp:member:`eth_mac_clock_config_t::clock_mode` of :cpp:member:`eth_esp32_emac_config_t::clock_config_out_in` to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN` and select GPIO number associated with ``REF_CLK`` input GPIO's ({IDF_TARGET_SOC_REF_CLK_IN_GPIO}).
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
.. figure:: ../../../_static/rmii_ref_clk_esp32p4.png
|
||||
:scale: 95 %
|
||||
:alt: RMII REF_CKL Output Loopback
|
||||
:figclass: align-center
|
||||
|
||||
RMII REF_CKL Output Loopback
|
||||
|
||||
**No matter which RMII clock mode you select, you really need to take care of the signal integrity of REF_CLK in your hardware design!** Keep the trace as short as possible. Keep it away from RF devices and inductor elements.
|
||||
|
||||
.. only:: not SOC_EMAC_USE_MULTI_IO_MUX
|
||||
|
||||
.. note::
|
||||
Signals used in the data plane are fixed to specific GPIOs via IO_MUX, they can not be modified to other GPIOs. Signals used in the control plane can be routed to any free GPIOs via Matrix. Please refer to `ESP32-Ethernet-Kit <https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32/esp32-ethernet-kit/index.html>`_ for hardware design example.
|
||||
|
||||
.. only:: SOC_EMAC_USE_MULTI_IO_MUX
|
||||
|
||||
.. note::
|
||||
Signals used in the data plane can be configured to predefined set of GPIOs via IO_MUX for the RMII, see below table. The data plane GPIO configuration is performed by the driver based on content of :cpp:member:`eth_esp32_emac_config_t::emac_dataif_gpio`. Signals used in the control plane can be routed to any free GPIOs via GPIO Matrix.
|
||||
|
||||
.. list-table:: {IDF_TARGET_NAME} RMII Data Plane GPIO
|
||||
:header-rows: 1
|
||||
:widths: 50 50
|
||||
:align: center
|
||||
|
||||
* - Pin Name
|
||||
- GPIO Number
|
||||
|
||||
* - TX_EN
|
||||
- {IDF_TARGET_SOC_RMII_TX_EN}
|
||||
|
||||
* - TXD0
|
||||
- {IDF_TARGET_SOC_RMII_TXD0}
|
||||
|
||||
* - TXD1
|
||||
- {IDF_TARGET_SOC_RMII_TXD1}
|
||||
|
||||
* - CRS_DV
|
||||
- {IDF_TARGET_SOC_RMII_CRS_DV}
|
||||
|
||||
* - RXD0
|
||||
- {IDF_TARGET_SOC_RMII_RXD0}
|
||||
|
||||
* - RXD1
|
||||
- {IDF_TARGET_SOC_RMII_RXD1}
|
||||
|
||||
You need to set up the necessary parameters for MAC and PHY respectively based on your Ethernet board design, and then combine the two together to complete the driver installation.
|
||||
|
||||
Basic common configuration for MAC layer is described in :cpp:class:`eth_mac_config_t`, including:
|
||||
|
||||
.. list::
|
||||
|
||||
* :cpp:member:`eth_mac_config_t::sw_reset_timeout_ms`: software reset timeout value, in milliseconds. Typically, MAC reset should be finished within 100 ms.
|
||||
|
||||
* :cpp:member:`eth_mac_config_t::rx_task_stack_size` and :cpp:member:`eth_mac_config_t::rx_task_prio`: the MAC driver creates a dedicated task to process incoming packets. These two parameters are used to set the stack size and priority of the task.
|
||||
|
||||
* :cpp:member:`eth_mac_config_t::flags`: specifying extra features that the MAC driver should have, it could be useful in some special situations. The value of this field can be OR'd with macros prefixed with ``ETH_MAC_FLAG_``. For example, if the MAC driver should work when the cache is disabled, then you should configure this field with :c:macro:`ETH_MAC_FLAG_WORK_WITH_CACHE_DISABLE`.
|
||||
|
||||
.. only:: SOC_EMAC_SUPPORTED
|
||||
|
||||
Specific configuration for **internal MAC module** is described in :cpp:class:`eth_esp32_emac_config_t`, including:
|
||||
|
||||
.. list::
|
||||
|
||||
* :cpp:member:`eth_esp32_emac_config_t::smi_mdc_gpio_num` and :cpp:member:`eth_esp32_emac_config_t::smi_mdio_gpio_num`: the GPIO number used to connect the SMI signals.
|
||||
|
||||
* :cpp:member:`eth_esp32_emac_config_t::interface`: configuration of MAC Data interface to PHY (MII/RMII).
|
||||
|
||||
* :cpp:member:`eth_esp32_emac_config_t::clock_config`: configuration of EMAC Interface clock (``REF_CLK`` mode and GPIO number in case of RMII).
|
||||
|
||||
* :cpp:member:`eth_esp32_emac_config_t::intr_priority`: sets the priority of the MAC interrupt. If it is set to ``0`` or a negative value, the driver will allocate an interrupt with a default priority. Otherwise, the driver will use the given priority. Note that *Low* and *Medium* interrupt priorities (1 to 3) can be set since these can be handled in C.
|
||||
|
||||
:SOC_EMAC_USE_MULTI_IO_MUX: * :cpp:member:`eth_esp32_emac_config_t::emac_dataif_gpio`: configuration of EMAC MII/RMII data plane GPIO numbers.
|
||||
|
||||
:not SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK: * :cpp:member:`eth_esp32_emac_config_t::clock_config_out_in`: configuration of EMAC input interface clock when ``REF_CLK`` signal is generated internally and is looped back to the EMAC externally. The mode must be always configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`. This option is valid only when configuration of :cpp:member:`eth_esp32_emac_config_t::clock_config` is set to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`.
|
||||
|
||||
Memory Considerations when Using Internal MAC
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The internal MAC subsystem transfers data to and from the CPU domain via DMA using a linked list of descriptors. There are two types of descriptors: Transmit and Receive. Based on its type, a descriptor holds status information about the received or transmitted frame or provides controls for transmission. Each descriptor also contains pointers to the current data buffer and the next descriptor. As such, a single EMAC DMA descriptor has size of {IDF_TARGET_SOC_DMA_DESC_SIZE} in DMA-capable memory.
|
||||
|
||||
The default configuration should cover most use cases. However, certain scenarios may require configuring the Ethernet DMA memory utilization to suit specific needs. Typical problems may arise in the following situations:
|
||||
|
||||
.. list::
|
||||
|
||||
* **Short and frequent frames dominate network traffic**: If your network traffic primarily consists of very short and frequently transmitted/received frames, you may observe issues such as lower-than-expected throughput (despite the rated 100 Mbps) and missed frames during reception. On transmission, the socket send API may return ``errno`` equal to ``ENOMEM``, accompanied by the `insufficient TX buffer size` message (if debug log level is enabled). This is because the default memory configuration is optimized for larger frames; :ref:`CONFIG_ETH_DMA_BUFFER_SIZE` is set to 512 bytes by default to ensure a better *data buffer* to *descriptor* size overhead ratio. The solution is to increase :ref:`CONFIG_ETH_DMA_RX_BUFFER_NUM` or :ref:`CONFIG_ETH_DMA_TX_BUFFER_NUM`. Additionally, consider decreasing :ref:`CONFIG_ETH_DMA_BUFFER_SIZE` to match the typical frame size in your network to maintain a reasonable memory footprint of the Ethernet driver.
|
||||
|
||||
* **High throughput leads to buffer exhaustion**: If the socket send API intermittently returns ``errno`` equal to ``ENOMEM``, accompanied by the `insufficient TX buffer size` message (if debug log level is enabled), and the throughput is close to the rated 100 Mbps, this likely indicates nearing hardware limitations. In such case, the hardware cannot keep up with the transmission requests. The solution is to increase :ref:`CONFIG_ETH_DMA_TX_BUFFER_NUM` to buffer more frames and mitigate temporary peaks in transmission requests. However, this will not help if the requested traffic consistently exceeds the rated throughput. In such situations, the only solution is to limit the bandwidth by software means at the application level.
|
||||
|
||||
Configuration for PHY is described in :cpp:class:`eth_phy_config_t`, including:
|
||||
|
||||
.. list::
|
||||
|
||||
* :cpp:member:`eth_phy_config_t::phy_addr`: multiple PHY devices can share the same SMI bus, so each PHY needs a unique address. Usually, this address is configured during hardware design by pulling up/down some PHY strapping pins. You can set the value from ``0`` to ``15`` based on your Ethernet board. Especially, if the SMI bus is shared by only one PHY device, setting this value to ``-1`` can enable the driver to detect the PHY address automatically.
|
||||
|
||||
* :cpp:member:`eth_phy_config_t::reset_timeout_ms`: reset timeout value, in milliseconds. Typically, PHY reset should be finished within 100 ms.
|
||||
|
||||
* :cpp:member:`eth_phy_config_t::autonego_timeout_ms`: auto-negotiation timeout value, in milliseconds. The Ethernet driver starts negotiation with the peer Ethernet node automatically, to determine to duplex and speed mode. This value usually depends on the ability of the PHY device on your board.
|
||||
|
||||
* :cpp:member:`eth_phy_config_t::reset_gpio_num`: if your board also connects the PHY reset pin to one of the GPIO, then set it here. Otherwise, set this field to ``-1``.
|
||||
|
||||
* :cpp:member:`eth_phy_config_t::hw_reset_assert_time_us`: Time the PHY reset pin is asserted in usec. Set this field to ``0`` to use chip specific default timing.
|
||||
|
||||
* :cpp:member:`eth_phy_config_t::post_hw_reset_delay_ms`: Time to wait after the PHY hardware reset is done in msec. Set this field to ``0`` to use chip specific default timing. Set this field to ``-1`` to not wait after the PHY hardware reset.
|
||||
|
||||
ESP-IDF provides a default configuration for MAC and PHY in macro :c:macro:`ETH_MAC_DEFAULT_CONFIG` and :c:macro:`ETH_PHY_DEFAULT_CONFIG`.
|
||||
|
||||
|
||||
Create MAC and PHY Instance
|
||||
---------------------------
|
||||
|
||||
The Ethernet driver is implemented in an Object-Oriented style. Any operation on MAC and PHY should be based on the instance of the two.
|
||||
|
||||
.. only:: SOC_EMAC_SUPPORTED
|
||||
|
||||
Internal EMAC + External PHY
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); // apply default common MAC configuration
|
||||
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); // apply default vendor-specific MAC configuration
|
||||
esp32_emac_config.smi_gpio.mdc_num = CONFIG_ETHERNET_MDC_GPIO; // alter the GPIO used for MDC signal
|
||||
esp32_emac_config.smi_gpio.mdio_num = CONFIG_ETHERNET_MDIO_GPIO; // alter the GPIO used for MDIO signal
|
||||
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); // create MAC instance
|
||||
|
||||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration
|
||||
phy_config.phy_addr = CONFIG_ETHERNET_PHY_ADDR; // alter the PHY address according to your board design
|
||||
phy_config.reset_gpio_num = CONFIG_ETHERNET_PHY_RST_GPIO; // alter the GPIO used for PHY reset
|
||||
esp_eth_phy_t *phy = esp_eth_phy_new_generic(&phy_config); // create generic PHY instance
|
||||
|
||||
.. note::
|
||||
Any Ethernet PHY chip compliant with IEEE 802.3 can be used when creating new PHY instance with :cpp:func:`esp_eth_phy_new_generic`. However, while basic functionality should always work, some specific features might be limited, even if the PHY meets IEEE 802.3 standard. A typical example is loopback functionality, where certain PHYs may require setting a specific speed mode to operate correctly. If this is the concern and you need PHY driver specifically tailored to your chip needs, use drivers for PHY chips the ESP-IDF already officially supports or consult with :ref:`Custom PHY Driver <custom-phy-driver>` section to create a new custom driver.
|
||||
|
||||
.. tip::
|
||||
Espressif provides drivers for several specific Ethernet PHY chips in the `esp-eth-drivers <https://github.com/espressif/esp-eth-drivers>`_ repository. Drivers are distributed as components and are available in the `ESP Component Registry <https://components.espressif.com/>`_.
|
||||
|
||||
Optional Runtime MAC Clock Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
EMAC ``REF_CLK`` can be optionally configured from the user application code.
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); // apply default vendor-specific MAC configuration
|
||||
|
||||
// ...
|
||||
|
||||
esp32_emac_config.interface = EMAC_DATA_INTERFACE_RMII; // alter EMAC Data Interface
|
||||
esp32_emac_config.clock_config.rmii.clock_mode = EMAC_CLK_OUT; // select EMAC REF_CLK mode
|
||||
esp32_emac_config.clock_config.rmii.clock_gpio = 17; // select GPIO number used to input/output EMAC REF_CLK
|
||||
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); // create MAC instance
|
||||
|
||||
|
||||
SPI-Ethernet Module
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); // apply default common MAC configuration
|
||||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration
|
||||
phy_config.phy_addr = CONFIG_ETHERNET_PHY_ADDR; // alter the PHY address according to your board design
|
||||
phy_config.reset_gpio_num = CONFIG_ETHERNET_PHY_RST_GPIO; // alter the GPIO used for PHY reset
|
||||
// Install GPIO interrupt service (as the SPI-Ethernet module is interrupt-driven)
|
||||
gpio_install_isr_service(0);
|
||||
// SPI bus configuration
|
||||
spi_device_handle_t spi_handle = NULL;
|
||||
spi_bus_config_t buscfg = {
|
||||
.miso_io_num = CONFIG_ETHERNET_SPI_MISO_GPIO,
|
||||
.mosi_io_num = CONFIG_ETHERNET_SPI_MOSI_GPIO,
|
||||
.sclk_io_num = CONFIG_ETHERNET_SPI_SCLK_GPIO,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
};
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_ETHERNET_SPI_HOST, &buscfg, 1));
|
||||
// Configure SPI device
|
||||
spi_device_interface_config_t spi_devcfg = {
|
||||
.mode = 0,
|
||||
.clock_speed_hz = CONFIG_ETHERNET_SPI_CLOCK_MHZ * 1000 * 1000,
|
||||
.spics_io_num = CONFIG_ETHERNET_SPI_CS_GPIO,
|
||||
.queue_size = 20
|
||||
};
|
||||
/* dm9051 ethernet driver is based on spi driver */
|
||||
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(CONFIG_ETHERNET_SPI_HOST, &spi_devcfg);
|
||||
dm9051_config.int_gpio_num = CONFIG_ETHERNET_SPI_INT_GPIO;
|
||||
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
|
||||
esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config);
|
||||
|
||||
|
||||
.. note::
|
||||
* When creating MAC and PHY instances for SPI-Ethernet modules (e.g., DM9051), the constructor function must have the same suffix (e.g., `esp_eth_mac_new_dm9051` and `esp_eth_phy_new_dm9051`). This is because we don not have other choices but the integrated PHY.
|
||||
|
||||
* The SPI device configuration (i.e., `spi_device_interface_config_t`) may slightly differ for other Ethernet modules or to meet SPI timing on specific PCB. Please check out your module's specs and the examples in ESP-IDF.
|
||||
|
||||
.. tip::
|
||||
Espressif provides drivers for various SPI-Ethernet modules in the `esp-eth-drivers <https://github.com/espressif/esp-eth-drivers>`_ repository. Drivers are distributed as components and are available in the `ESP Component Registry <https://components.espressif.com/>`_.
|
||||
|
||||
Install Driver
|
||||
--------------
|
||||
|
||||
To install the Ethernet driver, we need to combine the instance of MAC and PHY and set some additional high-level configurations (i.e., not specific to either MAC or PHY) in :cpp:class:`esp_eth_config_t`:
|
||||
|
||||
* :cpp:member:`esp_eth_config_t::mac`: instance that created from MAC generator (e.g., :cpp:func:`esp_eth_mac_new_esp32`).
|
||||
|
||||
* :cpp:member:`esp_eth_config_t::phy`: instance that created from PHY generator (e.g., :cpp:func:`esp_eth_phy_new_generic`).
|
||||
|
||||
* :cpp:member:`esp_eth_config_t::check_link_period_ms`: Ethernet driver starts an OS timer to check the link status periodically, this field is used to set the interval, in milliseconds.
|
||||
|
||||
* :cpp:member:`esp_eth_config_t::stack_input` or :cpp:member:`esp_eth_config_t::stack_input_info`: In most Ethernet IoT applications, any Ethernet frame received by a driver should be passed to the upper layer (e.g., TCP/IP stack). This field is set to a function that is responsible to deal with the incoming frames. You can even update this field at runtime via function :cpp:func:`esp_eth_update_input_path` after driver installation.
|
||||
|
||||
* :cpp:member:`esp_eth_config_t::on_lowlevel_init_done` and :cpp:member:`esp_eth_config_t::on_lowlevel_deinit_done`: These two fields are used to specify the hooks which get invoked when low-level hardware has been initialized or de-initialized.
|
||||
|
||||
ESP-IDF provides a default configuration for driver installation in macro :c:macro:`ETH_DEFAULT_CONFIG`.
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
|
||||
esp_eth_handle_t eth_handle = NULL; // after the driver is installed, we will get the handle of the driver
|
||||
esp_eth_driver_install(&config, ð_handle); // install driver
|
||||
|
||||
The Ethernet driver also includes an event-driven model, which sends useful and important events to user space. We need to initialize the event loop before installing the Ethernet driver. For more information about event-driven programming, please refer to :doc:`ESP Event <../system/esp_event>`.
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
/** Event handler for Ethernet events */
|
||||
static void eth_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
uint8_t mac_addr[6] = {0};
|
||||
/* we can get the ethernet driver handle from event data */
|
||||
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
|
||||
|
||||
switch (event_id) {
|
||||
case ETHERNET_EVENT_CONNECTED:
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
|
||||
ESP_LOGI(TAG, "Ethernet Link Up");
|
||||
ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
break;
|
||||
case ETHERNET_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "Ethernet Link Down");
|
||||
break;
|
||||
case ETHERNET_EVENT_START:
|
||||
ESP_LOGI(TAG, "Ethernet Started");
|
||||
break;
|
||||
case ETHERNET_EVENT_STOP:
|
||||
ESP_LOGI(TAG, "Ethernet Stopped");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
esp_event_loop_create_default(); // create a default event loop that runs in the background
|
||||
esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL); // register Ethernet event handler (to deal with user-specific stuff when events like link up/down happened)
|
||||
|
||||
Start Ethernet Driver
|
||||
---------------------
|
||||
|
||||
After driver installation, we can start Ethernet immediately.
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
esp_eth_start(eth_handle); // start Ethernet driver state machine
|
||||
|
||||
.. _connect-driver-to-stack:
|
||||
|
||||
Connect Driver to TCP/IP Stack
|
||||
------------------------------
|
||||
|
||||
Up until now, we have installed the Ethernet driver. From the view of OSI (Open System Interconnection), we are still on level 2 (i.e., Data Link Layer). While we can detect link up and down events and gain MAC address in user space, it is infeasible to obtain the IP address, let alone send an HTTP request. The TCP/IP stack used in ESP-IDF is called LwIP. For more information about it, please refer to :doc:`LwIP <../../api-guides/lwip>`.
|
||||
|
||||
To connect the Ethernet driver to TCP/IP stack, follow these three steps:
|
||||
|
||||
1. Create a network interface for the Ethernet driver
|
||||
2. Attach the network interface to the Ethernet driver
|
||||
3. Register IP event handlers
|
||||
|
||||
For more information about the network interface, please refer to :doc:`Network Interface <esp_netif>`.
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
/** Event handler for IP_EVENT_ETH_GOT_IP */
|
||||
static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
|
||||
const esp_netif_ip_info_t *ip_info = &event->ip_info;
|
||||
|
||||
ESP_LOGI(TAG, "Ethernet Got IP Address");
|
||||
ESP_LOGI(TAG, "~~~~~~~~~~~");
|
||||
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
|
||||
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
|
||||
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
|
||||
ESP_LOGI(TAG, "~~~~~~~~~~~");
|
||||
}
|
||||
|
||||
esp_netif_init()); // Initialize TCP/IP network interface (should be called only once in application)
|
||||
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); // apply default network interface configuration for Ethernet
|
||||
esp_netif_t *eth_netif = esp_netif_new(&cfg); // create network interface for Ethernet driver
|
||||
|
||||
esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)); // attach Ethernet driver to TCP/IP stack
|
||||
esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL); // register user defined IP event handlers
|
||||
esp_eth_start(eth_handle); // start Ethernet driver state machine
|
||||
|
||||
.. warning::
|
||||
It is recommended to fully initialize the Ethernet driver and network interface before registering the user's Ethernet/IP event handlers, i.e., register the event handlers as the last thing prior to starting the Ethernet driver. Such an approach ensures that Ethernet/IP events get executed first by the Ethernet driver or network interface so the system is in the expected state when executing the user's handlers.
|
||||
|
||||
.. _misc-operation-of-driver:
|
||||
|
||||
Misc Control of Ethernet Driver
|
||||
-------------------------------
|
||||
|
||||
The following functions should only be invoked after the Ethernet driver has been installed.
|
||||
|
||||
* Stop Ethernet driver: :cpp:func:`esp_eth_stop`
|
||||
* Update Ethernet data input path: :cpp:func:`esp_eth_update_input_path`
|
||||
* Misc get/set of Ethernet driver attributes: :cpp:func:`esp_eth_ioctl`
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
/* get MAC address */
|
||||
uint8_t mac_addr[6];
|
||||
memset(mac_addr, 0, sizeof(mac_addr));
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
|
||||
ESP_LOGI(TAG, "Ethernet MAC Address: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
|
||||
/* get PHY address */
|
||||
int phy_addr = -1;
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_PHY_ADDR, &phy_addr);
|
||||
ESP_LOGI(TAG, "Ethernet PHY Address: %d", phy_addr);
|
||||
|
||||
.. _time-stamping:
|
||||
|
||||
.. only:: SOC_EMAC_IEEE1588V2_SUPPORTED
|
||||
|
||||
EMAC Hardware Time Stamping
|
||||
---------------------------
|
||||
|
||||
Time stamping in EMAC allows precise tracking of when Ethernet frames are transmitted or received. Hardware time stamping is crucial for applications like Precision Time Protocol (PTP) because it minimizes jitter and inaccuracies that can occur when relying on software time stamps. Embedded time stamps in hardware avoid delays introduced by software layers or processing overhead. Therefore, it ensures nanosecond-level precision.
|
||||
|
||||
.. warning::
|
||||
|
||||
Time stamp associated API is currently in **"Experimental Feature"** state so be aware it may change with future releases.
|
||||
|
||||
The basic way how to enable time stamping, get and set time in the EMAC is demonstrated below.
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
esp_eth_mac_t *mac;
|
||||
esp_eth_get_mac_instance(eth_hndl, &mac);
|
||||
|
||||
// Enable hardware time stamping
|
||||
eth_mac_ptp_config_t ptp_cfg = ETH_MAC_ESP_PTP_DEFAULT_CONFIG();
|
||||
esp_eth_mac_ptp_enable(mac, &ptp_cfg);
|
||||
|
||||
// Get current EMAC time
|
||||
eth_mac_time_t ptp_time;
|
||||
esp_eth_mac_get_ptp_time(mac, &ptp_time);
|
||||
|
||||
// Set EMAC time
|
||||
ptp_time = {
|
||||
.seconds = 42,
|
||||
.nanoseconds = 0
|
||||
};
|
||||
esp_eth_mac_set_ptp_time(mac, &ptp_time);
|
||||
|
||||
The PTP module be can configured as follows:
|
||||
|
||||
.. list::
|
||||
* :cpp:member:`eth_mac_ptp_config_t::clk_src`: Clock source for PTP. Select one of the clock sources offered by the :cpp:type:`soc_periph_emac_ptp_clk_src_t` enumeration.
|
||||
|
||||
* :cpp:member:`eth_mac_ptp_config_t::clk_src_period_ns`: Period of the clock source for PTP in nanoseconds. For example, if the clock source is 40MHz, the period is 25ns.
|
||||
|
||||
* :cpp:member:`eth_mac_ptp_config_t::required_accuracy_ns`: Required accuracy for PTP in nanoseconds. The required accuracy must be worse than clock source for PTP. For example, if the clock source is 40MHz (25ns period), the required accuracy is 40ns.
|
||||
|
||||
* :cpp:member:`eth_mac_ptp_config_t::roll_type`: Rollover mode (digital or binary) for subseconds register. The binary rollover mode is recommended as it provides a more precise time synchronization.
|
||||
|
||||
Time stamps for transmitted and received frames can be accessed via the last argument of the registered :cpp:member:`esp_eth_config_t::stack_input_info` function for the receive path, and via the ``ctrl`` argument of the :cpp:func:`esp_eth_transmit_ctrl_vargs` function for the transmit path. However, a more user-friendly approach to retrieve time stamp information in user space is by utilizing the L2 TAP :ref:`Extended Buffer <esp_netif_l2tap_ext_buff>` mechanism.
|
||||
|
||||
You have an option to schedule event at precise point in time by registering callback function and configuring a target time when the event is supposed to be fired. Note that the callback function is then called from ISR context so it should be as brief as possible.
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
// Register the callback function
|
||||
esp_eth_mac_set_target_time_cb(mac, ts_callback);
|
||||
|
||||
// Set time when event is triggered
|
||||
eth_mac_time_t mac_target_time = {
|
||||
.seconds = 42,
|
||||
.nanoseconds = 0
|
||||
};
|
||||
esp_eth_mac_set_target_time(mac, &mac_target_time);
|
||||
|
||||
Alternatively, the PTP-synchronized time can be exposed via a PPS (Pulse-Per-Second) signal on a GPIO. This provides a precise hardware time reference that can be used to synchronize external devices, align independent clock domains, or drive time-critical processes outside the ESP32 chip series. As the name suggests, the PPS signal is a pulse that occurs once per second by default. However, the frequency can be adjusted by setting the PPS0 output frequency using the :cpp:func:`esp_eth_mac_set_pps_out_freq` function. The command accepts an integer value in the range of 0-16384, where 0 = 1PPS (narrow pulse), other values generate square clock signal. The clock frequency must be power of two and less than or equal to 16384 Hz. Note that due to non-linear toggling of bits in the digital rollover mode, the actual frequency is an average number (duty cycle differs from 50% in overall one second period). This behavior does not apply to the binary rollover mode and so this mode is recommended. The PPS signal can be configured to be output at a GPIO using the :cpp:func:`esp_eth_mac_set_pps_out_gpio` function.
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
.. note::
|
||||
The PPS signal output on GPIO pin is available starting from ESP32-P4 silicon revision 3.
|
||||
|
||||
.. _flow-control:
|
||||
|
||||
Flow Control
|
||||
------------
|
||||
|
||||
Ethernet on MCU usually has a limitation in the number of frames it can handle during network congestion, because of the limitation in RAM size. A sending station might be transmitting data faster than the peer end can accept it. The ethernet flow control mechanism allows the receiving node to signal the sender requesting the suspension of transmissions until the receiver catches up. The magic behind that is the pause frame, which was defined in IEEE 802.3x.
|
||||
|
||||
Pause frame is a special Ethernet frame used to carry the pause command, whose EtherType field is ``0x8808``, with the Control opcode set to ``0x0001``. Only stations configured for full-duplex operation may send pause frames. When a station wishes to pause the other end of a link, it sends a pause frame to the 48-bit reserved multicast address of ``01-80-C2-00-00-01``. The pause frame also includes the period of pause time being requested, in the form of a two-byte integer, ranging from ``0`` to ``65535``.
|
||||
|
||||
After the Ethernet driver installation, the flow control feature is disabled by default. You can enable it by:
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
bool flow_ctrl_enable = true;
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_S_FLOW_CTRL, &flow_ctrl_enable);
|
||||
|
||||
One thing that should be kept in mind is that the pause frame ability is advertised to the peer end by PHY during auto-negotiation. The Ethernet driver sends a pause frame only when both sides of the link support it.
|
||||
|
||||
.. -------------------------------- Examples -----------------------------------
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
* :example:`ethernet/basic` demonstrates how to use the Ethernet driver, covering driver installation, attaching it to `esp_netif`, sending DHCP requests, and obtaining a pingable IP address.
|
||||
|
||||
* :example:`ethernet/iperf` demonstrates how to use the Ethernet capabilities to measure the throughput/bandwidth using iPerf.
|
||||
|
||||
* :example:`ethernet/ptp` demonstrates the use of Precision Time Protocol (PTP) for time synchronization over Ethernet.
|
||||
|
||||
* :example:`network/vlan_support` demonstrates how to create virtual network interfaces over Ethernet, including VLAN and non-VLAN interfaces.
|
||||
|
||||
* :example:`network/sta2eth` demonstrates how to create a 1-to-1 bridge using a Wi-Fi station and a wired interface such as Ethernet or USB.
|
||||
|
||||
* :example:`network/simple_sniffer` demonstrates how to use Wi-Fi and Ethernet in sniffer mode to capture packets and save them in PCAP format.
|
||||
|
||||
* :example:`network/eth2ap` demonstrates how to implement a bridge that forwards packets between an Ethernet port and a Wi-Fi AP interface. It uses {IDF_TARGET_NAME} to create a 1-to-many connection between Ethernet and Wi-Fi without initializing the TCP/IP stack.
|
||||
|
||||
* :example:`network/bridge` demonstrates how to use the LwIP IEEE 802.1D bridge to forward Ethernet frames between multiple network segments based on MAC addresses.
|
||||
|
||||
* Most protocol examples should also work for Ethernet: :example:`protocols`.
|
||||
|
||||
.. ------------------------------ Advanced Topics -------------------------------
|
||||
|
||||
.. _advanced-topics:
|
||||
|
||||
Advanced Topics
|
||||
---------------
|
||||
|
||||
.. _custom-phy-driver:
|
||||
|
||||
Custom PHY Driver
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are multiple PHY manufacturers with wide portfolios of chips available. The ESP-IDF supports ``Generic PHY`` and also several specific PHY chips however one can easily get to a point where none of them satisfies the user's actual needs due to price, features, stock availability, etc.
|
||||
|
||||
Luckily, a management interface between EMAC and PHY is standardized by IEEE 802.3 in Section 22.2.4 Management Functions. It defines provisions of the so-called "MII Management Interface" to control the PHY and gather status from the PHY. A set of management registers is defined to control chip behavior, link properties, auto-negotiation configuration, etc. This basic management functionality is addressed by :component_file:`esp_eth/src/phy/esp_eth_phy_802_3.c` in ESP-IDF and so it makes the creation of a new custom PHY chip driver quite a simple task.
|
||||
|
||||
.. note::
|
||||
Always consult with PHY datasheet since some PHY chips may not comply with IEEE 802.3, Section 22.2.4. It does not mean you are not able to create a custom PHY driver, but it just requires more effort. You will have to define all PHY management functions.
|
||||
|
||||
The majority of PHY management functionality required by the ESP-IDF Ethernet driver is covered by the :component_file:`esp_eth/src/phy/esp_eth_phy_802_3.c`. However, the following may require developing chip-specific management functions:
|
||||
|
||||
* Link status which is almost always chip-specific
|
||||
* Chip initialization, even though not strictly required, should be customized to at least ensure that the expected chip is used
|
||||
* Chip-specific features configuration
|
||||
|
||||
**Steps to create a custom PHY driver:**
|
||||
|
||||
1. Define vendor-specific registry layout based on the PHY datasheet.
|
||||
2. Prepare derived PHY management object info structure which:
|
||||
|
||||
* must contain at least parent IEEE 802.3 :cpp:class:`phy_802_3_t` object
|
||||
* optionally contain additional variables needed to support non-IEEE 802.3 or customized functionality.
|
||||
|
||||
3. Define chip-specific management call-back functions.
|
||||
4. Initialize parent IEEE 802.3 object and re-assign chip-specific management call-back functions.
|
||||
|
||||
Once you finish the new custom PHY driver implementation, consider sharing it among other users via `ESP Component Registry <https://components.espressif.com/>`_.
|
||||
|
||||
.. ---------------------------- API Reference ----------------------------------
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/eth_types.inc
|
||||
.. include-build-file:: inc/esp_eth.inc
|
||||
.. include-build-file:: inc/esp_eth_driver.inc
|
||||
.. include-build-file:: inc/esp_eth_com.inc
|
||||
.. include-build-file:: inc/esp_eth_mac.inc
|
||||
.. include-build-file:: inc/esp_eth_mac_esp.inc
|
||||
.. include-build-file:: inc/esp_eth_mac_spi.inc
|
||||
.. include-build-file:: inc/esp_eth_phy.inc
|
||||
.. include-build-file:: inc/esp_eth_phy_802_3.inc
|
||||
.. include-build-file:: inc/esp_eth_netif_glue.inc
|
||||
@@ -0,0 +1,28 @@
|
||||
Wi-Fi Aware\ :sup:`TM` (NAN)
|
||||
===================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Wi-Fi Aware\ :sup:`TM` or NAN (Neighbor Awareness Networking) is a protocol that allows Wi-Fi devices to discover services in their proximity. Typically, location-based services are based on querying servers for information about the environment and the location knowledge is based on GPS or other location reckoning techniques. However, NAN does not require real-time connection to servers, GPS or other geo-location, but instead uses direct device-to-device Wi-Fi to discover and exchange information. NAN scales effectively in dense Wi-Fi environments and complements the connectivity of Wi-Fi by providing information about people and services in the proximity.
|
||||
|
||||
Multiple NAN devices which are in the vicinity form a NAN cluster which allows them to communicate with each other. Devices within a NAN cluster can advertise (Publish method) or look for (Subscribe method) services using NAN Service Discovery protocols. Matching of services is done by service name, once a match is found, a device can either send a message or establish an IPv6 Datapath with the peer.
|
||||
|
||||
{IDF_TARGET_NAME} supports Wi-Fi Aware in standalone mode with support for both Service Discovery and Datapath. Wi-Fi Aware is still an evolving protocol. Please refer to Wi-Fi Alliance's official page on `Wi-Fi Aware <https://www.wi-fi.org/discover-wi-fi/wi-fi-aware>`_ for more information. Many Android smartphones with Android 8 or higher support Wi-Fi Aware. Refer to Android's developer guide on Wi-Fi Aware `Wi-Fi Aware <https://www.wi-fi.org/discover-wi-fi/wi-fi-aware>`_ for more information.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`wifi/wifi_aware/nan_console` demonstrates how to use the NAN protocol to discover services in proximity, establish a datapath, and communicate between devices without requiring an Internet or AP connection. It provides console commands for configuring NAN services, publishing or subscribing to a service, sending messages, and initiating or terminating a datapath.
|
||||
|
||||
- :example:`wifi/wifi_aware/nan_publisher` demonstrates how to use the NAN protocol to publish a service for advertising a service to other devices in the vicinity, and how to respond to the devices that subscribes to the service.
|
||||
|
||||
- :example:`wifi/wifi_aware/nan_subscriber` demonstrates how to use the NAN protocol to discover other devices that publish the required service in the proximity, and communicate with them either by sending a message or initiating a datapath.
|
||||
|
||||
- :example:`wifi/wifi_aware/usd_publisher` demonstrates how to use lightweight NAN Unsynchronized Service Discovery (NAN-USD) protocol to advertise a service to nearby devices without forming NAN clusters. It walks through bringing up USD, publishing a service, responding to subscribers with follow-up messages, and returning to the idle state once discovery completes.
|
||||
|
||||
- :example:`wifi/wifi_aware/usd_subscriber` demonstrates how to use the lightweight NAN Unsynchronized Service Discovery (NAN-USD) protocol to discover services advertised by nearby devices and interact with them. It covers enabling USD discovery, subscribing to services, handling follow-up exchanges, and terminating discovery after the session ends.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_nan.inc
|
||||
@@ -0,0 +1,300 @@
|
||||
ESP-NETIF
|
||||
*********
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
The purpose of the ESP-NETIF library is twofold:
|
||||
|
||||
- It provides an abstraction layer for the application on top of the TCP/IP stack. This allows applications to choose between IP stacks in the future.
|
||||
- The APIs it provides are thread-safe, even if the underlying TCP/IP stack APIs are not.
|
||||
|
||||
ESP-IDF currently implements ESP-NETIF for the lwIP TCP/IP stack only. However, the adapter itself is TCP/IP implementation-agnostic and allows different implementations.
|
||||
|
||||
Some ESP-NETIF API functions are intended to be called by application code, for example, to get or set interface IP addresses, and configure DHCP. Other functions are intended for internal ESP-IDF use by the network driver layer. In many cases, applications do not need to call ESP-NETIF APIs directly as they are called by the default network event handlers.
|
||||
|
||||
If you are only interested in using the most common network interfaces with default setting, please read :ref:`esp_netif_user` to see how you can initialize default interfaces and register event handlers.
|
||||
|
||||
If you would like to learn more about the library interaction with other components, please refer to the :ref:`esp-netif structure`.
|
||||
|
||||
In case your application needs to configure the network interfaces differently, e.g. setting a static IP address or just update the configuration runtime, please read :ref:`esp_netif_programmer`.
|
||||
|
||||
If you would like to develop your own network driver, implement support for a new TCP/IP stack or customize the ESP-NETIF in some other way, please refer to the :ref:`esp_netif_developer`.
|
||||
|
||||
.. _esp_netif_user:
|
||||
|
||||
ESP-NETIF User's Manual
|
||||
=======================
|
||||
|
||||
It is usually just enough to create a default network interface after startup and destroy it upon closing (see :ref:`esp_netif_init`). It is also useful to receive a notification upon assigning a new IP address or losing it (see :ref:`esp-netif-ip-events`).
|
||||
|
||||
|
||||
.. _esp_netif_init:
|
||||
|
||||
Initialization
|
||||
--------------
|
||||
|
||||
Since the ESP-NETIF component uses system events, the typical network startup code looks like this (note that error handling is omitted for clarity, see :example_file:`ethernet/basic/main/ethernet_example_main.c` for complete startup code):
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// 1) Initialize the TCP/IP stack and the event loop
|
||||
esp_netif_init();
|
||||
esp_event_loop_create_default();
|
||||
|
||||
// 2) Create the network interface handle
|
||||
esp_netif = esp_netif_new(&config);
|
||||
|
||||
// 3) Create the network interface driver (e.g., Ethernet) and it's network layer glue
|
||||
// and register the ESP-NETIF event (e.g., to bring the interface up upon link-up event)
|
||||
esp_netif_glue_t glue = driver_glue(driver);
|
||||
|
||||
// 4) Attach the driver's glue layer to the network interface handle
|
||||
esp_netif_attach(esp_netif, glue);
|
||||
|
||||
// 5) Register user-side event handlers
|
||||
esp_event_handler_register(DRIVER_EVENT, ...); // to observe driver states, e.g., link-up
|
||||
esp_event_handler_register(IP_EVENT, ...); // to observe ESP-NETIF states, e.g., get an IP
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
These steps must be performed in the exact order shown above, as the network interface drivers use the default event loop when registering system events.
|
||||
|
||||
- The default event loop needs to be created **before** initializing an interface driver, as the driver typically needs to register system event handlers.
|
||||
- Registering application event handlers must occur **after** calling :cpp:func:`esp_netif_attach`, because event handlers are called in the order they were registered. To ensure that system handlers are called first, you should register application handlers afterward.
|
||||
|
||||
Steps ``2)``, ``3)`` and ``4)`` are quite complex for most common use-cases, so ESP-NETIF provides some pre-configured interfaces and convenience functions that create the most common network interfaces in their most common configurations.
|
||||
|
||||
.. note::
|
||||
|
||||
Each network interface needs to be initialized separately, so if you would like to use multiple interfaces, you would have to run steps ``2)`` to ``5)`` for every interface. Set ``1)`` should be performed only once.
|
||||
|
||||
|
||||
Creating and configuring the interface and attaching the network interface driver to it (steps ``2)``, ``3)`` and ``4)``) is described in :ref:`create_esp_netif`.
|
||||
|
||||
Using the ESP-NETIF event handlers (step ``5)``) is described in :ref:`esp-netif-ip-events`.
|
||||
|
||||
|
||||
.. _create_esp_netif:
|
||||
|
||||
Common Network Interfaces
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As the initialization of network interfaces could be quite complex, ESP-NETIF provides some convenient methods of creating the most common ones, such as Wi-Fi and Ethernet.
|
||||
|
||||
Please refer to the following examples to understand the initialization process of the default interface:
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_WIFI_SUPPORTED: - :example:`wifi/getting_started/station` demonstrates how to use the station functionality to connect {IDF_TARGET_NAME} to an AP.
|
||||
|
||||
:CONFIG_ESP_WIFI_SOFTAP_SUPPORT: - :example:`wifi/getting_started/softAP` demonstrates how to use the SoftAP functionality to configure {IDF_TARGET_NAME} as an AP.
|
||||
|
||||
- :example:`ethernet/basic` demonstrates how to use the Ethernet driver, attach it to `esp_netif`, and obtain an IP address that can be pinged.
|
||||
|
||||
- :example:`protocols/l2tap` demonstrates how to use the ESP-NETIF L2 TAP interface to access the Data Link Layer for receiving and transmitting frames, implement non-IP protocols, and echo Ethernet frames with specific EthTypes.
|
||||
|
||||
- :example:`protocols/static_ip` demonstrates how to configure Wi-Fi as a station, including setting up a static IP, netmask, gateway and DNS server.
|
||||
|
||||
.. only:: SOC_WIFI_SUPPORTED
|
||||
|
||||
Wi-Fi Default Initialization
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The initialization code as well as registering event handlers for default interfaces, such as softAP and station, are provided in separate APIs to facilitate simple startup code for most applications:
|
||||
|
||||
* :cpp:func:`esp_netif_create_default_wifi_sta()`
|
||||
|
||||
.. only:: CONFIG_ESP_WIFI_SOFTAP_SUPPORT
|
||||
|
||||
* :cpp:func:`esp_netif_create_default_wifi_ap()`
|
||||
|
||||
.. only:: SOC_WIFI_SUPPORTED
|
||||
|
||||
Please note that these functions return the ``esp_netif`` handle, i.e., a pointer to a network interface object allocated and configured with default settings, which means that:
|
||||
|
||||
* The created object has to be destroyed if a network de-initialization is provided by an application using :cpp:func:`esp_netif_destroy_default_wifi()`.
|
||||
|
||||
* These *default* interfaces must not be created multiple times unless the created handle is deleted using :cpp:func:`esp_netif_destroy_default_wifi()`.
|
||||
|
||||
|
||||
.. only:: CONFIG_ESP_WIFI_SOFTAP_SUPPORT
|
||||
|
||||
* When using Wi-Fi in ``AP+STA`` mode, both these interfaces have to be created. Please refer to the example :example_file:`wifi/softap_sta/main/softap_sta.c`.
|
||||
|
||||
.. _esp-netif-ip-events:
|
||||
|
||||
IP Events
|
||||
---------
|
||||
|
||||
In the final section of :ref:`esp_netif_init` code (step ``5)``), you register two sets of event handlers:
|
||||
|
||||
* **Network Interface Driver Events**: These events notify you about the driver's lifecycle states, such as when a Wi-Fi station joins an AP or gets disconnected. Handling these events is outside the scope of the ESP-NETIF component. It is worth noting that the same events are also used by ESP-NETIF to set the network interface to a desired state. Therefore, if your application uses the driver's events to determine specific states of the network interface, you should register these handlers **after** registering the system handlers (which typically happens when attaching the driver to the interface). This is why handler registration occurs in the final step of the :ref:`esp_netif_init` code.
|
||||
|
||||
* **IP Events**: These events notify you about IP address changes, such as when a new address is assigned or when a valid address is lost. Specific types of these events are listed in :cpp:type:`ip_event_t`. Each common interface has a related pair of ``GOT_IP`` and ``LOST_IP`` events.
|
||||
|
||||
Registering event handlers is crucial due to the asynchronous nature of networking, where changes in network state can occur unpredictably. By registering event handlers, applications can respond to these changes promptly, ensuring appropriate actions are taken in response to network events.
|
||||
|
||||
.. note::
|
||||
|
||||
Lost IP events are triggered by a timer that can be enabled or disabled by :ref:`CONFIG_ESP_NETIF_LOST_IP_TIMER_ENABLE`,
|
||||
with the delay configured by :ref:`CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL`. The timer is started upon losing the IP address, and the event is raised after the configured interval (120 s by default).
|
||||
For backward compatibility, setting the interval to 0 also disables the timer.
|
||||
|
||||
.. _esp-netif structure:
|
||||
|
||||
ESP-NETIF Architecture
|
||||
----------------------
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
|
||||
| (A) USER CODE |
|
||||
| Apps |
|
||||
.................| init settings events |
|
||||
. +----------------------------------------+
|
||||
. . | *
|
||||
. . | *
|
||||
--------+ +================================+ * +-----------------------+
|
||||
| | new/config get/set/apps | * | init |
|
||||
| | |...*.....| Apps (DHCP, SNTP) |
|
||||
| |--------------------------------| * | |
|
||||
init | | |**** | |
|
||||
start |************| event handler |*********| DHCP |
|
||||
stop | | | | |
|
||||
| |--------------------------------| | |
|
||||
| | | | NETIF |
|
||||
+-----| | | +-----------------+ |
|
||||
| glue|---<----|---| esp_netif_transmit |--<------| netif_output | |
|
||||
| | | | | | | |
|
||||
| |--->----|---| esp_netif_receive |-->------| netif_input | |
|
||||
| | | | | + ----------------+ |
|
||||
| |...<....|...| esp_netif_free_rx_buffer |...<.....| packet buffer |
|
||||
+-----| | | | | | |
|
||||
| | | | | | (D) |
|
||||
(B) | | | | (C) | +-----------------------+
|
||||
--------+ | | +================================+ NETWORK STACK
|
||||
NETWORK | | ESP-NETIF
|
||||
INTERFACE | |
|
||||
DRIVER | | +--------------------------------+ +------------------+
|
||||
| | | |.........| open/close |
|
||||
| | | | | |
|
||||
| -<--| l2tap_write |-----<---| write |
|
||||
| | | | |
|
||||
---->--| esp_vfs_l2tap_eth_filter_frame |----->---| read |
|
||||
| | | (A) |
|
||||
| (E) | +------------------+
|
||||
+--------------------------------+ USER CODE
|
||||
ESP-NETIF L2 TAP
|
||||
|
||||
|
||||
Data and Event Flow in the Diagram
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* ``........`` Initialization line from user code to ESP-NETIF and network interface driver
|
||||
|
||||
* ``--<--->--`` Data packets going from communication media to TCP/IP stack and back
|
||||
|
||||
* ``********`` Events aggregated in ESP-NETIF propagate to the driver, user code, and network stack
|
||||
|
||||
* ``|`` User settings and runtime configuration
|
||||
|
||||
ESP-NETIF Interaction
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A) User Code, Boilerplate
|
||||
'''''''''''''''''''''''''
|
||||
|
||||
Overall application interaction with a specific IO driver for the communication media (network interface driver) and configured TCP/IP network stack is abstracted using ESP-NETIF APIs and is outlined as below:
|
||||
|
||||
A) Initialization code
|
||||
|
||||
1) Initializes IO driver
|
||||
2) Creates a new instance of ESP-NETIF and configure it with
|
||||
|
||||
* ESP-NETIF specific options (flags, behavior, name)
|
||||
* Network stack options (netif init and input functions, not publicly available)
|
||||
* IO driver specific options (transmit, free rx buffer functions, IO driver handle)
|
||||
|
||||
3) Attaches the IO driver handle to the ESP-NETIF instance created in the above steps
|
||||
4) Configures event handlers
|
||||
|
||||
* Use default handlers for common interfaces defined in IO drivers; or define a specific handler for customized behavior or new interfaces
|
||||
* Register handlers for app-related events (such as IP lost or acquired)
|
||||
|
||||
B) Interaction with network interfaces using ESP-NETIF API
|
||||
|
||||
1) Gets and sets TCP/IP-related parameters (DHCP, IP, etc)
|
||||
2) Receives IP events (connect or disconnect)
|
||||
3) Controls application lifecycle (set interface up or down)
|
||||
|
||||
|
||||
B) Network Interface Driver
|
||||
'''''''''''''''''''''''''''
|
||||
|
||||
Network interface driver (also called I/O Driver, or Media Driver) plays these two important roles in relation to ESP-NETIF:
|
||||
|
||||
1) Event handlers: Defines behavior patterns of interaction with ESP-NETIF (e.g., ethernet link-up -> turn netif on)
|
||||
|
||||
2) Glue IO layer: Adapts the input or output functions to use ESP-NETIF transmit, receive, and free receive buffer
|
||||
|
||||
* Installs driver_transmit to the appropriate ESP-NETIF object so that outgoing packets from the network stack are passed to the IO driver
|
||||
* Calls :cpp:func:`esp_netif_receive()` to pass incoming data to the network stack
|
||||
|
||||
|
||||
C) ESP-NETIF
|
||||
''''''''''''
|
||||
|
||||
ESP-NETIF serves as an intermediary between an IO driver and a network stack, connecting the packet data path between the two. It provides a set of interfaces for attaching a driver to an ESP-NETIF object at runtime and configures a network stack during compiling. Additionally, a set of APIs is provided to control the network interface lifecycle and its TCP/IP properties. As an overview, the ESP-NETIF public interface can be divided into six groups:
|
||||
|
||||
1) Initialization APIs (to create and configure ESP-NETIF instance)
|
||||
2) Input or Output API (for passing data between IO driver and network stack)
|
||||
3) Event or Action API
|
||||
|
||||
* Used for network interface lifecycle management
|
||||
* ESP-NETIF provides building blocks for designing event handlers
|
||||
|
||||
4) Setters and Getters API for basic network interface properties
|
||||
5) Network stack abstraction API: enabling user interaction with TCP/IP stack
|
||||
|
||||
* Set interface up or down
|
||||
* DHCP server and client API
|
||||
* DNS API
|
||||
* :ref:`esp_netif-sntp-api`
|
||||
|
||||
6) Driver conversion utilities API
|
||||
|
||||
|
||||
D) Network Stack
|
||||
''''''''''''''''
|
||||
|
||||
The network stack has no public interaction with application code with regard to public interfaces and shall be fully abstracted by ESP-NETIF API.
|
||||
|
||||
|
||||
E) ESP-NETIF L2 TAP Interface
|
||||
'''''''''''''''''''''''''''''
|
||||
|
||||
The ESP-NETIF L2 TAP interface is a mechanism in ESP-IDF used to access Data Link Layer (L2 per OSI/ISO) for frame reception and transmission from the user application. Its typical usage in the embedded world might be the implementation of non-IP-related protocols, e.g., PTP, Wake on LAN. Note that only Ethernet (IEEE 802.3) is currently supported. Please read more about L2 TAP in :ref:`esp_netif_l2tap`.
|
||||
|
||||
.. _esp_netif_programmer:
|
||||
|
||||
ESP-NETIF Programmer's Manual
|
||||
=============================
|
||||
|
||||
In some cases, it is not enough to simply initialize a network interface by default, start using it and connect to the local network. If so, please consult the programming guide: :doc:`/api-reference/network/esp_netif_programming`.
|
||||
|
||||
You would typically need to use specific sets of ESP-NETIF APIs in the following use-cases:
|
||||
|
||||
* :ref:`esp_netif_set_ip`
|
||||
* :ref:`esp_netif_set_dhcp`
|
||||
* :ref:`esp_netif-sntp-api`
|
||||
* :ref:`esp_netif_l2tap`
|
||||
* :ref:`esp_netif_other_events`
|
||||
* :ref:`esp_netif_api_reference`
|
||||
|
||||
.. _esp_netif_developer:
|
||||
|
||||
ESP-NETIF Developer's Manual
|
||||
============================
|
||||
|
||||
In some cases, user applications might need to customize ESP-NETIF, register custom drivers or even use a custom TCP/IP stack. If so, please consult the :doc:`/api-reference/network/esp_netif_driver`.
|
||||
@@ -0,0 +1,120 @@
|
||||
ESP-NETIF Developer's manual
|
||||
============================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
As shown in the :ref:`esp-netif structure` diagram, ESP-NETIF is in fact an intermediary between the I/O driver and the TCP/IP stack. This manual describes customization of these two sides, that is if you need to implement :ref:`esp_netif_custom_driver` or if you need to employ :ref:`esp_netif_tcpip_stack`.
|
||||
|
||||
|
||||
.. _esp_netif_custom_driver:
|
||||
|
||||
ESP-NETIF Custom I/O Driver
|
||||
---------------------------
|
||||
|
||||
This section outlines implementing a new I/O driver with ESP-NETIF connection capabilities.
|
||||
|
||||
By convention, the I/O driver has to register itself as an ESP-NETIF driver, and thus holds a dependency on ESP-NETIF component and is responsible for providing data path functions, post-attach callback and in most cases, also default event handlers to define network interface actions based on driver's lifecycle transitions.
|
||||
|
||||
|
||||
Packet Input/Output
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
According to the diagram shown in the :ref:`esp-netif structure` part, the following three API functions for the packet data path must be defined for connecting with ESP-NETIF:
|
||||
|
||||
* :cpp:func:`esp_netif_transmit()`
|
||||
* :cpp:func:`esp_netif_free_rx_buffer()`
|
||||
* :cpp:func:`esp_netif_receive()`
|
||||
|
||||
The first two functions for transmitting and freeing the rx buffer are provided as callbacks, i.e., they get called from ESP-NETIF (and its underlying TCP/IP stack) and I/O driver provides their implementation.
|
||||
|
||||
The receiving function on the other hand gets called from the I/O driver, so that the driver's code simply calls :cpp:func:`esp_netif_receive()` on a new data received event.
|
||||
|
||||
|
||||
Post Attach Callback
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A final part of the network interface initialization consists of attaching the ESP-NETIF instance to the I/O driver, by means of calling the following API:
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_err_t esp_netif_attach(esp_netif_t *esp_netif, esp_netif_iodriver_handle driver_handle);
|
||||
|
||||
It is assumed that the ``esp_netif_iodriver_handle`` is a pointer to driver's object, a struct derived from ``struct esp_netif_driver_base_s``, so that the first member of I/O driver structure must be this base structure with pointers to:
|
||||
|
||||
* post-attach function callback
|
||||
* related ESP-NETIF instance
|
||||
|
||||
As a result, the I/O driver has to create an instance of the struct per below:
|
||||
|
||||
.. code:: c
|
||||
|
||||
typedef struct my_netif_driver_s {
|
||||
esp_netif_driver_base_t base; /*!< base structure reserved as esp-netif driver */
|
||||
driver_impl *h; /*!< handle of driver implementation */
|
||||
} my_netif_driver_t;
|
||||
|
||||
with actual values of ``my_netif_driver_t::base.post_attach`` and the actual drivers handle ``my_netif_driver_t::h``.
|
||||
|
||||
So when the :cpp:func:`esp_netif_attach()` gets called from the initialization code, the post-attach callback from I/O driver's code gets executed to mutually register callbacks between ESP-NETIF and I/O driver instances. Typically the driver is started as well in the post-attach callback. An example of a simple post-attach callback is outlined below:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static esp_err_t my_post_attach_start(esp_netif_t * esp_netif, void * args)
|
||||
{
|
||||
my_netif_driver_t *driver = args;
|
||||
const esp_netif_driver_ifconfig_t driver_ifconfig = {
|
||||
.driver_free_rx_buffer = my_free_rx_buf,
|
||||
.transmit = my_transmit,
|
||||
.handle = driver->driver_impl
|
||||
};
|
||||
driver->base.netif = esp_netif;
|
||||
ESP_ERROR_CHECK(esp_netif_set_driver_config(esp_netif, &driver_ifconfig));
|
||||
my_driver_start(driver->driver_impl);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
Default Handlers
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
I/O drivers also typically provide default definitions of lifecycle behavior of related network interfaces based on state transitions of I/O drivers. For example *driver start* ``->`` *network start*, etc.
|
||||
|
||||
An example of such a default handler is provided below:
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_err_t my_driver_netif_set_default_handlers(my_netif_driver_t *driver, esp_netif_t * esp_netif)
|
||||
{
|
||||
driver_set_event_handler(driver->driver_impl, esp_netif_action_start, MY_DRV_EVENT_START, esp_netif);
|
||||
driver_set_event_handler(driver->driver_impl, esp_netif_action_stop, MY_DRV_EVENT_STOP, esp_netif);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
Network Stack Connection
|
||||
------------------------
|
||||
|
||||
The packet data path functions for transmitting and freeing the rx buffer (defined in the I/O driver) are called from the ESP-NETIF, specifically from its TCP/IP stack connecting layer.
|
||||
|
||||
Note that ESP-IDF provides several network stack configurations for the most common network interfaces, such as for the Wi-Fi station or Ethernet. These configurations are defined in :component_file:`esp_netif/include/esp_netif_defaults.h` and should be sufficient for most network drivers.
|
||||
|
||||
In some cases, you might want to define a custom lwIP based interface, for example if you need to update :component_file:`esp_netif/lwip/netif/wlanif.c` with a specific packet pool. In that case, you would have to define an explicit dependency to lwIP and include :component_file:`esp_netif/include/lwip/esp_netif_net_stack.h` for the relevant lwIP configuration structures.
|
||||
|
||||
|
||||
.. _esp_netif_tcpip_stack:
|
||||
|
||||
ESP-NETIF Custom TCP/IP Stack
|
||||
-----------------------------
|
||||
|
||||
It is possible to use a custom TCP/IP stack with ESP-IDF, provided it implements BSD API. You can add support for your own TCP/IP stack, while using the generic ESP-NETIF functionality, so the application code can stay the same as with the lwIP.
|
||||
|
||||
In this case, please choose ``ESP_NETIF_PROVIDE_CUSTOM_IMPLEMENTATION`` in the ESP-NETIF component configuration menu. This option will disable lwIP implementation of the ESP-NETIF functions and provide only header files with declarations of types and API. You will have to supply the necessary implementation in your custom component. You can refer to the :component_file:`esp_netif/loopback/esp_netif_loopback.c` for example of dummy implementations of these functions.
|
||||
|
||||
It is also possible to build ESP-IDF without lwIP, please refer to :idf_file:`components/esp_netif_stack/README.md`.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
The following API reference outlines these network stack interaction with the ESP-NETIF:
|
||||
|
||||
.. include-build-file:: inc/esp_netif_net_stack.inc
|
||||
@@ -0,0 +1,426 @@
|
||||
ESP-NETIF Programmers Manual
|
||||
============================
|
||||
|
||||
.. _esp_netif_set_ip:
|
||||
|
||||
Configure IP, Gateway, and DNS
|
||||
------------------------------
|
||||
|
||||
Typically, IP addresses -- including the gateway, network mask, and DNS servers -- are automatically obtained through a DHCP server or Router Advertisement services. Notifications regarding IP address assignments are received via the ``IP_EVENT``, which provides the relevant IP address information. You can also retrieve the current address details using the function :cpp:func:`esp_netif_get_ip_info()`. This function returns a structure, :cpp:type:`esp_netif_ip_info_t`, that contains the IPv4 address of the network interface, along with its network mask and gateway address. Similarly, the function :cpp:func:`esp_netif_get_all_ip6()` can be used to obtain all IPv6 addresses associated with the interface.
|
||||
|
||||
In order to configure static IP addresses and DNS servers, it's necessary to disable or stop DHCP client (which is enabled by default on some network interfaces, such as the default Ethernet, or the default WiFi station). Please refer to the example :example:`/protocols/static_ip` for more details.
|
||||
|
||||
To set IPv4 address, you can use :cpp:func:`esp_netif_set_ip_info()`. For IPv6, these two functions can be used for adding or removing addresses: :cpp:func:`esp_netif_add_ip6_address()`, :cpp:func:`esp_netif_remove_ip6_address()`.
|
||||
To configure DNS servers, please use :cpp:func:`esp_netif_set_dns_info()` API.
|
||||
|
||||
Custom Got/Lost IP Events
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For user-defined interfaces, esp-netif lets you select which IP events are posted when an interface obtains or loses an IP address. Two generic event IDs are provided for this purpose:
|
||||
|
||||
- ``IP_EVENT_CUSTOM_GOT_IP``
|
||||
- ``IP_EVENT_CUSTOM_LOST_IP``
|
||||
|
||||
Configure these by setting :cpp:member:`esp_netif_inherent_config_t::get_ip_event` and :cpp:member:`esp_netif_inherent_config_t::lost_ip_event` when creating the interface:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_netif_inherent_config_t base = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA();
|
||||
base.get_ip_event = IP_EVENT_CUSTOM_GOT_IP;
|
||||
base.lost_ip_event = IP_EVENT_CUSTOM_LOST_IP;
|
||||
esp_netif_config_t cfg = { .base = &base, .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA };
|
||||
esp_netif_t *netif = esp_netif_new(&cfg);
|
||||
|
||||
Event data for these events is the same as the standard ones: :cpp:type:`ip_event_got_ip_t` is posted for “got IP”, and :cpp:type:`ip_event_got_ip_t` with :cpp:member:`ip_event_got_ip_t::ip_changed` set accordingly for updates. For lost IP, an empty :cpp:type:`ip_event_got_ip_t` with only :cpp:member:`ip_event_got_ip_t::esp_netif` set is posted.
|
||||
|
||||
.. _esp_netif_set_dhcp:
|
||||
|
||||
Configure DHCP options
|
||||
----------------------
|
||||
|
||||
Some network interfaces are pre-configured to use either a DHCP client (commonly for Ethernet interfaces) or a DHCP server (typically for Wi-Fi software access points). When manually creating a custom network interface, the configuration flags :cpp:type:`esp_netif_flags_t` are used to specify the behavior of the interface. Adding :cpp:enumerator:`ESP_NETIF_DHCP_CLIENT` or :cpp:enumerator:`ESP_NETIF_DHCP_SERVER` will enable the DHCP client or server, respectively.
|
||||
|
||||
It is important to note that these two options are mutually exclusive and cannot be changed at runtime. If an interface is configured as a DHCP client upon creation, it cannot later be used as a DHCP server. The only option is to destroy the existing network interface and create a new one with the desired configuration.
|
||||
|
||||
To set or get a specific DHCP option, the common type :cpp:type:`esp_netif_dhcp_option_id_t` is used for both the DHCP server and client. However, not all options are supported for both. For details on the available options for the DHCP client, refer to the API documentation for :cpp:func:`esp_netif_dhcpc_option()`. Similarly, for the options available for the DHCP server, consult the API documentation for :cpp:func:`esp_netif_dhcps_option()`.
|
||||
|
||||
.. _esp_netif-sntp-api:
|
||||
|
||||
SNTP Service
|
||||
------------
|
||||
|
||||
A brief introduction to SNTP, its initialization code, and basic modes can be found in Section :ref:`system-time-sntp-sync` in :doc:`System Time </api-reference/system/system_time>`.
|
||||
|
||||
This section provides more details on specific use cases for the SNTP service, such as using statically configured servers, DHCP-provided servers, or both. The workflow is typically straightforward:
|
||||
|
||||
1. Initialize and configure the service using :cpp:func:`esp_netif_sntp_init()`. This function can only be called once unless the SNTP service has been destroyed using :cpp:func:`esp_netif_sntp_deinit()`.
|
||||
2. Start the service with :cpp:func:`esp_netif_sntp_start()`. This step is not necessary if the service was auto-started in the previous step (default behavior). However, it can be useful to start the service explicitly after connecting if DHCP-provided NTP servers are being used. Note that this option needs to be enabled before connecting, but the SNTP service should only be started afterward.
|
||||
3. Wait for the system time to synchronize using :cpp:func:`esp_netif_sntp_sync_wait()` (if required).
|
||||
4. Stop and destroy the service using :cpp:func:`esp_netif_sntp_deinit()`.
|
||||
|
||||
Events
|
||||
^^^^^^
|
||||
|
||||
The SNTP wrapper posts an event when the system time is synchronized:
|
||||
|
||||
- Event base: ``NETIF_SNTP_EVENT``
|
||||
- Event ID: ``NETIF_SNTP_TIME_SYNC``
|
||||
- Event data: pointer to :cpp:type:`esp_netif_sntp_time_sync_t` with the synchronized ``timeval``
|
||||
|
||||
Register a handler after creating the default event loop:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static void sntp_evt_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
const esp_netif_sntp_time_sync_t *evt = (const esp_netif_sntp_time_sync_t *)data;
|
||||
if (evt) {
|
||||
ESP_LOGI("sntp", "time synchronized: %ld.%06ld", (long)evt->tv.tv_sec, (long)evt->tv.tv_usec);
|
||||
// Optionally convert to human-readable time using localtime_r() or gmtime_r()
|
||||
}
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_SNTP_EVENT, NETIF_SNTP_TIME_SYNC, &sntp_evt_handler, NULL));
|
||||
|
||||
|
||||
Basic Mode with Statically Defined Server(s)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Initialize the module with the default configuration after connecting to the network. Note that it is possible to provide multiple NTP servers in the configuration struct:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(2,
|
||||
ESP_SNTP_SERVER_LIST("time.windows.com", "pool.ntp.org" ) );
|
||||
esp_netif_sntp_init(&config);
|
||||
|
||||
.. note::
|
||||
|
||||
If you want to configure multiple SNTP servers, update the lwIP configuration option :ref:`CONFIG_LWIP_SNTP_MAX_SERVERS`.
|
||||
|
||||
|
||||
Use DHCP-Obtained SNTP Server(s)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
First, you need to enable the lwIP configuration option :ref:`CONFIG_LWIP_DHCP_GET_NTP_SRV`. Then, initialize the SNTP module with the DHCP option, without specifying an NTP server.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(0, {} );
|
||||
config.start = false; // start the SNTP service explicitly
|
||||
config.server_from_dhcp = true; // accept the NTP offer from the DHCP server
|
||||
esp_netif_sntp_init(&config);
|
||||
|
||||
Once connected, you can start the service using:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_netif_sntp_start();
|
||||
|
||||
.. note::
|
||||
|
||||
It is also possible to start the service during initialization (with the default ``config.start=true``). However, this may cause the initial SNTP request to fail since you are not connected yet, which could result in a back-off period for subsequent requests.
|
||||
|
||||
|
||||
Use Both Static and Dynamic Servers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This scenario is similar to using DHCP-provided SNTP servers. However, in this configuration, you need to ensure that the static server configuration is refreshed when obtaining NTP servers via DHCP. The underlying lwIP code removes the existing list of NTP servers when DHCP-provided information is accepted. Therefore, the ESP-NETIF SNTP module retains the statically configured server(s) and appends them to the list after obtaining a DHCP lease.
|
||||
|
||||
The typical configuration now looks as per below, providing the specific ``IP_EVENT`` to update the config and index of the first server to reconfigure (for example setting ``config.index_of_first_server=1`` would keep the DHCP provided server at index 0, and the statically configured server at index 1).
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org");
|
||||
config.start = false; // start the SNTP service explicitly (after connecting)
|
||||
config.server_from_dhcp = true; // accept the NTP offers from DHCP server
|
||||
config.renew_servers_after_new_IP = true; // let esp-netif update the configured SNTP server(s) after receiving the DHCP lease
|
||||
config.index_of_first_server = 1; // updates from server num 1, leaving server 0 (from DHCP) intact
|
||||
config.ip_event_to_renew = IP_EVENT_STA_GOT_IP; // IP event on which you refresh your configuration
|
||||
|
||||
Then you start the service normally with :cpp:func:`esp_netif_sntp_start()`.
|
||||
|
||||
|
||||
.. _esp_netif_l2tap:
|
||||
|
||||
L2 TAP Interface Usage
|
||||
----------------------
|
||||
|
||||
The ESP-NETIF L2 TAP interface is used to access Data Link Layer, please refer to the :ref:`esp-netif structure` diagram to see how L2 TAP interacts with ESP-NETIF and application.
|
||||
|
||||
From a user perspective, the ESP-NETIF L2 TAP interface is accessed using file descriptors of VFS, which provides file-like interfacing (using functions like ``open()``, ``read()``, ``write()``, etc). To learn more, refer to :doc:`/api-reference/storage/vfs`.
|
||||
|
||||
There is only one ESP-NETIF L2 TAP interface device (path name) available, but you can open multiple file descriptors from it, each with its own unique configuration. Think of the ESP-NETIF L2 TAP interface as a general gateway to the Layer 2 network infrastructure. The key point is that each file descriptor can be individually configured, which is crucial. For example, a file descriptor can be set up to access a specific network interface identified by if_key (like ETH_DEF) and to filter specific types of frames (such as filtering by Ethernet type for IEEE 802.3 frames).
|
||||
|
||||
This filtering is essential because the ESP-NETIF L2 TAP works alongside the IP stack, meaning that IP-related traffic (like IP, ARP, etc.) should not be sent directly to your application. Although this option is still possible, it is not recommended in standard use cases. The benefit of filtering is that it allows your application to receive only the frame types it cares about, while other traffic is either routed to different L2 TAP file descriptors or handled by the IP stack.
|
||||
|
||||
|
||||
Initialization
|
||||
^^^^^^^^^^^^^^
|
||||
To be able to use the ESP-NETIF L2 TAP interface, it needs to be enabled in Kconfig by :ref:`CONFIG_ESP_NETIF_L2_TAP` first and then registered by :cpp:func:`esp_vfs_l2tap_intf_register()` prior usage of any VFS function.
|
||||
|
||||
``open()``
|
||||
^^^^^^^^^^
|
||||
Once the ESP-NETIF L2 TAP is registered, it can be opened at path name "/dev/net/tap". The same path name can be opened multiple times up to :ref:`CONFIG_ESP_NETIF_L2_TAP_MAX_FDS` and multiple file descriptors with a different configuration may access the Data Link Layer frames.
|
||||
|
||||
The ESP-NETIF L2 TAP can be opened with the ``O_NONBLOCK`` file status flag to make sure the ``read()`` does not block. Note that the ``write()`` may block in the current implementation when accessing a Network interface since it is a shared resource among multiple ESP-NETIF L2 TAP file descriptors and IP stack, and there is currently no queuing mechanism deployed. The file status flag can be retrieved and modified using ``fcntl()``.
|
||||
|
||||
On success, ``open()`` returns the new file descriptor (a nonnegative integer). On error, -1 is returned, and ``errno`` is set to indicate the error.
|
||||
|
||||
``ioctl()``
|
||||
^^^^^^^^^^^
|
||||
The newly opened ESP-NETIF L2 TAP file descriptor needs to be configured prior to its usage since it is not bounded to any specific Network Interface and no frame type filter is configured. The following configuration options are available to do so:
|
||||
|
||||
* ``L2TAP_S_INTF_DEVICE`` - bounds the file descriptor to a specific Network Interface that is identified by its ``if_key``. ESP-NETIF Network Interface ``if_key`` is passed to ``ioctl()`` as the third parameter. Note that default Network Interfaces ``if_key``'s used in ESP-IDF can be found in :component_file:`esp_netif/include/esp_netif_defaults.h`.
|
||||
* ``L2TAP_S_DEVICE_DRV_HNDL`` - is another way to bound the file descriptor to a specific Network Interface. In this case, the Network interface is identified directly by IO Driver handle (e.g., :cpp:type:`esp_eth_handle_t` in case of Ethernet). The IO Driver handle is passed to ``ioctl()`` as the third parameter.
|
||||
* ``L2TAP_S_RCV_FILTER`` - sets the filter to frames with the type to be passed to the file descriptor. In the case of Ethernet frames, the frames are to be filtered based on the Length and Ethernet type field. In case the filter value is set less than or equal to 0x05DC, the Ethernet type field is considered to represent IEEE802.3 Length Field, and all frames with values in interval <0, 0x05DC> at that field are passed to the file descriptor. The IEEE802.2 logical link control (LLC) resolution is then expected to be performed by the user's application. In case the filter value is set greater than 0x05DC, the Ethernet type field is considered to represent protocol identification and only frames that are equal to the set value are to be passed to the file descriptor.
|
||||
* ``L2TAP_S_TIMESTAMP_EN`` - enables the hardware Time Stamping processing inside the file descriptor. The Time Stamps are retrieved to user space by using :ref:`Extended Buffer <esp_netif_l2tap_ext_buff>` mechanism when accessing the file descriptor by ``read()`` and ``write()`` functions. Hardware time stamping needs to be supported by target and needs to be enabled in IO Driver to this option work as expected.
|
||||
|
||||
All above-set configuration options have a getter counterpart option to read the current settings except for ``L2TAP_S_TIMESTAMP_EN``.
|
||||
|
||||
.. warning::
|
||||
The file descriptor needs to be firstly bounded to a specific Network Interface by ``L2TAP_S_INTF_DEVICE`` or ``L2TAP_S_DEVICE_DRV_HNDL`` to make ``L2TAP_S_RCV_FILTER`` option available.
|
||||
|
||||
.. note::
|
||||
VLAN-tagged frames are currently not recognized. If the user needs to process VLAN-tagged frames, they need a set filter to be equal to the VLAN tag (i.e., 0x8100 or 0x88A8) and process the VLAN-tagged frames in the user application.
|
||||
|
||||
.. note::
|
||||
``L2TAP_S_DEVICE_DRV_HNDL`` is particularly useful when the user's application does not require the usage of an IP stack and so ESP-NETIF is not required to be initialized too. As a result, Network Interface cannot be identified by its ``if_key`` and hence it needs to be identified directly by its IO Driver handle.
|
||||
|
||||
| On success, ``ioctl()`` returns 0. On error, -1 is returned, and ``errno`` is set to indicate the error.
|
||||
| * EBADF - not a valid file descriptor.
|
||||
| * EACCES - options change is denied in this state (e.g., file descriptor has not been bounded to Network interface yet).
|
||||
| * EINVAL - invalid configuration argument. Ethernet type filter is already used by other file descriptors on that same Network interface.
|
||||
| * ENODEV - no such Network Interface which is tried to be assigned to the file descriptor exists.
|
||||
| * ENOSYS - unsupported operation, passed configuration option does not exist.
|
||||
|
||||
``fcntl()``
|
||||
^^^^^^^^^^^
|
||||
The ``fcntl()`` is used to manipulate with properties of opened ESP-NETIF L2 TAP file descriptor.
|
||||
|
||||
The following commands manipulate the status flags associated with the file descriptor:
|
||||
|
||||
* ``F_GETFD`` - the function returns the file descriptor flags, and the third argument is ignored.
|
||||
* ``F_SETFD`` - sets the file descriptor flags to the value specified by the third argument. Zero is returned.
|
||||
|
||||
| On success, ``fcntl()`` returns 0. On error, -1 is returned, and ``errno`` is set to indicate the error.
|
||||
| * EBADF - not a valid file descriptor.
|
||||
| * ENOSYS - unsupported command.
|
||||
|
||||
``read()``
|
||||
^^^^^^^^^^
|
||||
Opened and configured ESP-NETIF L2 TAP file descriptor can be accessed by ``read()`` to get inbound frames. The read operation can be either blocking or non-blocking based on the actual state of the ``O_NONBLOCK`` file status flag. When the file status flag is set to blocking, the read operation waits until a frame is received and the context is switched to other tasks. When the file status flag is set to non-blocking, the read operation returns immediately. In such case, either a frame is returned if it was already queued or the function indicates the queue is empty. The number of queued frames associated with one file descriptor is limited by :ref:`CONFIG_ESP_NETIF_L2_TAP_RX_QUEUE_SIZE` Kconfig option. Once the number of queued frames reached a configured threshold, the newly arrived frames are dropped until the queue has enough room to accept incoming traffic (Tail Drop queue management).
|
||||
|
||||
| On success, ``read()`` returns the number of bytes read. Zero is returned when the size of the destination buffer is 0. On error, -1 is returned, and ``errno`` is set to indicate the error.
|
||||
| * EBADF - not a valid file descriptor.
|
||||
| * EAGAIN - the file descriptor has been marked non-blocking (``O_NONBLOCK``), and the read would block.
|
||||
|
||||
.. note::
|
||||
ESP-NETIF L2 TAP ``read()`` implementation extends the standard and offers Extended Buffer mechanism to retrieve additional information about received frame. See :ref:`Extended Buffer <esp_netif_l2tap_ext_buff>` section for more information.
|
||||
|
||||
``write()``
|
||||
^^^^^^^^^^^
|
||||
A raw Data Link Layer frame can be sent to Network Interface via opened and configured ESP-NETIF L2 TAP file descriptor. The user's application is responsible to construct the whole frame except for fields which are added automatically by the physical interface device. The following fields need to be constructed by the user's application in case of an Ethernet link: source/destination MAC addresses, Ethernet type, actual protocol header, and user data. The length of these fields is as follows:
|
||||
|
||||
.. packetdiag::
|
||||
|
||||
packetdiag {
|
||||
colwidth = 16;
|
||||
node_width = 38;
|
||||
0-5: Destination MAC (6B) [color = "#ffcccc"];
|
||||
6-11: Source MAC Port (6B) [color = "#ffcccc"];
|
||||
12-13: Type/Length (2B) [color = "#ccccff"];
|
||||
14-15: [color = "#ffffcc"];
|
||||
16-31: Payload (protocol header/data - 1486B) [color = "#ffffcc", colheight = 3];
|
||||
}
|
||||
|
||||
In other words, there is no additional frame processing performed by the ESP-NETIF L2 TAP interface. It only checks the Ethernet type of the frame is the same as the filter configured in the file descriptor. If the Ethernet type is different, an error is returned and the frame is not sent. Note that the ``write()`` may block in the current implementation when accessing a Network interface since it is a shared resource among multiple ESP-NETIF L2 TAP file descriptors and IP stack, and there is currently no queuing mechanism deployed.
|
||||
|
||||
| On success, ``write()`` returns the number of bytes written. Zero is returned when the size of the input buffer is 0. On error, -1 is returned, and ``errno`` is set to indicate the error.
|
||||
| * EBADF - not a valid file descriptor.
|
||||
| * EBADMSG - The Ethernet type of the frame is different from the file descriptor configured filter.
|
||||
| * EIO - Network interface not available or busy.
|
||||
|
||||
.. note::
|
||||
ESP-NETIF L2 TAP ``write()`` implementation extends the standard and offers Extended Buffer mechanism to retrieve additional information about transmitted frame. See :ref:`Extended Buffer <esp_netif_l2tap_ext_buff>` section for more information.
|
||||
|
||||
``close()``
|
||||
^^^^^^^^^^^
|
||||
Opened ESP-NETIF L2 TAP file descriptor can be closed by the ``close()`` to free its allocated resources. The ESP-NETIF L2 TAP implementation of ``close()`` may block. On the other hand, it is thread-safe and can be called from a different task than the file descriptor is actually used. If such a situation occurs and one task is blocked in the I/O operation and another task tries to close the file descriptor, the first task is unblocked. The first's task ``read`` operation then ends with returning `0` bytes was read.
|
||||
|
||||
| On success, ``close()`` returns zero. On error, -1 is returned, and ``errno`` is set to indicate the error.
|
||||
| * EBADF - not a valid file descriptor.
|
||||
|
||||
``select()``
|
||||
^^^^^^^^^^^^
|
||||
Select is used in a standard way, just :ref:`CONFIG_VFS_SUPPORT_SELECT` needs to be enabled to make the ``select()`` function available.
|
||||
|
||||
.. _esp_netif_l2tap_ext_buff:
|
||||
|
||||
Extended Buffer
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The Extended Buffer is ESP-NETIF L2 TAP's mechanism of how to retrieve additional information about transmitted or received IO frame via ``write()`` or ``read()`` functions. The Extended Buffer must be only used when specific functionality is enabled in the file descriptor (such as ``L2TAP_S_TIMESTAMP_EN``) and you want to access the additional data (such as Time Stamp) or control the frame processing.
|
||||
|
||||
The **Extended Buffer** is a structure with fields which serve as arguments to drive underlying functionality in the ESP-NETIF L2 TAP file descriptor. The structure is defined as follows:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct {
|
||||
size_t info_recs_len; /*!< Length of Information Records buffer */
|
||||
void *info_recs_buff; /*!< Buffer holding extended information (IRECs) related to IO frames */
|
||||
size_t buff_len; /*!< Length of the actual IO Frame buffer */
|
||||
void *buff; /*!< Pointer to the IO Frame buffer */
|
||||
} l2tap_extended_buff_t;
|
||||
|
||||
One Extended buffer may hold multiple **Information Records** (IRECs). These are variable data typed (and sized) records which may hold any datatype of additional information associated with the IO frame. The IREC structure is defined as follows:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct
|
||||
{
|
||||
size_t len; /*!< Length of the record including header and data*/
|
||||
l2tap_irec_type_t type; /*!< Type of the record */
|
||||
alignas(long long) uint8_t data[]; /*!< Records Data aligned to double word */
|
||||
} l2tap_irec_hdr_t;
|
||||
|
||||
Currently implement and used IREC data types are defined in :cpp:type:`l2tap_irec_type_t`.
|
||||
|
||||
Since the flexible array to hold data is used, proper memory alignment of multiple IRECs in the records buffer is required to correctly access memory. Improper alignment can result in slower memory access due to misaligned read/write operations, or in the worst case, cause undefined behavior on certain architectures. Therefore it is strictly recommended to use the below macros when manipulating with IRECs:
|
||||
|
||||
* ``L2TAP_IREC_SPACE()`` - determines the space required for an IREC, ensuring that it is properly aligned.
|
||||
* ``L2TAP_IREC_LEN()`` - calculates the total length of one IREC, including the header and the data section of the record.
|
||||
* ``L2TAP_IREC_FIRST()`` - retrieves the first IREC from the :cpp:member:`l2tap_extended_buff_t::info_recs_buff` pool of Extended Buffer. If the :cpp:member:`l2tap_extended_buff_t::info_recs_len` is smaller than the size of a record header, it returns NULL.
|
||||
* ``L2TAP_IREC_NEXT()`` - retrieves the next IREC in the Extended Buffer after the current record. If the current record is NULL, it returns the first record.
|
||||
|
||||
Extended Buffer Usage
|
||||
"""""""""""""""""""""
|
||||
|
||||
Prior any Extended Buffer IO operation (either ``write()`` or ``read()``), you first need to fully populate the Extended Buffer and its IREC fields. For example, when you want to retrieve Time Stamp, you need to set type of the IREC to :cpp:enumerator:`L2TAP_IREC_TIME_STAMP` and configure appropriate length. If you don't set the type correctly, the frame is still received or transmitted but information to be retrieved is lost. Similarly, when the IREC length is less than expected length, the frame is still received or transmitted but the type of affected IREC is marked to :cpp:enumerator:`L2TAP_IREC_INVALID` by the ESP-NETIF L2 TAP and information to be retrieved is lost.
|
||||
|
||||
When accessing the file descriptor using Extended Buffer, ``size`` parameter of ``write()`` or ``read()`` function must be set equal to ``0``. Failing to do so (i.e. accessing such file descriptor in a standard way with ``size`` parameter set to data length) will result in an -1 error and ``errno`` set to EINVAL.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// wrap "Info Records Buffer" into union to ensure proper alignment of data (this is typically needed when
|
||||
// accessing double word variables or structs containing double word variables)
|
||||
union {
|
||||
uint8_t info_recs_buff[L2TAP_IREC_SPACE(sizeof(struct timespec))];
|
||||
l2tap_irec_hdr_t align;
|
||||
} u;
|
||||
|
||||
l2tap_extended_buff_t ptp_msg_ext_buff;
|
||||
|
||||
ptp_msg_ext_buff.info_recs_len = sizeof(u.info_recs_buff);
|
||||
ptp_msg_ext_buff.info_recs_buff = u.info_recs_buff;
|
||||
ptp_msg_ext_buff.buff = eth_frame;
|
||||
ptp_msg_ext_buff.buff_len = sizeof(eth_frame);
|
||||
|
||||
l2tap_irec_hdr_t *ts_info = L2TAP_IREC_FIRST(&ptp_msg_ext_buff);
|
||||
ts_info->len = L2TAP_IREC_LEN(sizeof(struct timespec));
|
||||
ts_info->type = L2TAP_IREC_TIME_STAMP;
|
||||
|
||||
int ret = write(state->ptp_socket, &ptp_msg_ext_buff, 0);
|
||||
|
||||
// check if write was successful and ts_info is valid
|
||||
if (ret > 0 && ts_info->type == L2TAP_IREC_TIME_STAMP) {
|
||||
*ts = *(struct timespec *)ts_info->data;
|
||||
}
|
||||
|
||||
.. _esp_netif_other_events:
|
||||
|
||||
IP Event: Transmit/Receive Packet
|
||||
---------------------------------
|
||||
|
||||
This event, ``IP_EVENT_TX_RX``, is triggered for every transmitted or received IP packet. It provides information about packet transmission or reception, data length, and the ``esp_netif`` handle.
|
||||
|
||||
Enabling the Event
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
**Compile Time:**
|
||||
|
||||
The feature can be completely disabled during compilation time using the flag :ref:`CONFIG_ESP_NETIF_REPORT_DATA_TRAFFIC` in the kconfig.
|
||||
|
||||
**Run Time:**
|
||||
|
||||
At runtime, you can enable or disable this event using the functions :cpp:func:`esp_netif_tx_rx_event_enable()` and :cpp:func:`esp_netif_tx_rx_event_disable()`.
|
||||
|
||||
Event Registration
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To handle this event, you need to register a handler using the following syntax:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static void
|
||||
tx_rx_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
ip_event_tx_rx_t *event = (ip_event_tx_rx_t *)event_data;
|
||||
|
||||
if (event->dir == ESP_NETIF_TX) {
|
||||
ESP_LOGI(TAG, "Got TX event: Interface \"%s\" data len: %d", esp_netif_get_desc(event->esp_netif), event->len);
|
||||
} else if (event->dir == ESP_NETIF_RX) {
|
||||
ESP_LOGI(TAG, "Got RX event: Interface \"%s\" data len: %d", esp_netif_get_desc(event->esp_netif), event->len);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Got Unknown event: Interface \"%s\"", esp_netif_get_desc(event->esp_netif));
|
||||
}
|
||||
}
|
||||
|
||||
esp_event_handler_register(IP_EVENT, IP_EVENT_TX_RX, &tx_rx_event_handler, NULL);
|
||||
|
||||
Here, ``tx_rx_event_handler`` is the name of the function that will handle the event.
|
||||
|
||||
Event Data Structure
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The event data structure, :cpp:class:`ip_event_tx_rx_t`, contains the following fields:
|
||||
|
||||
- :cpp:member:`ip_event_tx_rx_t::dir`: Indicates whether the packet was transmitted (``ESP_NETIF_TX``) or received (``ESP_NETIF_RX``).
|
||||
- :cpp:member:`ip_event_tx_rx_t::len`: Length of the data frame.
|
||||
- :cpp:member:`ip_event_tx_rx_t::esp_netif`: The network interface on which the packet was sent or received.
|
||||
|
||||
IP Events: Netif Status (Unified)
|
||||
---------------------------------
|
||||
|
||||
ESP-NETIF emits unified status events when an interface becomes usable for L3 traffic or goes down. These are derived from the lwIP extended netif callbacks and posted on ``IP_EVENT``:
|
||||
|
||||
- ``IP_EVENT_NETIF_UP`` / ``IP_EVENT_NETIF_DOWN``
|
||||
|
||||
ESP-IDF normalizes link and administrative state changes into these two events (including PPP). Subscribe as follows:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static void netif_status_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
const ip_event_netif_status_t *evt = (const ip_event_netif_status_t *)data;
|
||||
ESP_LOGI("netif", "status %s on %s", (id == IP_EVENT_NETIF_UP) ? "UP" : "DOWN", esp_netif_get_desc(evt->esp_netif));
|
||||
}
|
||||
|
||||
esp_event_handler_register(IP_EVENT, IP_EVENT_NETIF_UP, &netif_status_handler, NULL);
|
||||
esp_event_handler_register(IP_EVENT, IP_EVENT_NETIF_DOWN, &netif_status_handler, NULL);
|
||||
|
||||
Event Data Structure
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The event data structure is :cpp:type:`ip_event_netif_status_t`, which contains the ``esp_netif`` handle of the interface that changed state.
|
||||
|
||||
|
||||
.. _esp_netif_api_reference:
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_netif.inc
|
||||
.. include-build-file:: inc/esp_netif_sntp.inc
|
||||
.. include-build-file:: inc/esp_netif_types.inc
|
||||
.. include-build-file:: inc/esp_netif_ip_addr.inc
|
||||
.. include-build-file:: inc/esp_vfs_l2tap.inc
|
||||
|
||||
.. only:: SOC_WIFI_SUPPORTED
|
||||
|
||||
Wi-Fi Default API Reference
|
||||
---------------------------
|
||||
|
||||
.. include-build-file:: inc/esp_wifi_default.inc
|
||||
@@ -0,0 +1,154 @@
|
||||
ESP-NOW
|
||||
=======
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
ESP-NOW is a kind of connectionless Wi-Fi communication protocol that is defined by Espressif. In ESP-NOW, application data is encapsulated in a vendor-specific action frame and then transmitted from one Wi-Fi device to another without connection.
|
||||
|
||||
CTR with CBC-MAC Protocol (CCMP) is used to protect the action frame for security. ESP-NOW is widely used in smart light, remote controlling, sensor, etc.
|
||||
|
||||
Frame Format
|
||||
------------
|
||||
|
||||
ESP-NOW uses a vendor-specific action frame to transmit ESP-NOW data. The default ESP-NOW bit rate is 1 Mbps.
|
||||
|
||||
Currently, ESP-NOW supports two versions: v1.0 and v2.0. The maximum packet length supported by v2.0 devices is 1470 (``ESP_NOW_MAX_DATA_LEN_V2``) bytes, while the maximum packet length supported by v1.0 devices is 250 (``ESP_NOW_MAX_DATA_LEN``) bytes.
|
||||
|
||||
The v2.0 devices are capable of receiving packets from both v2.0 and v1.0 devices. In contrast, v1.0 devices can only receive packets from other v1.0 devices.
|
||||
|
||||
However, v1.0 devices can receive v2.0 packets if the packet length is less than or equal to 250 (``ESP_NOW_MAX_IE_DATA_LEN``).For packets exceeding this length, the v1.0 devices will either truncate the data to the first 250 (``ESP_NOW_MAX_IE_DATA_LEN``) bytes or discard the packet entirely.
|
||||
|
||||
For detailed behavior, please refer to the documentation corresponding to the specific IDF version.
|
||||
|
||||
The format of the vendor-specific action frame is as follows:
|
||||
|
||||
.. highlight:: none
|
||||
|
||||
::
|
||||
|
||||
------------------------------------------------------------------------------------------------------------
|
||||
| MAC Header | Category Code | Organization Identifier | Random Values | Vendor Specific Content | FCS |
|
||||
------------------------------------------------------------------------------------------------------------
|
||||
24 bytes 1 byte 3 bytes 4 bytes 7-x bytes 4 bytes
|
||||
|
||||
- Category Code: The Category Code field is set to the value (127) indicating the vendor-specific category.
|
||||
- Organization Identifier: The Organization Identifier contains a unique identifier (0x18fe34), which is the first three bytes of MAC address applied by Espressif.
|
||||
- Random Value: The Random Value filed is used to prevents relay attacks.
|
||||
- Vendor Specific Content: The Vendor Specific Content contains several (at least one) vendor-specific element fields. For version v2.0, x = 1512(1470 + 6*7), for version v1.0, x = 257(250 + 7).
|
||||
|
||||
The format of the vendor-specific element frame is as follows:
|
||||
|
||||
.. highlight:: none
|
||||
|
||||
::
|
||||
|
||||
ESP-NOW v1.0:
|
||||
------------------------------------------------------------------------------------------
|
||||
| Element ID | Length | Organization Identifier | Type | Reserved | Version | Body |
|
||||
------------------------------------------------------------------------------------------
|
||||
7~4 bits | 3~0 bits
|
||||
1 byte 1 byte 3 bytes 1 byte 1 byte 0-250 bytes
|
||||
|
||||
ESP-NOW v2.0:
|
||||
-----------------------------------------------------------------------------------------------------
|
||||
| Element ID | Length | Organization Identifier | Type | Reserved | More data |Version | Body |
|
||||
-----------------------------------------------------------------------------------------------------
|
||||
7~5 bits | 1 bit |3~0 bits
|
||||
1 byte 1 byte 3 bytes 1 byte 1 byte 0-250 bytes
|
||||
|
||||
- Element ID: The Element ID field is set to the value (221), indicating the vendor-specific element.
|
||||
- Length: The length is the total length of Organization Identifier, Type, Version and Body, the maximum value is 255.
|
||||
- Organization Identifier: The Organization Identifier contains a unique identifier (0x18fe34), which is the first three bytes of MAC address applied by Espressif.
|
||||
- Type: The Type field is set to the value (4) indicating ESP-NOW.
|
||||
- Version: The Version field is set to the version of ESP-NOW.
|
||||
- Body: The Body contains the actual ESP-NOW data to be transmitted.
|
||||
|
||||
As ESP-NOW is connectionless, the MAC header is a little different from that of standard frames. The FromDS and ToDS bits of FrameControl field are both 0. The first address field is set to the destination address. The second address field is set to the source address. The third address field is set to broadcast address (0xff:0xff:0xff:0xff:0xff:0xff).
|
||||
|
||||
Security
|
||||
--------
|
||||
|
||||
ESP-NOW uses the CCMP method, which is described in IEEE Std. 802.11-2012, to protect the vendor-specific action frame. The Wi-Fi device maintains a Primary Master Key (PMK) and several Local Master Keys (LMKs, each paired device has one LMK). The lengths of both PMK and LMK are 16 bytes.
|
||||
|
||||
* PMK is used to encrypt LMK with the AES-128 algorithm. Call :cpp:func:`esp_now_set_pmk()` to set PMK. If PMK is not set, a default PMK will be used.
|
||||
* LMK of the paired device is used to encrypt the vendor-specific action frame with the CCMP method. If the LMK of the paired device is not set, the vendor-specific action frame will not be encrypted.
|
||||
|
||||
Encrypting multicast vendor-specific action frame is not supported.
|
||||
|
||||
Initialization and Deinitialization
|
||||
------------------------------------
|
||||
|
||||
Call :cpp:func:`esp_now_init()` to initialize ESP-NOW and :cpp:func:`esp_now_deinit()` to de-initialize ESP-NOW. ESP-NOW data must be transmitted after Wi-Fi is started, so it is recommended to start Wi-Fi before initializing ESP-NOW and stop Wi-Fi after de-initializing ESP-NOW.
|
||||
|
||||
When :cpp:func:`esp_now_deinit()` is called, all of the information of paired devices are deleted.
|
||||
|
||||
Add Paired Device
|
||||
-----------------
|
||||
|
||||
Call :cpp:func:`esp_now_add_peer()` to add the device to the paired device list before you send data to this device. If security is enabled, the LMK must be set. A device with a broadcast MAC address must be added before sending broadcast data.
|
||||
|
||||
You can send ESP-NOW data via both the Station and the SoftAP interface. Make sure that the interface is enabled before sending ESP-NOW data.
|
||||
|
||||
.. only:: esp32 or esp32c2 or esp32s2 or esp32s3 or esp32c3 or esp32c6
|
||||
|
||||
The range of the channel of paired devices is from 0 to 14. If the channel is set to 0, data will be sent on the current channel. Otherwise, the channel must be set as the channel that the local device is on.
|
||||
|
||||
.. only:: esp32c5
|
||||
|
||||
The channel range for paired devices in the 2.4 GHz band is from 1 to 14. The channel range for paired devices in the 5 GHz band includes the following channels: [36, 40, 44, 48, 52, 56, 60, 64, 100, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173, 177].
|
||||
|
||||
If the channel is set to 0, data will be transmitted on the current channel. Otherwise, the channel must correspond to the local device's current channel.
|
||||
|
||||
For the receiving device, calling :cpp:func:`esp_now_add_peer()` is not required. If no paired device is added, it can only receive broadcast packets and unencrypted unicast packets. To receive encrypted unicast packets, a paired device must be added, and the same LMK must be set.
|
||||
|
||||
.. only:: esp32c2
|
||||
|
||||
The maximum number of paired devices is 20, and the paired encryption devices are no more than 4, the default is 2. If you want to change the number of paired encryption devices, set :ref:`CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM` in the Wi-Fi component configuration menu.
|
||||
|
||||
.. only:: esp32 or esp32s2 or esp32s3 or esp32c3 or esp32c6 or esp32c5
|
||||
|
||||
The maximum number of paired devices is 20, and the paired encryption devices are no more than 17, the default is 7. If you want to change the number of paired encryption devices, set :ref:`CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM` in the Wi-Fi component configuration menu.
|
||||
|
||||
Send ESP-NOW Data
|
||||
-----------------
|
||||
|
||||
Call :cpp:func:`esp_now_send()` to send ESP-NOW data and :cpp:func:`esp_now_register_send_cb()` to register sending callback function. It will return `ESP_NOW_SEND_SUCCESS` in sending callback function if the data is received successfully on the MAC layer. Otherwise, it will return `ESP_NOW_SEND_FAIL`. Several reasons can lead to ESP-NOW fails to send data. For example, the destination device does not exist; the channels of the devices are not the same; the action frame is lost when transmitting on the air, etc. It is not guaranteed that application layer can receive the data. If necessary, send back ack data when receiving ESP-NOW data. If receiving ack data timeouts, retransmit the ESP-NOW data. A sequence number can also be assigned to ESP-NOW data to drop the duplicate data.
|
||||
|
||||
If there is a lot of ESP-NOW data to send, call :cpp:func:`esp_now_send()` to send less than or equal to the maximum packet length (v1.0 is 250 bytes, v2.0 is 1470 bytes) of data once a time. Note that too short interval between sending two ESP-NOW data may lead to disorder of sending callback function. So, it is recommended that sending the next ESP-NOW data after the sending callback function of the previous sending has returned. The sending callback function runs from a high-priority Wi-Fi task. So, do not do lengthy operations in the callback function. Instead, post the necessary data to a queue and handle it from a lower priority task.
|
||||
|
||||
Receiving ESP-NOW Data
|
||||
----------------------
|
||||
|
||||
Call :cpp:func:`esp_now_register_recv_cb()` to register receiving callback function. Call the receiving callback function when receiving ESP-NOW. The receiving callback function also runs from the Wi-Fi task. So, do not do lengthy operations in the callback function.
|
||||
Instead, post the necessary data to a queue and handle it from a lower priority task.
|
||||
|
||||
Config ESP-NOW Rate
|
||||
-------------------
|
||||
|
||||
Call :cpp:func:`esp_now_set_peer_rate_config()` to configure ESP-NOW rate of each peer. Make sure that the peer is added before configuring the rate. This API should be called after :cpp:func:`esp_wifi_start()` and :cpp:func:`esp_now_add_peer()`.
|
||||
|
||||
Config ESP-NOW Power-saving Parameter
|
||||
--------------------------------------------
|
||||
|
||||
Sleep is supported only when {IDF_TARGET_NAME} is configured as station.
|
||||
|
||||
Call :cpp:func:`esp_now_set_wake_window()` to configure Window for ESP-NOW RX at sleep. The default value is the maximum, which allowing RX all the time.
|
||||
|
||||
If Power-saving is needed for ESP-NOW, call :cpp:func:`esp_wifi_connectionless_module_set_wake_interval()` to configure Interval as well.
|
||||
|
||||
.. only:: SOC_WIFI_SUPPORTED
|
||||
|
||||
Please refer to :ref:`connectionless module power save <connectionless-module-power-save>` to get more detail.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`wifi/espnow` demonstrates how to use the ESPNOW feature of {IDF_TARGET_NAME}'s Wi-Fi, including starting Wi-Fi, initializing ESP-NOW, registering ESP-NOW sending or receiving callback function, adding ESP-NOW peer information, and sending and receiving ESP-NOW data between two devices.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_now.inc
|
||||
@@ -0,0 +1,37 @@
|
||||
Thread
|
||||
==========
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
`Thread <https://www.threadgroup.org>`_ is an IP-based mesh networking protocol. It is based on the 802.15.4 physical and MAC layer.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`openthread/ot_br` demonstrates how to set up a Thread border router on {IDF_TARGET_NAME}, enabling functionalities such as bidirectional IPv6 connectivity, service discovery, etc.
|
||||
|
||||
- :example:`openthread/ot_cli` demonstrates how to use the OpenThread Command Line Interface with additional features such as TCP, UDP, and Iperf. This requires a board equipped with an IEEE 802.15.4 module. This example provides instructions on how to set up a network using at least two 802.15.4 boards.
|
||||
|
||||
- :example:`openthread/ot_rcp` demonstrates how to work with a Host Processor to perform as a Thread border router and function as a Thread sniffer, using a board with an IEEE 802.15.4 module.
|
||||
|
||||
- :example:`openthread/ot_trel` demonstrates Thread Radio Encapsulation Link (TREL) function. This requires a board equipped with a Wi-Fi module.
|
||||
|
||||
- :example:`openthread/ot_sleepy_device/deep_sleep` demonstrates Thread Deep-sleep function.
|
||||
|
||||
- :example:`openthread/ot_sleepy_device/light_sleep` demonstrates Thread Light-sleep function.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
For manipulating the Thread network, the OpenThread API shall be used. The OpenThread API docs can be found at the `OpenThread API docs <https://openthread.io/reference>`_.
|
||||
|
||||
ESP-IDF provides extra APIs for launching and managing the OpenThread stack, binding to network interfaces and border routing features.
|
||||
|
||||
.. include-build-file:: inc/esp_openthread.inc
|
||||
.. include-build-file:: inc/esp_openthread_types.inc
|
||||
.. include-build-file:: inc/esp_openthread_lock.inc
|
||||
.. include-build-file:: inc/esp_openthread_netif_glue.inc
|
||||
.. include-build-file:: inc/esp_openthread_border_router.inc
|
||||
@@ -0,0 +1,29 @@
|
||||
SmartConfig
|
||||
===========
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The SmartConfig\ :sup:`TM` is a provisioning technology developed by TI to connect a new Wi-Fi device to a Wi-Fi network. It uses a mobile application to broadcast the network credentials from a smartphone, or a tablet, to an un-provisioned Wi-Fi device.
|
||||
|
||||
The advantage of this technology is that the device does not need to directly know SSID or password of an Access Point (AP). This information is provided using the smartphone. This is particularly important to headless device and systems, due to their lack of a user interface.
|
||||
|
||||
Currently, {IDF_TARGET_NAME} support three types of SmartConfig: Airkiss, ESPTouch, and ESPTouch v2. ESPTouch v2 has been supported since SmartConfig v3.0 (the version of SmartConfig can be get from :cpp:func:`esp_smartconfig_get_version()`), and it employs a completely different algorithm compared to ESPTouch, resulting in faster setup times. Additionally, ESPTouch v2 introduces AES encryption and custom data fields.
|
||||
|
||||
Starting from SmartConfig v3.0.2, ESPTouch v2 introduces support for random IV in AES encryption. On the application side, when the option for random IV is disabled, the default IV is set to 0, maintaining consistency with previous versions. When the random IV option is enabled, the IV will be a random value. It is important to note that when AES encryption is enabled with a random IV, the provision time will be extended due to the need of transmitting the IV to the provisioning device. On the provisioning device side, the device will identify whether the random IV for AES is enabled based on the flag in the provisioning packet.
|
||||
|
||||
If you are looking for other options to provision your {IDF_TARGET_NAME} devices, check :doc:`../provisioning/index`.
|
||||
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
Connect {IDF_TARGET_NAME} to the target AP using SmartConfig: :example:`wifi/smart_config`.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_smartconfig.inc
|
||||
@@ -0,0 +1,36 @@
|
||||
Wi-Fi
|
||||
=====
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The Wi-Fi libraries provide support for configuring and monitoring the {IDF_TARGET_NAME} Wi-Fi networking functionality. This includes configuration for:
|
||||
|
||||
- Station mode (aka STA mode or Wi-Fi client mode). {IDF_TARGET_NAME} connects to an access point.
|
||||
- AP mode (aka Soft-AP mode or Access Point mode). Stations connect to the {IDF_TARGET_NAME}.
|
||||
- Station/AP-coexistence mode ({IDF_TARGET_NAME} is concurrently an access point and a station connected to another access point).
|
||||
|
||||
- Various security modes for the above (WPA, WPA2, WPA3, etc.)
|
||||
- Scanning for access points (active & passive scanning).
|
||||
- Promiscuous mode for monitoring of IEEE802.11 Wi-Fi packets.
|
||||
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
Several application examples demonstrating the functionality of Wi-Fi library are provided in :example:`wifi` directory of ESP-IDF repository. Please check the :example_file:`README <wifi/README.md>` for more details.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_wifi.inc
|
||||
.. include-build-file:: inc/esp_wifi_types.inc
|
||||
.. include-build-file:: inc/esp_wifi_types_generic.inc
|
||||
.. include-build-file:: inc/esp_eap_client.inc
|
||||
.. include-build-file:: inc/esp_wps.inc
|
||||
.. include-build-file:: inc/esp_rrm.inc
|
||||
.. include-build-file:: inc/esp_wnm.inc
|
||||
.. include-build-file:: inc/esp_mbo.inc
|
||||
@@ -0,0 +1,73 @@
|
||||
Networking APIs
|
||||
***************
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
.. only:: SOC_WIFI_SUPPORTED
|
||||
|
||||
Wi-Fi
|
||||
=====
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
esp_now
|
||||
:SOC_WIFI_MESH_SUPPORT: esp-wifi-mesh
|
||||
esp_smartconfig
|
||||
esp_wifi
|
||||
esp_dpp
|
||||
:SOC_WIFI_NAN_SUPPORT: esp_nan
|
||||
|
||||
Code examples for the Wi-Fi API are provided in the :example:`wifi` directory of ESP-IDF examples.
|
||||
|
||||
.. only:: SOC_WIFI_MESH_SUPPORT
|
||||
|
||||
Code examples for ESP-WIFI-MESH are provided in the :example:`mesh` directory of ESP-IDF examples.
|
||||
|
||||
|
||||
|
||||
Ethernet
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
esp_eth
|
||||
|
||||
Code examples for the Ethernet API are provided in the :example:`ethernet` directory of ESP-IDF examples.
|
||||
|
||||
Thread
|
||||
==========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
esp_openthread
|
||||
|
||||
Thread is an IPv6-based mesh networking technology for IoT.
|
||||
|
||||
Code examples for the Thread API are provided in the :example:`openthread` directory of ESP-IDF examples.
|
||||
|
||||
ESP-NETIF
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
esp_netif
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
esp_netif_programming
|
||||
esp_netif_driver
|
||||
|
||||
IP Network Layer
|
||||
================
|
||||
|
||||
Code examples for TCP/IP socket APIs are provided in the :example:`protocols/sockets` directory of ESP-IDF examples.
|
||||
|
||||
Application Layer
|
||||
=================
|
||||
|
||||
Documentation for Application layer network protocols (above the IP Network layer) are provided in :doc:`../protocols/index`.
|
||||
@@ -0,0 +1,214 @@
|
||||
Analog to Digital Converter (ADC) Calibration Driver
|
||||
====================================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
In {IDF_TARGET_NAME}, the analog-to-digital converter (ADC) compares the input analog voltage to the reference, and determines each bit of the output digital result. By design, the ADC reference voltage for {IDF_TARGET_NAME} is 1100 mV. However, the true reference voltage can range from 1000 mV to 1200 mV among different chips. This guide introduces the ADC calibration driver to minimize the effect of different reference voltages, and get more accurate output results.
|
||||
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
The following sections of this document cover the typical steps to install and use the ADC calibration driver:
|
||||
|
||||
.. list::
|
||||
|
||||
- :ref:`adc-calibration-scheme-creation` - covers how to create a calibration scheme handle and delete the calibration scheme handle.
|
||||
- :ref:`adc-result-conversion` - covers how to convert ADC raw result to calibrated result.
|
||||
- :ref:`adc-thread-safety` - lists which APIs are guaranteed to be thread-safe by the driver.
|
||||
- :ref:`Minimize Noise <adc-minimize-noise>` - describes a general way to minimize the noise.
|
||||
:esp32: - :ref:`adc-kconfig-options` - lists the supported Kconfig options that can be used to make a different effect on driver behavior.
|
||||
|
||||
|
||||
.. _adc-calibration-scheme-creation:
|
||||
|
||||
Calibration Scheme Creation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ADC calibration driver provides ADC calibration scheme(s). From the calibration driver's point of view, an ADC calibration scheme is created for an ADC calibration handle :cpp:type:`adc_cali_handle_t`.
|
||||
|
||||
:cpp:func:`adc_cali_check_scheme` can be used to know which calibration scheme is supported on the chip. If you already know the supported schemes, this step can be skipped. Just call the corresponding function to create the scheme handle.
|
||||
|
||||
If you use your custom ADC calibration schemes, you could either modify this function :cpp:func:`adc_cali_check_scheme`, or just skip this step and call your custom creation function.
|
||||
|
||||
.. only:: esp32 or esp32s2 or esp32c2
|
||||
|
||||
ADC Calibration Line Fitting Scheme
|
||||
```````````````````````````````````
|
||||
|
||||
{IDF_TARGET_NAME} supports :c:macro:`ADC_CALI_SCHEME_VER_LINE_FITTING` scheme. To create this scheme, set up :cpp:type:`adc_cali_line_fitting_config_t` first.
|
||||
|
||||
- :cpp:member:`adc_cali_line_fitting_config_t::unit_id`, the ADC that your ADC raw results are from.
|
||||
- :cpp:member:`adc_cali_line_fitting_config_t::atten`, ADC attenuation that your ADC raw results use.
|
||||
- :cpp:member:`adc_cali_line_fitting_config_t::bitwidth`, bit width of ADC raw result.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
There is also a configuration :cpp:member:`adc_cali_line_fitting_config_t::default_vref`. Normally this can be simply set to 0. Line Fitting scheme does not rely on this value. However, if the Line Fitting scheme required eFuse bits are not burned on your board, the driver will rely on this value to do the calibration.
|
||||
|
||||
You can use :cpp:func:`adc_cali_scheme_line_fitting_check_efuse` to check the eFuse bits. Normally the Line Fitting scheme eFuse value is :c:macro:`ADC_CALI_LINE_FITTING_EFUSE_VAL_EFUSE_TP` or :c:macro:`ADC_CALI_LINE_FITTING_EFUSE_VAL_EFUSE_VREF`. This means the Line Fitting scheme uses calibration parameters burned in the eFuse to do the calibration.
|
||||
|
||||
When the Line Fitting scheme eFuse value is :c:macro:`ADC_CALI_LINE_FITTING_EFUSE_VAL_DEFAULT_VREF`, you need to set the :cpp:member:`esp_adc_cali_line_fitting_init::default_vref`. Default vref is an estimate of the ADC reference voltage provided as a parameter during calibration.
|
||||
|
||||
After setting up the configuration structure, call :cpp:func:`adc_cali_create_scheme_line_fitting` to create a Line Fitting calibration scheme handle.
|
||||
|
||||
.. only:: esp32s2
|
||||
|
||||
This function may fail due to reasons such as :c:macro:`ESP_ERR_INVALID_ARG` or :c:macro:`ESP_ERR_NO_MEM`. Especially, when the function returns :c:macro:`ESP_ERR_NOT_SUPPORTED`, this means the calibration scheme required eFuse bits are not burned on your board.
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
};
|
||||
ESP_ERROR_CHECK(adc_cali_create_scheme_line_fitting(&cali_config, &handle));
|
||||
|
||||
|
||||
When the ADC calibration is no longer used, please delete the calibration scheme handle by calling :cpp:func:`adc_cali_delete_scheme_line_fitting`.
|
||||
|
||||
|
||||
Delete Line Fitting Scheme
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_LOGI(TAG, "delete %s calibration scheme", "Line Fitting");
|
||||
ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(handle));
|
||||
|
||||
|
||||
.. only:: esp32c3 or esp32s3 or esp32c6 or esp32h2 or esp32c5 or esp32p4
|
||||
|
||||
ADC Calibration Curve Fitting Scheme
|
||||
````````````````````````````````````
|
||||
|
||||
{IDF_TARGET_NAME} supports :c:macro:`ADC_CALI_SCHEME_VER_CURVE_FITTING` scheme. To create this scheme, set up :cpp:type:`adc_cali_curve_fitting_config_t` first.
|
||||
|
||||
|
||||
.. only:: esp32c3 or esp32s3
|
||||
|
||||
- :cpp:member:`adc_cali_curve_fitting_config_t::unit_id`, the ADC that your ADC raw results are from.
|
||||
- :cpp:member:`adc_cali_curve_fitting_config_t::chan`, this member is kept here for extensibility. The calibration scheme only differs by attenuation, there is no difference among different channels.
|
||||
- :cpp:member:`adc_cali_curve_fitting_config_t::atten`, ADC attenuation that your ADC raw results use.
|
||||
- :cpp:member:`adc_cali_curve_fitting_config_t::bitwidth`, bit width of ADC raw result.
|
||||
|
||||
.. only:: esp32c6 or esp32h2 or esp32c5 or esp32p4
|
||||
|
||||
- :cpp:member:`adc_cali_curve_fitting_config_t::unit_id`, the ADC that your ADC raw results are from.
|
||||
- :cpp:member:`adc_cali_curve_fitting_config_t::chan`, the ADC channel that your ADC raw results are from. The calibration scheme not only differs by attenuation but is also related to the channels.
|
||||
- :cpp:member:`adc_cali_curve_fitting_config_t::atten`, ADC attenuation that your ADC raw results use.
|
||||
- :cpp:member:`adc_cali_curve_fitting_config_t::bitwidth`, bit width of ADC raw result.
|
||||
|
||||
After setting up the configuration structure, call :cpp:func:`adc_cali_create_scheme_curve_fitting` to create a Curve Fitting calibration scheme handle. This function may fail due to reasons such as :c:macro:`ESP_ERR_INVALID_ARG` or :c:macro:`ESP_ERR_NO_MEM`.
|
||||
|
||||
ADC Calibration eFuse Related Failures
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When the function :cpp:func:`adc_cali_create_scheme_curve_fitting` returns :c:macro:`ESP_ERR_NOT_SUPPORTED`, this means the calibration scheme required eFuse bits are not correct on your board.
|
||||
|
||||
The ADC calibration scheme provided by ESP-IDF is based on the values in certain ADC calibration related on-chip eFuse bits. Espressif guarantees that these bits are burned during module manufacturing, so you don't have to burn these eFuses bits yourself.
|
||||
|
||||
If you see such an error, please contact us at `Technical Inquiries <https://www.espressif.com/en/contact-us/technical-inquiries>`__ website.
|
||||
|
||||
Create Curve Fitting Scheme
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
};
|
||||
ESP_ERROR_CHECK(adc_cali_create_scheme_curve_fitting(&cali_config, &handle));
|
||||
|
||||
|
||||
When the ADC calibration is no longer used, please delete the calibration scheme driver from the calibration handle by calling :cpp:func:`adc_cali_delete_scheme_curve_fitting`.
|
||||
|
||||
|
||||
Delete Curve Fitting Scheme
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_LOGI(TAG, "delete %s calibration scheme", "Curve Fitting");
|
||||
ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle));
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
If you want to use your custom calibration schemes, you could provide a creation function to create your calibration scheme handle. Check the function table ``adc_cali_scheme_t`` in ``components/esp_adc/interface/adc_cali_interface.h`` to know the ESP ADC calibration interface.
|
||||
|
||||
|
||||
.. _adc-result-conversion:
|
||||
|
||||
Result Conversion
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
After setting up the calibration characteristics, you can call :cpp:func:`adc_cali_raw_to_voltage` to convert the ADC raw result into calibrated result. The calibrated result is in the unit of mV. This function may fail due to an invalid argument. Especially, if this function returns :c:macro:`ESP_ERR_INVALID_STATE`, this means the calibration scheme is not created. You need to create a calibration scheme handle, use :cpp:func:`adc_cali_check_scheme` to know the supported calibration scheme. On the other hand, you could also provide a custom calibration scheme and create the handle.
|
||||
|
||||
.. only:: esp32c2
|
||||
|
||||
.. note::
|
||||
|
||||
ADC calibration is only supported under :c:macro:`ADC_ATTEN_DB_0` and :c:macro:`ADC_ATTEN_DB_12`. Under :c:macro:`ADC_ATTEN_DB_0`, the attenuation of ADC is set to 0 dB, and input voltage higher than 950 mV is not supported. Under :c:macro:`ADC_ATTEN_DB_12`, the attenuation of ADC is set to 11 dB, and input voltage higher than 2800 mV is not supported.
|
||||
|
||||
Get Voltage
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc_cali_handle, adc_raw[0][0], &voltage[0][0]));
|
||||
ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, voltage[0][0]);
|
||||
|
||||
|
||||
.. _adc-thread-safety:
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The factory function :cpp:func:`esp_adc_cali_new_scheme` is guaranteed to be thread-safe by the driver. Therefore, you can call them from different RTOS tasks without protection by extra locks.
|
||||
|
||||
Other functions that take the :cpp:type:`adc_cali_handle_t` as the first positional parameter are not thread-safe, you should avoid calling them from multiple tasks.
|
||||
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. _adc-kconfig-options:
|
||||
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_ADC_CALI_EFUSE_TP_ENABLE` - disable this to decrease the code size, if the calibration eFuse value is not set to :cpp:type:`ADC_CALI_LINE_FITTING_EFUSE_VAL_EFUSE_TP`.
|
||||
- :ref:`CONFIG_ADC_CALI_EFUSE_VREF_ENABLE` - disable this to decrease the code size, if the calibration eFuse value is not set to :cpp:type:`ADC_CALI_LINE_FITTING_EFUSE_VAL_EFUSE_VREF`.
|
||||
- :ref:`CONFIG_ADC_CALI_LUT_ENABLE` - disable this to decrease the code size, if you do not calibrate the ADC raw results under :c:macro:`ADC_ATTEN_DB_12`.
|
||||
|
||||
|
||||
.. _adc-minimize-noise:
|
||||
|
||||
Minimize Noise
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The {IDF_TARGET_NAME} ADC is sensitive to noise, leading to large discrepancies in ADC readings. Depending on the usage scenario, you may need to connect a bypass capacitor (e.g., a 100 nF ceramic capacitor) to the ADC input pad in use, to minimize noise. Besides, multisampling may also be used to further mitigate the effects of noise.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. figure:: ../../../../_static/diagrams/adc/adc-noise-graph.jpg
|
||||
:align: center
|
||||
:alt: ADC noise mitigation
|
||||
|
||||
Graph illustrating noise mitigation using capacitor and multisampling of 64 samples.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
|
||||
.. include-build-file:: inc/adc_cali.inc
|
||||
.. include-build-file:: inc/adc_cali_scheme.inc
|
||||
@@ -0,0 +1,436 @@
|
||||
Analog to Digital Converter (ADC) Continuous Mode Driver
|
||||
========================================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This document describes the ADC continuous mode driver on {IDF_TARGET_NAME}.
|
||||
|
||||
In continuous mode, the ADC performs automatic, high-speed sampling on one or more analog input channels, with results efficiently transferred to memory via DMA. This mode is suitable for applications that require periodic or high-frequency data acquisition.
|
||||
|
||||
To perform single, on-demand ADC conversions, check :doc:`ADC Oneshot Mode Driver <adc_oneshot>`.
|
||||
|
||||
Driver Concepts
|
||||
---------------
|
||||
|
||||
ADC continuous mode organizes data into conversion frames, which consist of multiple conversion results.
|
||||
|
||||
- Conversion Frame: A frame contains multiple conversion results. Conversion frame size is configured with :cpp:func:`adc_continuous_new_handle` in bytes.
|
||||
- Conversion Result: A result consists of multiple bytes, as defined by :c:macro:`SOC_ADC_DIGI_RESULT_BYTES`. Its structure is :cpp:type:`adc_digi_output_data_t`, including the ADC unit, ADC channel, and raw data.
|
||||
|
||||
.. image:: /../_static/diagrams/adc/adc_conversion_frame.png
|
||||
:scale: 100 %
|
||||
:align: center
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
The following sections of this document cover the typical steps to install the ADC continuous mode driver, and read ADC conversion results from a group of ADC channels continuously:
|
||||
|
||||
- :ref:`adc-continuous-resource-allocation`: covers which parameters should be set up to initialize the ADC continuous mode driver and how to deinitialize it.
|
||||
- :ref:`adc-continuous-adc-configurations`: describes how to configure the ADC(s) to make it work under continuous mode.
|
||||
- :ref:`adc-continuous-adc-control`: describes ADC control functions.
|
||||
- :ref:`adc-continuous-register-event-callbacks`: describes how to hook user-specific code to an ADC continuous mode event callback function.
|
||||
- :ref:`adc-continuous-read-conversion-result`: covers how to get ADC conversion result.
|
||||
- :ref:`adc-continuous-hardware-limitations`: describes the ADC-related hardware limitations.
|
||||
- :ref:`adc-continuous-power-management`: covers power management-related information.
|
||||
- :ref:`adc-continuous-iram-safe`: covers the IRAM safe functions.
|
||||
- :ref:`adc-continuous-thread-safety`: lists which APIs are guaranteed to be thread-safe by the driver.
|
||||
|
||||
|
||||
.. _adc-continuous-resource-allocation:
|
||||
|
||||
Resource Allocation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ADC continuous mode driver is implemented based on {IDF_TARGET_NAME} SAR ADC module. Different ESP targets might have different numbers of independent ADCs.
|
||||
|
||||
To create an ADC continuous mode driver handle, set up the required configuration structure :cpp:type:`adc_continuous_handle_cfg_t`:
|
||||
|
||||
- :cpp:member:`adc_continuous_handle_cfg_t::max_store_buf_size`: set the maximum size of the pool in bytes, and the driver saves ADC conversion result into the pool.
|
||||
- :cpp:member:`adc_continuous_handle_cfg_t::conv_frame_size`: set the size of the ADC conversion frame, in bytes.
|
||||
- :cpp:member:`adc_continuous_handle_cfg_t::flags`: set the flags that can change the driver's behavior.
|
||||
|
||||
- ``flush_pool``: When the pool is full, the old data in the buffer pool will be automatically flushed and new data will be written. Otherwise, when the pool is full, new conversion results will be lost.
|
||||
|
||||
|
||||
After setting up the above configurations for the ADC, call :cpp:func:`adc_continuous_new_handle` with the prepared :cpp:type:`adc_continuous_handle_cfg_t`. This function may fail due to various errors such as invalid arguments, insufficient memory, etc.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
Especially, when this function returns :c:macro:`ESP_ERR_NOT_FOUND`, this means the I2S0 peripheral is in use. See :ref:`adc-continuous-hardware-limitations` for more information.
|
||||
|
||||
.. only:: esp32s2
|
||||
|
||||
Especially, when this function returns :c:macro:`ESP_ERR_NOT_FOUND`, this means the SPI3 peripheral is in use. See :ref:`adc-continuous-hardware-limitations` for more information.
|
||||
|
||||
.. only:: SOC_GDMA_SUPPORTED
|
||||
|
||||
Especially, when this function returns :c:macro:`ESP_ERR_NOT_FOUND`, this means there is no free GDMA channel.
|
||||
|
||||
If the ADC continuous mode driver is no longer used, you should deinitialize the driver by calling :cpp:func:`adc_continuous_deinit`.
|
||||
|
||||
|
||||
.. only:: SOC_ADC_DIG_IIR_FILTER_SUPPORTED
|
||||
|
||||
IIR filter
|
||||
~~~~~~~~~~
|
||||
|
||||
Two IIR filters are available when ADC is working in continuous mode. To create an ADC IIR filter, you should set up :cpp:type:`adc_continuous_iir_filter_config_t` and call :cpp:func:`adc_new_continuous_iir_filter`.
|
||||
|
||||
- :cpp:member:`adc_digi_filter_config_t::unit`: ADC unit.
|
||||
- :cpp:member:`adc_digi_filter_config_t::channel`: ADC channel to be filtered.
|
||||
- :cpp:member:`adc_digi_filter_config_t::coeff`: Filter coefficient.
|
||||
|
||||
.. only:: SOC_ADC_DIG_IIR_FILTER_UNIT_BINDED
|
||||
|
||||
On {IDF_TARGET_NAME}, the filter is per ADC unit. Once a filter is enabled, all the enabled ADC channels in this ADC unit will be filtered. However, we suggest only enabling one ADC channel per unit, when using the filter feature. Because the filtered results depend on the previous filtered result. So you should not enable multiple ADC channels, to avoid mixing the filtered results.
|
||||
|
||||
To recycle a filter, you should call :cpp:func:`adc_del_continuous_iir_filter`.
|
||||
|
||||
.. only:: not SOC_ADC_DIG_IIR_FILTER_UNIT_BINDED
|
||||
|
||||
.. note::
|
||||
|
||||
If you use both filters on the same ADC channel, then only the first one will take effect.
|
||||
|
||||
.. only:: SOC_ADC_MONITOR_SUPPORTED
|
||||
|
||||
Monitor
|
||||
~~~~~~~
|
||||
|
||||
{IDF_TARGET_SOC_ADC_DIGI_MONITOR_NUM} monitors are available when ADC is working under continuous mode, you can set one or two threshold(s) of a monitor on a working ADC channel, then the monitor will invoke interrupts every sample loop if conversion result outranges of the threshold. To create an ADC monitor, you need to set up the :cpp:type:`adc_monitor_config_t` and call :cpp:func:`adc_new_continuous_monitor`.
|
||||
|
||||
- :cpp:member:`adc_monitor_config_t::adc_unit`: Configures which ADC unit the channel you want to monitor belongs to.
|
||||
- :cpp:member:`adc_monitor_config_t::channel`: The channel you want to monitor.
|
||||
- :cpp:member:`adc_monitor_config_t::h_threshold`: The high threshold, conversion result larger than this value invokes interrupt, set to -1 if do not use.
|
||||
- :cpp:member:`adc_monitor_config_t::l_threshold`: The low threshold, conversion result less than this value invokes interrupt, set to -1 if do not use.
|
||||
|
||||
Once a monitor is created, you can operate it by following APIs to construct your apps.
|
||||
|
||||
- :cpp:func:`adc_continuous_monitor_enable`: Enable a monitor.
|
||||
- :cpp:func:`adc_continuous_monitor_disable`: Disable a monitor.
|
||||
- :cpp:func:`adc_continuous_monitor_register_event_callbacks`: register user callbacks to take action when the ADC value exceeds of the thresholds.
|
||||
- :cpp:func:`adc_del_continuous_monitor`: Delete a created monitor and free resources.
|
||||
|
||||
.. only:: esp32s2
|
||||
|
||||
.. NOTE::
|
||||
|
||||
There are some hardware limitations on {IDF_TARGET_NAME}:
|
||||
1. Only one threshold is supported for one monitor.
|
||||
2. Only one monitor is supported for one ADC unit.
|
||||
3. All enabled channel(s) of a certain ADC unit in ADC continuous mode driver will be monitored. The :cpp:member:`adc_monitor_config_t::channel` parameter will not be used.
|
||||
|
||||
Specifically, the monitor function can be used to implement zero-crossing detection. As ADC cannot directly process negative input signals, an extra **DC bias** should be applied to the original signal before measurement.
|
||||
|
||||
First, add a DC bias to the input signal through a circuit to "shift" the negative signal into the ADC's measurement range. For the measurement range, please refer to the On-Chip Sensor and Analog Signal Processing chapter in `TRM <{IDF_TARGET_TRM_EN_URL}>`__. For example, adding a 1 V bias would transform a signal from -1 V to +1 V into 0 V to 2 V range. Then by setting the appropriate high and low thresholds, the ADC can detect if the input signal approaches zero, allowing for the identification of phase changes in the signal. Refer to the example code below for details.
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Initialize the ADC monitor handle
|
||||
adc_monitor_handle_t adc_monitor_handle = NULL;
|
||||
|
||||
// Configure the ADC monitor
|
||||
adc_monitor_config_t zero_crossing_config = {
|
||||
.adc_unit = EXAMPLE_ADC_UNIT_1, // Specify the ADC unit to monitor
|
||||
.channel = EXAMPLE_ADC_CHANNEL_0, // Specify the ADC channel to monitor
|
||||
.h_threshold = 1100, // Set the high threshold close to the DC bias and adjust it as needed
|
||||
.l_threshold = 900, // Set the low threshold close to the DC bias and adjust it as needed
|
||||
};
|
||||
|
||||
// Create the ADC monitor
|
||||
ESP_ERROR_CHECK(adc_new_continuous_monitor(&zero_crossing_config, &adc_monitor_handle));
|
||||
|
||||
// Register the callback function
|
||||
adc_monitor_evt_cbs_t zero_crossing_cbs = {
|
||||
.on_over_high_thresh = example_on_exceed_high_thresh,
|
||||
.on_below_low_thresh = example_on_below_low_thresh,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(adc_continuous_monitor_register_event_callbacks(adc_monitor_handle, &zero_crossing_cbs, NULL));
|
||||
|
||||
// Enable the ADC monitor
|
||||
ESP_ERROR_CHECK(adc_continuous_monitor_enable(adc_monitor_handle));
|
||||
|
||||
// Disable and delete the ADC monitor
|
||||
ESP_ERROR_CHECK(adc_continuous_monitor_disable(adc_monitor_handle));
|
||||
ESP_ERROR_CHECK(adc_del_continuous_monitor(adc_monitor_handle));
|
||||
|
||||
Initialize the ADC Continuous Mode Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: c
|
||||
|
||||
adc_continuous_handle_t handle = NULL;
|
||||
adc_continuous_handle_cfg_t adc_config = {
|
||||
.max_store_buf_size = 1024,
|
||||
.conv_frame_size = 256,
|
||||
};
|
||||
ESP_ERROR_CHECK(adc_continuous_new_handle(&adc_config, &handle));
|
||||
|
||||
|
||||
Recycle the ADC Unit
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_ERROR_CHECK(adc_continuous_deinit(handle));
|
||||
|
||||
|
||||
.. _adc-continuous-adc-configurations:
|
||||
|
||||
ADC Configurations
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After the ADC continuous mode driver is initialized, set up the :cpp:type:`adc_continuous_config_t` to configure ADC IOs to measure analog signal:
|
||||
|
||||
- :cpp:member:`adc_continuous_config_t::pattern_num`: number of ADC channels that will be used.
|
||||
- :cpp:member:`adc_continuous_config_t::adc_pattern`: list of configs for each ADC channel that will be used, see the description below.
|
||||
- :cpp:member:`adc_continuous_config_t::sample_freq_hz`: expected ADC sampling frequency in Hz.
|
||||
- :cpp:member:`adc_continuous_config_t::conv_mode`: continuous conversion mode.
|
||||
- :cpp:member:`adc_continuous_config_t::format`: conversion output format.
|
||||
|
||||
Set :cpp:type:`adc_digi_pattern_config_t` with the following process:
|
||||
|
||||
- :cpp:member:`adc_digi_pattern_config_t::atten`: ADC attenuation. Refer to the On-Chip Sensor and Analog Signal Processing chapter in `TRM <{IDF_TARGET_TRM_EN_URL}>`__.
|
||||
- :cpp:member:`adc_digi_pattern_config_t::channel`: the IO corresponding ADC channel number. See the note below.
|
||||
- :cpp:member:`adc_digi_pattern_config_t::unit`: the ADC that the IO is subordinate to.
|
||||
- :cpp:member:`adc_digi_pattern_config_t::bit_width`: the bitwidth of the raw conversion result.
|
||||
|
||||
.. note::
|
||||
|
||||
For the IO corresponding ADC channel number, check `TRM <{IDF_TARGET_TRM_EN_URL}#sensor>`__ to acquire the ADC IOs. Besides, :cpp:func:`adc_continuous_io_to_channel` and :cpp:func:`adc_continuous_channel_to_io` can be used to acquire the ADC channels and ADC IOs.
|
||||
|
||||
To make these settings take effect, call :cpp:func:`adc_continuous_config` with the configuration structure above. This API may fail due to reasons like :c:macro:`ESP_ERR_INVALID_ARG`. When it returns :c:macro:`ESP_ERR_INVALID_STATE`, this means the ADC continuous mode driver is started, you should not call this API at this moment.
|
||||
|
||||
See ADC continuous mode example :example:`peripherals/adc/continuous_read` to see configuration codes.
|
||||
|
||||
|
||||
.. only:: SOC_ADC_DIG_IIR_FILTER_SUPPORTED
|
||||
|
||||
To enable/disable the ADC IIR filter, you should call :cpp:func:`adc_continuous_iir_filter_enable` / :cpp:func:`adc_continuous_iir_filter_disable`.
|
||||
|
||||
.. only:: SOC_ADC_MONITOR_SUPPORTED
|
||||
|
||||
To enable/disable the ADC monitor, you should call :cpp:func:`adc_continuous_monitor_enable` / :cpp:func:`adc_continuous_monitor_disable`.
|
||||
|
||||
.. _adc-continuous-adc-control:
|
||||
|
||||
ADC Control
|
||||
^^^^^^^^^^^
|
||||
|
||||
Start and Stop
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Calling :cpp:func:`adc_continuous_start` makes the ADC start to measure analog signals from the configured ADC channels, and generate the conversion results.
|
||||
|
||||
On the contrary, calling :cpp:func:`adc_continuous_stop` stops the ADC conversion.
|
||||
|
||||
.. code::c
|
||||
|
||||
ESP_ERROR_CHECK(adc_continuous_start(handle));
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_ERROR_CHECK(adc_continuous_stop(handle));
|
||||
|
||||
|
||||
.. _adc-continuous-register-event-callbacks:
|
||||
|
||||
Register Event Callbacks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By calling :cpp:func:`adc_continuous_register_event_callbacks`, you can hook your own function to the driver ISR. Supported event callbacks are listed in :cpp:type:`adc_continuous_evt_cbs_t`.
|
||||
|
||||
- :cpp:member:`adc_continuous_evt_cbs_t::on_conv_done`: this is invoked when one conversion frame finishes.
|
||||
- :cpp:member:`adc_continuous_evt_cbs_t::on_pool_ovf`: this is invoked when the internal pool is full. Newer conversion results will be discarded.
|
||||
|
||||
As the above callbacks are called in an ISR context, you should always ensure the callback function is suitable for an ISR context. Blocking logic should not appear in these callbacks. The callback function prototype is declared in :cpp:type:`adc_continuous_callback_t`.
|
||||
|
||||
You can also register your own context when calling :cpp:func:`adc_continuous_register_event_callbacks` by the parameter ``user_data``. This user data will be passed to the callback functions directly.
|
||||
|
||||
This function may fail due to reasons like :c:macro:`ESP_ERR_INVALID_ARG`. Especially, when :ref:`CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE` is enabled, this error may indicate that the callback functions are not in the internal RAM. Check the error log for more details. Besides, when it fails due to :c:macro:`ESP_ERR_INVALID_STATE`, it indicates that the ADC continuous mode driver is started, and you should not add a callback at this moment.
|
||||
|
||||
|
||||
Conversion Done Event
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When the driver completes a conversion, it triggers the :cpp:member:`adc_continuous_evt_cbs_t::on_conv_done` event and fills the event data. Event data contains a buffer pointer to a conversion frame buffer, together with the size. Refer to :cpp:type:`adc_continuous_evt_data_t` to know the event data structure.
|
||||
|
||||
.. note::
|
||||
|
||||
It is worth noting that, the data buffer :cpp:member:`adc_continuous_evt_data_t::conv_frame_buffer` is maintained by the driver itself. Therefore, never free this piece of memory.
|
||||
|
||||
.. note::
|
||||
|
||||
When the Kconfig option :ref:`CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE` is enabled, the registered callbacks and the functions called by the callbacks should be placed in IRAM. The involved variables should be placed in internal RAM as well.
|
||||
|
||||
Pool Overflow Event
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ADC continuous mode driver has an internal pool to save the conversion results. When the pool is full, a pool overflow event will emerge. Under this condition, the driver will not fill in the event data. This usually happens because the speed to read data from the pool by calling :cpp:func:`adc_continuous_read` is much slower than the ADC conversion speed.
|
||||
|
||||
|
||||
.. _adc-continuous-read-conversion-result:
|
||||
|
||||
Read Conversion Result
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After calling :cpp:func:`adc_continuous_start`, the ADC continuous conversion starts. Call :cpp:func:`adc_continuous_read` to get the conversion results of the ADC channels. You need to provide a buffer to get the raw results.
|
||||
|
||||
Function :cpp:func:`adc_continuous_read` tries to read the expected length of conversion results each time.
|
||||
|
||||
- When calling :cpp:func:`adc_continuous_read`, you can request to read a conversion result of the specified length. Sometimes, however, the actual available conversion results may be less than the requested length, in which case the function still moves the data from the internal pool into the buffer you provided. Therefore, to learn the number of conversion results actually moved into the buffer, please check the value of ``out_length``.
|
||||
- If there is no conversion result generated in the internal pool, the function will block for ``timeout_ms`` until at least one conversion result is generated. If there are still no generated results, the function will return :c:macro:`ESP_ERR_TIMEOUT`.
|
||||
- If the generated results fill up the internal pool, newly generated results will be lost. Next time when :cpp:func:`adc_continuous_read` is called, this function will return :c:macro:`ESP_ERR_INVALID_STATE` to indicate this situation.
|
||||
|
||||
This API aims to give you a chance to read all the ADC continuous conversion results.
|
||||
|
||||
The ADC conversion results read from the above function are raw data. To calculate the voltage based on the ADC raw results, this formula can be used:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
Vout = Dout * Vmax / Dmax (1)
|
||||
|
||||
where:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 80
|
||||
:align: center
|
||||
|
||||
* - Vout
|
||||
- Digital output result, standing for the voltage.
|
||||
* - Dout
|
||||
- ADC raw digital reading result.
|
||||
* - Vmax
|
||||
- Maximum measurable input analog voltage, this is related to the ADC attenuation, please refer to the On-Chip Sensor and Analog Signal Processing chapter in `TRM <{IDF_TARGET_TRM_EN_URL}#sensor>`__.
|
||||
* - Dmax
|
||||
- Maximum of the output ADC raw digital reading result, which is 2^bitwidth, where the bitwidth is the :cpp:member:`adc_digi_pattern_config_t::bit_width` configured before.
|
||||
|
||||
To do further calibration to convert the ADC raw result to voltage in mV, please refer to :doc:`adc_calibration`.
|
||||
|
||||
Parse ADC Raw Data
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The raw data read from ADC continuous mode needs to be further parsed to obtain usable ADC conversion results. The function :cpp:func:`adc_continuous_parse_data` provides the functionality to parse raw data into structured ADC data.
|
||||
|
||||
.. note::
|
||||
|
||||
Input buffer requirements:
|
||||
|
||||
- **Length alignment**: `raw_data_size` must be a multiple of :c:macro:`SOC_ADC_DIGI_RESULT_BYTES`
|
||||
- **Buffer size**: Ensure the `raw_data` buffer is large enough to hold `raw_data_size` bytes of data
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Read raw data
|
||||
uint32_t ret_num = 0;
|
||||
esp_err_t ret = adc_continuous_read(handle, result, EXAMPLE_READ_LEN, &ret_num, 0);
|
||||
if (ret == ESP_OK) {
|
||||
// Parse raw data
|
||||
adc_continuous_data_t parsed_data[ret_num / SOC_ADC_DIGI_RESULT_BYTES];
|
||||
uint32_t num_parsed_samples = 0;
|
||||
|
||||
esp_err_t parse_ret = adc_continuous_parse_data(handle, result, ret_num, parsed_data, &num_parsed_samples);
|
||||
if (parse_ret == ESP_OK) {
|
||||
for (int i = 0; i < num_parsed_samples; i++) {
|
||||
if (parsed_data[i].valid) {
|
||||
ESP_LOGI(TAG, "ADC%d, Channel: %d, Value: %"PRIu32,
|
||||
parsed_data[i].unit + 1,
|
||||
parsed_data[i].channel,
|
||||
parsed_data[i].raw_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The parsed data structure :cpp:type:`adc_continuous_data_t` contains the following information:
|
||||
|
||||
- :cpp:member:`adc_continuous_data_t::unit`:ADC unit (ADC_UNIT_1 or ADC_UNIT_2)
|
||||
- :cpp:member:`adc_continuous_data_t::channel`:ADC channel number (0-9)
|
||||
- :cpp:member:`adc_continuous_data_t::raw_data`:ADC raw data value
|
||||
- :cpp:member:`adc_continuous_data_t::valid`:Whether the data is valid
|
||||
|
||||
Read and Parse ADC Data
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To simplify the usage flow, the function :cpp:func:`adc_continuous_read_parse` is provided, which merges the read and parse operations into a single function call.
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Using the read and parse function
|
||||
adc_continuous_data_t parsed_data[64]; // User specifies maximum number of samples
|
||||
uint32_t num_samples = 0;
|
||||
|
||||
esp_err_t ret = adc_continuous_read_parse(handle, parsed_data, 64, &num_samples, 1000);
|
||||
if (ret == ESP_OK) {
|
||||
for (int i = 0; i < num_samples; i++) {
|
||||
if (parsed_data[i].valid) {
|
||||
ESP_LOGI(TAG, "ADC%d, Channel: %d, Value: %"PRIu32,
|
||||
parsed_data[i].unit + 1,
|
||||
parsed_data[i].channel,
|
||||
parsed_data[i].raw_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.. _adc-continuous-hardware-limitations:
|
||||
|
||||
.. _hardware_limitations_adc_continuous:
|
||||
|
||||
Hardware Limitations
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. list::
|
||||
|
||||
- A specific ADC unit can only work under one operating mode at any one time, either continuous mode or one-shot mode. :cpp:func:`adc_continuous_start` has provided the protection.
|
||||
:not esp32s31: - Random Number Generator (RNG) uses ADC as an input source. When ADC continuous mode driver works, the random number generated from RNG will be less random.
|
||||
:SOC_ADC_DIFF_SUPPORTED: - In single-ended mode, if the N-side channel is used as the input interface, its raw-data polarity is inverted. For an input range of -2 V to 2 V, the N-side raw code is 4393 to 0. Please use the ADC calibration APIs.
|
||||
:esp32 or esp32s2: - ADC2 is also used by Wi-Fi. :cpp:func:`adc_continuous_start` has provided the protection between Wi-Fi driver and ADC continuous mode driver.
|
||||
:esp32: - ADC continuous mode driver uses I2S0 peripheral as hardware DMA FIFO. Therefore, if I2S0 is in use already, the :cpp:func:`adc_continuous_new_handle` will return :c:macro:`ESP_ERR_NOT_FOUND`.
|
||||
:esp32: - ESP32 DevKitC: GPIO 0 cannot be used due to external auto program circuits.
|
||||
:esp32: - ESP-WROVER-KIT: GPIO 0, 2, 4, and 15 cannot be used due to external connections for different purposes.
|
||||
:esp32s2: - ADC continuous mode driver uses SPI3 peripheral as hardware DMA FIFO. Therefore, if SPI3 is in use already, the :cpp:func:`adc_continuous_new_handle` will return :c:macro:`ESP_ERR_NOT_FOUND`.
|
||||
:esp32c3: - ADC2 DMA functionality is no longer supported to retrieve ADC conversion results due to hardware limitations, as unstable results have been observed. This issue can be found in `ESP32C3 Errata <https://www.espressif.com/sites/default/files/documentation/esp32-c3_errata_en.pdf>`_. For compatibility, you can enable :ref:`CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3` to force use ADC2.
|
||||
:esp32s3: - ADC2 DMA functionality is no longer supported to retrieve ADC conversion results due to hardware limitations, as unstable results have been observed. This issue can be found in `ESP32S3 Errata <https://www.espressif.com/sites/default/files/documentation/esp32-s3_errata_en.pdf>`_. For compatibility, you can enable :ref:`CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3` to force use ADC2.
|
||||
|
||||
.. _adc-continuous-power-management:
|
||||
|
||||
Power Management
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
When power management is enabled, i.e., :ref:`CONFIG_PM_ENABLE` is on, the APB clock frequency may be adjusted when the system is in an idle state, thus potentially changing the behavior of ADC continuous conversion.
|
||||
|
||||
However, the continuous mode driver can prevent this change by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. The lock is acquired after the continuous conversion is started by :cpp:func:`adc_continuous_start`. Similarly, the lock will be released after :cpp:func:`adc_continuous_stop`. Therefore, :cpp:func:`adc_continuous_start` and :cpp:func:`adc_continuous_stop` should appear in pairs, otherwise, the power management will be out of action.
|
||||
|
||||
|
||||
.. _adc-continuous-iram-safe:
|
||||
|
||||
IRAM Safe
|
||||
^^^^^^^^^
|
||||
|
||||
All the ADC continuous mode driver APIs are not IRAM-safe. They are not supposed to be run when the Cache is disabled. By enabling the Kconfig option :ref:`CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE`, the driver's internal ISR handler is IRAM-safe, which means even when the Cache is disabled, the driver will still save the conversion results into its internal pool.
|
||||
|
||||
|
||||
.. _adc-continuous-thread-safety:
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
ADC continuous mode driver APIs are not guaranteed to be thread-safe. However, the share hardware mutual exclusion is provided by the driver. See :ref:`adc-continuous-hardware-limitations` for more details.
|
||||
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
* :example:`peripherals/adc/continuous_read` demonstrates how to use the ADC Continuous Read Mode (DMA Mode) on {IDF_TARGET_NAME} development boards to read from GPIO pins via on-chip ADC modules.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/adc_continuous.inc
|
||||
@@ -0,0 +1,216 @@
|
||||
Analog to Digital Converter (ADC) Oneshot Mode Driver
|
||||
=====================================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This document describes the ADC oneshot mode driver on {IDF_TARGET_NAME}.
|
||||
|
||||
Oneshot mode allows you to perform single, on-demand ADC conversions on selected analog input channels. It is suitable for applications that require infrequent or triggered sampling, as opposed to continuous data acquisition.
|
||||
|
||||
.. only:: SOC_ADC_DMA_SUPPORTED
|
||||
|
||||
To perform continuous data acquisition, {IDF_TARGET_NAME} provides :doc:`ADC Continuous Mode Driver <adc_continuous>`.
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
The following sections of this document cover the typical steps to install and operate an ADC:
|
||||
|
||||
- :ref:`adc-oneshot-resource-allocation` - covers which parameters should be set up to get an ADC handle and how to recycle the resources when ADC finishes working.
|
||||
- :ref:`adc-oneshot-unit-configuration` - covers the parameters that should be set up to configure the ADC unit, so as to get ADC conversion raw result.
|
||||
- :ref:`adc-oneshot-read-conversion-result` - covers how to get ADC conversion raw result.
|
||||
- :ref:`hardware_limitations_adc_oneshot` - describes the ADC-related hardware limitations.
|
||||
- :ref:`adc-oneshot-power-management` - covers power management-related information.
|
||||
- :ref:`adc-oneshot-iram-safe` - describes tips on how to read ADC conversion raw results when the cache is disabled.
|
||||
- :ref:`adc-oneshot-thread-safety` - lists which APIs are guaranteed to be thread-safe by the driver.
|
||||
- :ref:`adc-oneshot-kconfig-options` - lists the supported Kconfig options that can be used to make a different effect on driver behavior.
|
||||
|
||||
.. _adc-oneshot-resource-allocation:
|
||||
|
||||
Resource Allocation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ADC oneshot mode driver is implemented based on {IDF_TARGET_NAME} SAR ADC module. Different ESP chips might have different numbers of independent ADCs. From the oneshot mode driver's point of view, an ADC instance is represented by :cpp:type:`adc_oneshot_unit_handle_t`.
|
||||
|
||||
To install an ADC instance, set up the required initial configuration structure :cpp:type:`adc_oneshot_unit_init_cfg_t`:
|
||||
|
||||
- :cpp:member:`adc_oneshot_unit_init_cfg_t::unit_id` selects the ADC. Please refer to the `datasheet <{IDF_TARGET_TRM_EN_URL}>`__ to know dedicated analog IOs for this ADC.
|
||||
- :cpp:member:`adc_oneshot_unit_init_cfg_t::clk_src` selects the source clock of the ADC. If set to 0, the driver will fall back to using a default clock source, see :cpp:type:`adc_oneshot_clk_src_t` to know the details.
|
||||
- :cpp:member:`adc_oneshot_unit_init_cfg_t::ulp_mode` sets if the ADC will be working under ULP mode.
|
||||
|
||||
.. todo::
|
||||
|
||||
Add ULP ADC-related docs here.
|
||||
|
||||
After setting up the initial configurations for the ADC, call :cpp:func:`adc_oneshot_new_unit` with the prepared :cpp:type:`adc_oneshot_unit_init_cfg_t`. This function will return an ADC unit handle if the allocation is successful.
|
||||
|
||||
This function may fail due to various errors such as invalid arguments, insufficient memory, etc. Specifically, when the to-be-allocated ADC instance is registered already, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. Number of available ADC(s) is recorded by :c:macro:`SOC_ADC_PERIPH_NUM`.
|
||||
|
||||
If a previously created ADC instance is no longer required, you should recycle the ADC instance by calling :cpp:func:`adc_oneshot_del_unit`, related hardware and software resources will be recycled as well.
|
||||
|
||||
Create an ADC Unit Handle Under Normal Oneshot Mode
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: c
|
||||
|
||||
adc_oneshot_unit_handle_t adc1_handle;
|
||||
adc_oneshot_unit_init_cfg_t init_config1 = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.ulp_mode = ADC_ULP_MODE_DISABLE,
|
||||
};
|
||||
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
|
||||
|
||||
|
||||
Recycle the ADC Unit
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle));
|
||||
|
||||
|
||||
.. _adc-oneshot-unit-configuration:
|
||||
|
||||
Unit Configuration
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After an ADC instance is created, set up the :cpp:type:`adc_oneshot_chan_cfg_t` to configure ADC IOs to measure analog signal:
|
||||
|
||||
- :cpp:member:`adc_oneshot_chan_cfg_t::atten`, ADC attenuation. Refer to `Datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__ > ``ADC Characteristics``.
|
||||
- :cpp:member:`adc_oneshot_chan_cfg_t::bitwidth`, the bitwidth of the raw conversion result.
|
||||
|
||||
.. note::
|
||||
|
||||
For the IO corresponding ADC channel number, check `datasheet <{IDF_TARGET_TRM_EN_URL}>`__ to know the ADC IOs.
|
||||
|
||||
Additionally, :cpp:func:`adc_continuous_io_to_channel` and :cpp:func:`adc_continuous_channel_to_io` can be used to know the ADC channels and ADC IOs.
|
||||
|
||||
To make these settings take effect, call :cpp:func:`adc_oneshot_config_channel` with the above configuration structure. You should specify an ADC channel to be configured as well. Function :cpp:func:`adc_oneshot_config_channel` can be called multiple times to configure different ADC channels. The Driver will save each of these channel configurations internally.
|
||||
|
||||
|
||||
Configure Two ADC Channels
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: c
|
||||
|
||||
adc_oneshot_chan_cfg_t config = {
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
.atten = ADC_ATTEN_DB_12,
|
||||
};
|
||||
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN0, &config));
|
||||
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN1, &config));
|
||||
|
||||
|
||||
.. _adc-oneshot-read-conversion-result:
|
||||
|
||||
Read Conversion Result
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After above configurations, the ADC is ready to measure the analog signal(s) from the configured ADC channel(s). Call :cpp:func:`adc_oneshot_read` to get the conversion raw result of an ADC channel.
|
||||
|
||||
- :cpp:func:`adc_oneshot_read` is safe to use. ADC(s) are shared by some other drivers/peripherals, see :ref:`hardware_limitations_adc_oneshot`. This function uses mutexes to avoid concurrent hardware usage. Therefore, this function should not be used in an ISR context. This function may fail when the ADC is in use by other drivers/peripherals, and return :c:macro:`ESP_ERR_TIMEOUT`. Under this condition, the ADC raw result is invalid.
|
||||
|
||||
This function will fail due to invalid arguments.
|
||||
|
||||
The ADC conversion results read from this function are raw data. To calculate the voltage based on the ADC raw results, this formula can be used:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
Vout = Dout * Vmax / Dmax (1)
|
||||
|
||||
where:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 80
|
||||
:align: center
|
||||
|
||||
* - Vout
|
||||
- Digital output result, standing for the voltage.
|
||||
* - Dout
|
||||
- ADC raw digital reading result.
|
||||
* - Vmax
|
||||
- Maximum measurable input analog voltage, this is related to the ADC attenuation, please refer to `TRM <{IDF_TARGET_TRM_EN_URL}>`__ > ``On-Chip Sensor and Analog Signal Processing``.
|
||||
* - Dmax
|
||||
- Maximum of the output ADC raw digital reading result, which is 2^bitwidth, where bitwidth is the :cpp:member:`adc_oneshot_chan_cfg_t::bitwidth` configured before.
|
||||
|
||||
For further calibration to convert the ADC raw result to voltage in mV, please refer to :doc:`adc_calibration`.
|
||||
|
||||
|
||||
Read Raw Result
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN0, &adc_raw[0][0]));
|
||||
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, adc_raw[0][0]);
|
||||
|
||||
ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN1, &adc_raw[0][1]));
|
||||
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN1, adc_raw[0][1]);
|
||||
|
||||
|
||||
.. _hardware_limitations_adc_oneshot:
|
||||
|
||||
Hardware Limitations
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. list::
|
||||
|
||||
:not esp32s31: - Random Number Generator (RNG) uses ADC as an input source. When ADC :cpp:func:`adc_oneshot_read` works, the random number generated from RNG will be less random.
|
||||
:SOC_ADC_DMA_SUPPORTED: - A specific ADC unit can only work under one operating mode at any one time, either continuous mode or oneshot mode. :cpp:func:`adc_oneshot_read` has provided the protection.
|
||||
:SOC_ADC_DIFF_SUPPORTED: - In single-ended mode, if the N-side channel is used as the input interface, its raw-data polarity is inverted. For an input range of -2 to 2, the N-side raw code is 4393 to 0. Please use the ADC calibration APIs.
|
||||
:esp32 or esp32s2 or esp32s3: - ADC2 is also used by Wi-Fi. :cpp:func:`adc_oneshot_read` has provided protection between the Wi-Fi driver and ADC oneshot mode driver.
|
||||
:esp32c3: - ADC2 oneshot mode is no longer supported, due to hardware limitations. The results are not stable. This issue can be found in `ESP32-C3 Series SoC Errata <https://www.espressif.com/sites/default/files/documentation/esp32-c3_errata_en.pdf>`_. For compatibility, you can enable :ref:`CONFIG_ADC_ONESHOT_FORCE_USE_ADC2_ON_C3` to force use ADC2.
|
||||
:esp32: - ESP32-DevKitC: GPIO0 cannot be used in oneshot mode, because the DevKit has used it for auto-flash.
|
||||
:esp32: - ESP-WROVER-KIT: GPIO 0, 2, 4, and 15 cannot be used due to external connections for different purposes.
|
||||
|
||||
.. _adc-oneshot-power-management:
|
||||
|
||||
Power Management
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
When power management is enabled, i.e., :ref:`CONFIG_PM_ENABLE` is on, the system clock frequency may be adjusted when the system is in an idle state. However, the ADC oneshot mode driver works in a polling routine, the :cpp:func:`adc_oneshot_read` will poll the CPU until the function returns. During this period of time, the task in which ADC oneshot mode driver resides will not be blocked. Therefore the clock frequency is stable when reading.
|
||||
|
||||
|
||||
.. _adc-oneshot-iram-safe:
|
||||
|
||||
IRAM Safe
|
||||
^^^^^^^^^
|
||||
|
||||
By default, all the ADC oneshot mode driver APIs are not supposed to be run when the Cache is disabled. Cache may be disabled due to many reasons, such as Flash writing/erasing, OTA, etc. If these APIs execute when the Cache is disabled, you will probably see errors like ``Illegal Instruction`` or ``Load/Store Prohibited``.
|
||||
|
||||
|
||||
.. _adc-oneshot-thread-safety:
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
- :cpp:func:`adc_oneshot_new_unit`
|
||||
- :cpp:func:`adc_oneshot_config_channel`
|
||||
- :cpp:func:`adc_oneshot_read`
|
||||
- :cpp:func:`adc_oneshot_del_unit`
|
||||
|
||||
Above functions are guaranteed to be thread-safe. Therefore, you can call them from different RTOS tasks without protection by extra locks.
|
||||
|
||||
|
||||
.. _adc-oneshot-kconfig-options:
|
||||
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM` controls where to place the ADC fast read function (IRAM or Flash), see :ref:`adc-oneshot-iram-safe` for more details.
|
||||
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
* :example:`peripherals/adc/oneshot_read` demonstrates how to obtain a one-shot ADC reading from a GPIO pin using the ADC one-shot mode driver and how to use the ADC Calibration functions to obtain a calibrated result in mV on {IDF_TARGET_NAME}.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/adc_oneshot.inc
|
||||
@@ -0,0 +1,102 @@
|
||||
Analog to Digital Converter (ADC)
|
||||
=================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
This guide provides a comprehensive overview of the ADC (Analog to Digital Converter) controller on {IDF_TARGET_NAME}. It begins by introducing core ADC concepts such as conversion principles, raw data resolution, reference voltage, and attenuation. Then it walks through the two supported ADC driver modes — oneshot mode and continuous mode — along with ADC calibration, which helps improve accuracy.
|
||||
|
||||
{IDF_TARGET_NAME} integrates {IDF_TARGET_SOC_ADC_PERIPH_NUM} ADC(s) for measuring analog signals from multiple input channels. For details about the number of measurement channels (analog-enabled pins), voltage ranges, and other ADC characteristics, please refer to the `datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__.
|
||||
|
||||
|
||||
ADC Conversion
|
||||
---------------
|
||||
|
||||
ADC conversion is the process of converting an input analog voltage to a digital value. The results provided by the ADC driver APIs are raw data values that represent the analog input in digital form.
|
||||
|
||||
By default, the bit width of these raw ADC results is 12 bits. This means the input voltage range is divided into 4096 (2\ :sup:`12`) discrete levels, which defines the minimum detectable change in input signal.
|
||||
|
||||
The voltage ``Vdata`` corresponding to a raw ADC result ``data`` is calculated as:
|
||||
|
||||
.. math::
|
||||
|
||||
V_{data} = \frac{data}{2^{bitwidth} - 1} \times V_{ref}
|
||||
|
||||
Where:
|
||||
|
||||
- ``data`` is the raw ADC result.
|
||||
- ``bitwidth`` is the resolution of the ADC result (e.g., 12 bits).
|
||||
- ``Vref`` is the ADC’s reference voltage.
|
||||
|
||||
By design, ``Vref`` is set to 1100 mV. However, due to manufacturing variations, the actual value may range between 1000 mV and 1200 mV depending on the chip.
|
||||
|
||||
To obtain calibrated and accurate voltage values, refer to the section :doc:`adc_calibration`, which explains how to use the ADC calibration driver to adjust the raw results based on the actual ``Vref`` value.
|
||||
|
||||
|
||||
ADC Attenuation
|
||||
---------------
|
||||
|
||||
The ADC can measure analog voltages from 0 V to ``Vref``. To measure higher voltages, input signals can be attenuated before being passed to the ADC.
|
||||
|
||||
The supported attenuation levels are:
|
||||
|
||||
- 0 dB (k≈100%)
|
||||
- 2.5 dB (k≈75%)
|
||||
- 6 dB (k≈50%)
|
||||
- 12 dB (k≈25%)
|
||||
|
||||
Higher attenuation levels allow the ADC to measure higher input voltages. The voltage ``Vdata`` after applying attenuation can be calculated using:
|
||||
|
||||
.. math::
|
||||
|
||||
V_{data} = \frac{V_{ref}}{k}\times{\frac{data}{2^{bitwidth} - 1}}
|
||||
|
||||
Where:
|
||||
|
||||
- ``k`` is the ratio value corresponding to the attenuation level.
|
||||
- Other variables are as defined above.
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
For detailed input voltage ranges associated with each attenuation setting, refer to the `datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__ > Electrical Characteristics > ADC Characteristics.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
For detailed input voltage ranges associated with each attenuation setting, refer to the `datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__ > Function Description > Analog Peripherals > Analog-to-Digital Converter (ADC).
|
||||
|
||||
Driver Usage
|
||||
------------
|
||||
|
||||
.. list::
|
||||
|
||||
- ADC unit supports **oneshot mode**. Oneshot mode is suitable for oneshot sampling: ADC samples one channel at a time.
|
||||
:SOC_ADC_DMA_SUPPORTED: - Each ADC unit supports **continuous mode**. Continuous mode is designed for continuous sampling: ADC sequentially samples a group of channels or continuously samples a single channel.
|
||||
|
||||
See the guide below for implementation details:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
adc_oneshot
|
||||
:SOC_ADC_DMA_SUPPORTED: adc_continuous
|
||||
|
||||
|
||||
ADC Calibration
|
||||
----------------
|
||||
|
||||
The ADC calibration driver corrects deviations through software to obtain more accurate output results.
|
||||
|
||||
For more information, refer to the following guide:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
adc_calibration
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/adc_channel.inc
|
||||
.. include-build-file:: inc/adc_types.inc
|
||||
@@ -0,0 +1,598 @@
|
||||
=================
|
||||
Analog Comparator
|
||||
=================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
An analog comparator tells you whether a source signal is above or below a reference signal. The reference can come from either an internal generator or an external input.
|
||||
|
||||
This makes the analog comparator useful for tasks such as:
|
||||
|
||||
- Detecting whether a waveform crosses a fixed threshold
|
||||
- Turning a sine wave into a digital square wave around a chosen reference level
|
||||
- Comparing one analog signal against another analog signal
|
||||
- Generating interrupts or ETM events when the comparison result changes
|
||||
- Building simple hardware signal-processing pipelines without continuous CPU involvement
|
||||
|
||||
.. note::
|
||||
|
||||
The analog comparator peripheral is not a continuous-time comparator in the same sense as a discrete analog comparator IC or an op-amp based analog front end. It is driven by a source clock, so comparison results are sampled in hardware time. In practice, clock source, scan timing, debounce, and ETM configuration all affect what you observe on real signals.
|
||||
|
||||
Quick Start
|
||||
===========
|
||||
|
||||
If you are new to this peripheral, start with the simplest workflow:
|
||||
|
||||
1. Create a comparator unit
|
||||
2. Select the source signal and reference source
|
||||
3. Configure the internal reference or external reference input
|
||||
4. Optionally configure debounce
|
||||
5. Register a callback or create ETM connections
|
||||
6. Enable the comparator
|
||||
|
||||
The following example uses an internal reference and an interrupt callback to detect when a source signal crosses 50% VDD:
|
||||
|
||||
.. code:: c
|
||||
|
||||
ana_cmpr_handle_t cmpr = NULL;
|
||||
ana_cmpr_config_t config = {
|
||||
.unit = 0, // Pick a comparator unit that is available on your target
|
||||
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT, // Most applications can use the default clock source
|
||||
.ref_src = ANA_CMPR_REF_SRC_INTERNAL, // Use the internal reference for a simple fixed threshold
|
||||
.cross_type = ANA_CMPR_CROSS_ANY, // Trigger on both positive and negative crossings
|
||||
.src_chan0_gpio = 0, // Replace with the analog comparator source GPIO wired on your board
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr));
|
||||
|
||||
ana_cmpr_internal_ref_config_t ref_cfg = {
|
||||
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD, // Set the internal reference voltage to 50% of VDD, i.e., compare whether the source signal is above or below half of the supply voltage
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg));
|
||||
|
||||
ana_cmpr_debounce_config_t dbc_cfg = {
|
||||
.wait_us = 10, // Set a debounce time of 10 microseconds to suppress duplicate interrupts from noisy signals around the threshold
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
|
||||
|
||||
ana_cmpr_event_callbacks_t cbs = {
|
||||
.on_cross = example_ana_cmpr_on_cross_callback, // Register a callback to be called when a crossing event occurs; this callback should be defined elsewhere in your code
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_register_event_callbacks(cmpr, &cbs, NULL));
|
||||
|
||||
ESP_ERROR_CHECK(ana_cmpr_enable(cmpr));
|
||||
|
||||
This flow introduces the most important ideas:
|
||||
|
||||
- :cpp:func:`ana_cmpr_new_unit` creates a comparator instance and selects the source GPIO and reference source.
|
||||
- :cpp:func:`ana_cmpr_set_internal_reference` chooses the threshold when using the internal reference.
|
||||
- :cpp:func:`ana_cmpr_set_debounce` can suppress duplicate crossing interrupts caused by noisy or slow-moving signals.
|
||||
- :cpp:func:`ana_cmpr_register_event_callbacks` lets your code react when the comparison result changes.
|
||||
- :cpp:func:`ana_cmpr_enable` starts the hardware after configuration is complete.
|
||||
|
||||
The rest of this document expands this flow into typical usage scenarios.
|
||||
|
||||
Lifecycle and Valid API States
|
||||
------------------------------
|
||||
|
||||
The following diagram shows the analog comparator lifecycle and the states in which each API is intended to be used:
|
||||
|
||||
.. mermaid::
|
||||
|
||||
flowchart TD
|
||||
NC([Not created]) -->|ana_cmpr_new_unit| INIT[Init / Disabled]
|
||||
INIT -->|ana_cmpr_enable| EN[Enabled]
|
||||
EN -->|ana_cmpr_disable| INIT
|
||||
INIT -->|ana_cmpr_del_unit| NC
|
||||
|
||||
subgraph INIT_APIS [Init-state APIs]
|
||||
SCAN[ana_cmpr_set_scan_config]
|
||||
ADD[ana_cmpr_add_src_chan<br/>ana_cmpr_remove_src_chan]
|
||||
CB[ana_cmpr_register_event_callbacks]
|
||||
end
|
||||
|
||||
subgraph BOTH_APIS [APIs usable in both states]
|
||||
IR[ana_cmpr_set_internal_reference]
|
||||
DBC[ana_cmpr_set_debounce]
|
||||
end
|
||||
|
||||
subgraph EN_APIS [Enabled-only API]
|
||||
TRIG[ana_cmpr_trigger_scan]
|
||||
end
|
||||
|
||||
INIT -. can call .-> SCAN
|
||||
INIT -. can call .-> ADD
|
||||
INIT -. can call .-> CB
|
||||
INIT -. can call .-> IR
|
||||
INIT -. can call .-> DBC
|
||||
EN -. can call .-> IR
|
||||
EN -. can call .-> DBC
|
||||
EN -. can call .-> TRIG
|
||||
|
||||
classDef stateNeutral fill:#f8fafc,stroke:#64748b,stroke-width:1.5px,color:#111827;
|
||||
classDef stateInit fill:#eff6ff,stroke:#2563eb,stroke-width:1.5px,color:#111827;
|
||||
classDef stateEnabled fill:#fdf2f8,stroke:#db2777,stroke-width:1.5px,color:#111827;
|
||||
classDef initApi fill:#eff6ff,stroke:#60a5fa,color:#111827;
|
||||
classDef bothApi fill:#ecfdf5,stroke:#10b981,color:#111827;
|
||||
classDef enabledApi fill:#fdf2f8,stroke:#f472b6,color:#111827;
|
||||
|
||||
class NC stateNeutral;
|
||||
class INIT stateInit;
|
||||
class EN stateEnabled;
|
||||
class SCAN,ADD,CB initApi;
|
||||
class IR,DBC bothApi;
|
||||
class TRIG enabledApi;
|
||||
|
||||
The main constraints to remember are:
|
||||
|
||||
- :cpp:func:`ana_cmpr_register_event_callbacks`, :cpp:func:`ana_cmpr_add_src_chan`, :cpp:func:`ana_cmpr_remove_src_chan`, and :cpp:func:`ana_cmpr_set_scan_config` are intended to be used while the comparator is still in init state.
|
||||
- :cpp:func:`ana_cmpr_trigger_scan` only works after :cpp:func:`ana_cmpr_enable`.
|
||||
- :cpp:func:`ana_cmpr_del_unit` requires the comparator to be disabled first.
|
||||
|
||||
Scenario 1: Detect When a Signal Crosses a Fixed Threshold
|
||||
==========================================================
|
||||
|
||||
Use this setup when you have one analog input and you only care whether it is above or below a threshold.
|
||||
|
||||
Typical examples:
|
||||
|
||||
- Detect whether a sensor output rises above a limit
|
||||
- Convert a sine wave into a digital waveform around a fixed threshold
|
||||
- Trigger software when a signal changes polarity around mid-supply
|
||||
|
||||
The easiest way to build this is to compare the source signal against the internal reference.
|
||||
|
||||
Example: Internal Reference with Callback
|
||||
-----------------------------------------
|
||||
|
||||
.. code:: c
|
||||
|
||||
static bool IRAM_ATTR example_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr,
|
||||
const ana_cmpr_cross_event_data_t *edata,
|
||||
void *user_ctx)
|
||||
{
|
||||
if (edata->cross_type == ANA_CMPR_CROSS_POS) {
|
||||
gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, 1);
|
||||
} else if (edata->cross_type == ANA_CMPR_CROSS_NEG) {
|
||||
gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ana_cmpr_handle_t cmpr = NULL;
|
||||
ana_cmpr_config_t config = {
|
||||
.unit = 0, // Use a valid unit index for the selected target
|
||||
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT, // Start with default unless you need specific timing
|
||||
.ref_src = ANA_CMPR_REF_SRC_INTERNAL, // Compare source against internal threshold
|
||||
.cross_type = ANA_CMPR_CROSS_ANY, // Report both positive and negative crossings
|
||||
.src_chan0_gpio = 0, // Replace with the source channel GPIO connected on your board
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr));
|
||||
|
||||
ana_cmpr_internal_ref_config_t ref_cfg = {
|
||||
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg));
|
||||
|
||||
ana_cmpr_debounce_config_t dbc_cfg = {
|
||||
.wait_us = 10,
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
|
||||
|
||||
ana_cmpr_event_callbacks_t cbs = {
|
||||
.on_cross = example_ana_cmpr_on_cross_callback,
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_register_event_callbacks(cmpr, &cbs, NULL));
|
||||
|
||||
ESP_ERROR_CHECK(ana_cmpr_enable(cmpr));
|
||||
|
||||
How to Read the Configuration
|
||||
-----------------------------
|
||||
|
||||
The :cpp:type:`ana_cmpr_config_t` structure describes the basic shape of the comparator instance:
|
||||
|
||||
- :cpp:member:`ana_cmpr_config_t::unit` selects which comparator unit to use.
|
||||
- :cpp:member:`ana_cmpr_config_t::clk_src` selects the comparator source clock. For most applications, :cpp:enumerator:`ANA_CMPR_CLK_SRC_DEFAULT` is the right starting point.
|
||||
- :cpp:member:`ana_cmpr_config_t::ref_src` selects whether the reference comes from the internal threshold generator or an external analog input.
|
||||
- :cpp:member:`ana_cmpr_config_t::cross_type` selects which crossings should trigger the interrupt callback: positive, negative, or both.
|
||||
- :cpp:member:`ana_cmpr_config_t::src_chan0_gpio` selects the source GPIO on targets whose analog comparator pads are configurable.
|
||||
- :cpp:member:`ana_cmpr_config_t::resample_limit` sets the unit-wide number of consecutive consistent samples required before a channel state update is accepted.
|
||||
|
||||
When using the internal reference, :cpp:func:`ana_cmpr_set_internal_reference` sets the threshold with :cpp:member:`ana_cmpr_internal_ref_config_t::ref_volt`. The available values are fixed percentages of VDD. For example, :cpp:enumerator:`ANA_CMPR_REF_VOLT_50_PCT_VDD` means the comparator reports whether the source signal is above or below half of the supply voltage.
|
||||
|
||||
Debounce and Signal Stability
|
||||
-----------------------------
|
||||
|
||||
Real analog signals are often noisy near the threshold. Without debounce, a signal that slowly moves across the threshold or jitters around it may trigger several crossing interrupts in a short time.
|
||||
|
||||
Use :cpp:func:`ana_cmpr_set_debounce` to suppress these duplicate crossings:
|
||||
|
||||
- :cpp:member:`ana_cmpr_debounce_config_t::wait_us` is the time window during which new crossing interrupts are temporarily ignored after one crossing has already been reported.
|
||||
|
||||
As a rule of thumb:
|
||||
|
||||
- Increase ``wait_us`` if your signal is noisy and you see too many interrupts.
|
||||
- Decrease ``wait_us`` if your signal changes quickly and you start missing real crossings.
|
||||
|
||||
Callback Behavior
|
||||
-----------------
|
||||
|
||||
Register callbacks with :cpp:func:`ana_cmpr_register_event_callbacks` before enabling the unit.
|
||||
|
||||
The comparator currently provides one callback group member:
|
||||
|
||||
- :cpp:member:`ana_cmpr_event_callbacks_t::on_cross`
|
||||
|
||||
The callback receives :cpp:type:`ana_cmpr_cross_event_data_t`, which tells you:
|
||||
|
||||
- :cpp:member:`ana_cmpr_cross_event_data_t::cross_type` - whether the source crossed upward or downward
|
||||
- :cpp:member:`ana_cmpr_cross_event_data_t::src_chan_id` - which source channel triggered the event on targets that support multiple source channels
|
||||
|
||||
On targets that do not support independent positive and negative interrupt reporting, :cpp:member:`ana_cmpr_cross_event_data_t::cross_type` can always be reported as :cpp:enumerator:`ANA_CMPR_CROSS_ANY`.
|
||||
|
||||
The callback runs in interrupt context, so keep it short and non-blocking.
|
||||
|
||||
.. note::
|
||||
|
||||
If :ref:`CONFIG_ANA_CMPR_ISR_CACHE_SAFE` is enabled, place the callback and any accessed data in internal RAM.
|
||||
|
||||
Scenario 2: Compare a Signal Against an External Reference
|
||||
==========================================================
|
||||
|
||||
Use this setup when the reference level is not a fixed percentage of VDD.
|
||||
|
||||
Typical examples:
|
||||
|
||||
- Compare a sensor signal against a voltage divider output
|
||||
- Compare a waveform against another waveform
|
||||
- Generate a digital result from a source/reference pair that both come from analog front-end circuitry
|
||||
|
||||
In this case, the comparator source is still your input signal, but the reference comes from another analog input.
|
||||
|
||||
Example: External Reference Input
|
||||
---------------------------------
|
||||
|
||||
.. code:: c
|
||||
|
||||
ana_cmpr_handle_t cmpr = NULL;
|
||||
ana_cmpr_config_t config = {
|
||||
.unit = 0, // Use a valid unit index for the selected target
|
||||
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT, // Default clock is usually enough
|
||||
.ref_src = ANA_CMPR_REF_SRC_EXTERNAL, // Select external reference mode
|
||||
.cross_type = ANA_CMPR_CROSS_ANY, // Report both crossing directions
|
||||
.src_chan0_gpio = 0, // Source signal GPIO from your board design
|
||||
.ext_ref_gpio = 1, // Reference signal GPIO from your board design
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr));
|
||||
|
||||
ana_cmpr_debounce_config_t dbc_cfg = {
|
||||
.wait_us = 10,
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
|
||||
|
||||
ana_cmpr_event_callbacks_t cbs = {
|
||||
.on_cross = example_ana_cmpr_on_cross_callback,
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_register_event_callbacks(cmpr, &cbs, NULL));
|
||||
|
||||
ESP_ERROR_CHECK(ana_cmpr_enable(cmpr));
|
||||
|
||||
What Changes Compared with Scenario 1
|
||||
-------------------------------------
|
||||
|
||||
The main difference is :cpp:member:`ana_cmpr_config_t::ref_src`:
|
||||
|
||||
- :cpp:enumerator:`ANA_CMPR_REF_SRC_INTERNAL` means the reference is generated inside the comparator block.
|
||||
- :cpp:enumerator:`ANA_CMPR_REF_SRC_EXTERNAL` means the reference comes from a dedicated analog input.
|
||||
|
||||
When using an external reference:
|
||||
|
||||
- :cpp:member:`ana_cmpr_config_t::ext_ref_gpio` becomes meaningful on targets with configurable analog comparator pads.
|
||||
- :cpp:func:`ana_cmpr_set_internal_reference` is not used.
|
||||
|
||||
You can query the actual GPIOs in use with :cpp:func:`ana_cmpr_get_channel_gpio`:
|
||||
|
||||
.. code:: c
|
||||
|
||||
gpio_num_t src_gpio = -1;
|
||||
gpio_num_t ext_ref_gpio = -1;
|
||||
// Read back actual channel mapping after initialization.
|
||||
ESP_ERROR_CHECK(ana_cmpr_get_channel_gpio(cmpr, ANA_CMPR_SOURCE_CHAN, 0, &src_gpio));
|
||||
ESP_ERROR_CHECK(ana_cmpr_get_channel_gpio(cmpr, ANA_CMPR_EXT_REF_CHAN, 0, &ext_ref_gpio));
|
||||
|
||||
.. only:: SOC_ANA_CMPR_SUPPORT_ETM_SCAN
|
||||
|
||||
Scenario 3: Build a Low-CPU Hardware Signal Path with ETM
|
||||
=========================================================
|
||||
|
||||
Use this setup when you want the hardware to react to crossings directly, without routing every event through the CPU.
|
||||
|
||||
Typical examples:
|
||||
|
||||
- Turn a sampled analog input into a GPIO waveform with very low CPU load
|
||||
- Use a timer to schedule comparator scans periodically
|
||||
- Connect comparator crossings to other peripherals through ETM
|
||||
|
||||
Example: GPTimer Periodic Scan with ETM
|
||||
---------------------------------------
|
||||
|
||||
:example:`peripherals/analog_comparator/etm_periodic_scan` example builds the following hardware chain:
|
||||
|
||||
- GPTimer alarm event -> GPTimer enable-alarm task
|
||||
- GPTimer alarm event -> Analog comparator start task
|
||||
- Analog comparator positive cross event -> GPIO set task
|
||||
- Analog comparator negative cross event -> GPIO clear task
|
||||
|
||||
In other words, the timer schedules scans, the comparator samples the source signal, and the crossing result drives a monitor GPIO directly through ETM.
|
||||
|
||||
The key comparator-side configuration looks like this:
|
||||
|
||||
.. code:: c
|
||||
|
||||
ana_cmpr_handle_t cmpr = NULL;
|
||||
ana_cmpr_config_t config = {
|
||||
.unit = 0, // Use a valid unit index for your target
|
||||
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT, // Keep default unless ETM timing requires otherwise
|
||||
.ref_src = ANA_CMPR_REF_SRC_INTERNAL, // Use internal reference for threshold
|
||||
.cross_type = ANA_CMPR_CROSS_ANY, // Keep both edge directions for ETM output waveform
|
||||
.src_chan0_gpio = 0, // Set according to your board's source signal wiring
|
||||
.resample_limit = 3, // Improve noise immunity before reporting channel state
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr));
|
||||
|
||||
ana_cmpr_internal_ref_config_t ref_cfg = {
|
||||
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg));
|
||||
|
||||
ana_cmpr_scan_config_t scan_cfg = {
|
||||
.scan_mode = ANA_CMPR_SCAN_MODE_FULL, // Scan all configured channels each cycle
|
||||
.poll_period_us = 2, // Tune this for update rate vs. timing margin
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_set_scan_config(cmpr, &scan_cfg));
|
||||
|
||||
ana_cmpr_etm_task_config_t cmpr_task_cfg = {
|
||||
.task_type = ANA_CMPR_TASK_START,
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_new_etm_task(cmpr, &cmpr_task_cfg, &cmpr_start_task));
|
||||
|
||||
ana_cmpr_etm_event_config_t pos_evt_cfg = {
|
||||
.event_type = ANA_CMPR_EVENT_POS_CROSS, // Rising crossing event
|
||||
.src_chan_id = 0, // Monitor source channel 0 in this example
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &pos_evt_cfg, &cmpr_pos_evt));
|
||||
|
||||
ana_cmpr_etm_event_config_t neg_evt_cfg = {
|
||||
.event_type = ANA_CMPR_EVENT_NEG_CROSS, // Falling crossing event
|
||||
.src_chan_id = 0, // Same source channel as positive crossing
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &neg_evt_cfg, &cmpr_neg_evt));
|
||||
|
||||
Understanding Scan Configuration
|
||||
--------------------------------
|
||||
|
||||
When the target supports source-channel scan, :cpp:func:`ana_cmpr_set_scan_config` controls how the comparator walks through source channels:
|
||||
|
||||
- :cpp:member:`ana_cmpr_scan_config_t::scan_mode` selects the scanning behavior.
|
||||
- :cpp:member:`ana_cmpr_scan_config_t::poll_period_us` sets the wait time between channel comparisons.
|
||||
|
||||
These fields matter most when:
|
||||
|
||||
- you use multiple source channels
|
||||
- the source signal is noisy
|
||||
- you want periodic sampling instead of interrupting on every hardware edge
|
||||
|
||||
In practice:
|
||||
|
||||
- Shorter ``poll_period_us`` means faster scan response but tighter timing margins.
|
||||
|
||||
ETM Event and Task Handles
|
||||
--------------------------
|
||||
|
||||
The analog comparator provides ETM handles through two APIs:
|
||||
|
||||
- :cpp:func:`ana_cmpr_new_etm_event` creates an event handle from a comparator crossing.
|
||||
- :cpp:func:`ana_cmpr_new_etm_task` creates a task handle that can be triggered by another ETM event.
|
||||
|
||||
In the current analog comparator driver:
|
||||
|
||||
- ETM event type is selected by :cpp:member:`ana_cmpr_etm_event_config_t::event_type`
|
||||
- The source channel is selected by :cpp:member:`ana_cmpr_etm_event_config_t::src_chan_id`
|
||||
- Optional event delay is configured by :cpp:member:`ana_cmpr_etm_event_config_t::event_delay`
|
||||
- Task type is selected by :cpp:member:`ana_cmpr_etm_task_config_t::task_type`
|
||||
|
||||
For how to connect the comparator events and tasks to ETM channels, refer to :doc:`ETM </api-reference/peripherals/etm>`.
|
||||
|
||||
Working with Multiple Source Channels
|
||||
=====================================
|
||||
|
||||
Some targets support more than one source channel per comparator unit. If your application needs to sample several analog inputs against the same reference, you can configure extra source channels while the comparator is in init state.
|
||||
|
||||
The relevant APIs are:
|
||||
|
||||
- :cpp:func:`ana_cmpr_add_src_chan`
|
||||
- :cpp:func:`ana_cmpr_remove_src_chan`
|
||||
- :cpp:func:`ana_cmpr_set_src_chan_cross_type`
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
ana_cmpr_src_chan_config_t src_cfg = {
|
||||
.gpio_num = EXAMPLE_SRC_CHAN1_GPIO,
|
||||
.cross_type = ANA_CMPR_CROSS_ANY,
|
||||
};
|
||||
ESP_ERROR_CHECK(ana_cmpr_add_src_chan(cmpr, 1, &src_cfg));
|
||||
|
||||
When using multiple source channels:
|
||||
|
||||
- Add or remove channels only before :cpp:func:`ana_cmpr_enable`.
|
||||
- If you remove every configured source channel, :cpp:func:`ana_cmpr_enable` fails with ``ESP_ERR_INVALID_STATE``.
|
||||
- :cpp:func:`ana_cmpr_set_src_chan_cross_type` is only runtime-configurable on ESP32-H2.
|
||||
|
||||
Runtime Observation and Timing Helpers
|
||||
======================================
|
||||
|
||||
The following APIs are general runtime helpers and are not tied to multi-source configuration:
|
||||
|
||||
- :cpp:func:`ana_cmpr_trigger_scan` forces one software-triggered scan sequence (on targets that support software-triggered scan).
|
||||
- :cpp:func:`ana_cmpr_get_output_level` reads the latest sampled comparator output of a source channel (on targets that support output-level reporting).
|
||||
- :cpp:func:`ana_cmpr_get_clock_resolution_hz` returns PAD_COMP_CLK resolution in Hz.
|
||||
- :cpp:func:`ana_cmpr_get_capture_timestamps` reads current/previous crossing timestamps (on capture-capable targets).
|
||||
|
||||
Use :cpp:func:`ana_cmpr_get_output_level` when you need current digital comparison state from software. Use :cpp:func:`ana_cmpr_get_clock_resolution_hz` when converting PAD_COMP_CLK ticks (for example, capture timestamps) to time units. Use :cpp:func:`ana_cmpr_trigger_scan` when you want deterministic software-controlled refresh timing; it requires :cpp:func:`ana_cmpr_enable` and returns ``ESP_ERR_INVALID_STATE`` in init state.
|
||||
|
||||
To get meaningful capture timestamps, enable the capture timer when creating the unit:
|
||||
|
||||
.. code:: c
|
||||
|
||||
ana_cmpr_config_t config = {
|
||||
.unit = 0, // Use a valid unit index for your target
|
||||
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT, // PAD_COMP_CLK source
|
||||
.ref_src = ANA_CMPR_REF_SRC_INTERNAL, // Reference mode used by your capture scenario
|
||||
.cross_type = ANA_CMPR_CROSS_ANY, // Keep both crossing directions by default
|
||||
.src_chan0_gpio = 0, // Must match your board's source signal wiring
|
||||
.en_capture_timer = true, // Required for capture timestamp APIs
|
||||
};
|
||||
|
||||
Then convert ticks to time with:
|
||||
|
||||
.. code:: c
|
||||
|
||||
uint32_t resolution_hz = 0;
|
||||
uint32_t current_ticks = 0;
|
||||
ESP_ERROR_CHECK(ana_cmpr_get_clock_resolution_hz(cmpr, &resolution_hz));
|
||||
ESP_ERROR_CHECK(ana_cmpr_get_capture_timestamps(cmpr, 0, ¤t_ticks, NULL));
|
||||
uint32_t time_us = (uint32_t)(((uint64_t)current_ticks * 1000000U) / resolution_hz);
|
||||
|
||||
Practical Configuration Notes
|
||||
=============================
|
||||
|
||||
Choosing the Clock Source
|
||||
-------------------------
|
||||
|
||||
The comparator clock affects sampling behavior. In most applications, use :cpp:enumerator:`ANA_CMPR_CLK_SRC_DEFAULT` unless you have a specific clocking requirement.
|
||||
|
||||
Note that the analog comparator clock source can be shared with other GPIO-extension style peripherals on some targets. If different peripherals request incompatible clock sources, initialization can fail.
|
||||
|
||||
Choosing the Internal Reference Level
|
||||
-------------------------------------
|
||||
|
||||
The internal reference is defined as a percentage of VDD, not as an arbitrary voltage in volts.
|
||||
|
||||
This is convenient when:
|
||||
|
||||
- the input signal naturally swings with the supply range
|
||||
- you want a simple mid-supply or supply-relative threshold
|
||||
|
||||
This is less suitable when:
|
||||
|
||||
- you need a precise threshold independent of VDD variation
|
||||
- the threshold must change continuously instead of in coarse steps
|
||||
|
||||
Tuning Debounce
|
||||
---------------
|
||||
|
||||
Debounce is one of the first parameters to tune on real hardware.
|
||||
|
||||
- Too little debounce: duplicate interrupts or unstable event behavior near the threshold
|
||||
- Too much debounce: missed crossings on fast-changing signals
|
||||
|
||||
There is no single good value for every application. Start small and adjust based on observed signal behavior.
|
||||
|
||||
Tuning Scan Timing
|
||||
------------------
|
||||
|
||||
On scan-capable targets, scan timing and resampling should be chosen together:
|
||||
|
||||
- Reduce ``poll_period_us`` when you need quicker updates
|
||||
- Increase :cpp:member:`ana_cmpr_config_t::resample_limit` when you need better noise rejection
|
||||
- Avoid choosing a scan rate that is much slower than the effective input dynamics you want to detect
|
||||
|
||||
Operational Notes
|
||||
=================
|
||||
|
||||
Enable and Disable Lifecycle
|
||||
----------------------------
|
||||
|
||||
The comparator follows a simple lifecycle:
|
||||
|
||||
1. Create and configure it
|
||||
2. Enable it
|
||||
3. Run your application
|
||||
4. Disable it before deleting it
|
||||
|
||||
Use :cpp:func:`ana_cmpr_enable` and :cpp:func:`ana_cmpr_disable` as a pair.
|
||||
|
||||
While enabled, only a limited set of runtime control functions are intended to be adjusted. Configuration functions such as source channel management and scan configuration must be done before enabling the unit.
|
||||
|
||||
Power Management
|
||||
----------------
|
||||
|
||||
When :ref:`CONFIG_PM_ENABLE` is enabled, sleep and clock changes can affect the comparator. The driver uses a power management lock internally when needed, so enabling the comparator can prevent light sleep while the unit is active.
|
||||
|
||||
If power consumption matters, structure your application so the comparator is only enabled when needed.
|
||||
|
||||
IRAM Safe
|
||||
---------
|
||||
|
||||
If you need comparator interrupts to keep working while the cache is disabled, enable :ref:`CONFIG_ANA_CMPR_ISR_CACHE_SAFE`.
|
||||
|
||||
If you also need the runtime control functions to remain callable in that situation, enable :ref:`CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM`.
|
||||
|
||||
The following control APIs can be placed in IRAM:
|
||||
|
||||
- :cpp:func:`ana_cmpr_set_internal_reference`
|
||||
- :cpp:func:`ana_cmpr_set_debounce`
|
||||
- :cpp:func:`ana_cmpr_set_src_chan_cross_type`
|
||||
- :cpp:func:`ana_cmpr_trigger_scan`
|
||||
|
||||
Thread Safety
|
||||
-------------
|
||||
|
||||
The driver is designed to be thread-safe for concurrent API calls on the same :cpp:type:`ana_cmpr_handle_t`, by combining:
|
||||
|
||||
- A per-handle FSM (INIT/ENABLE/WAIT) with atomic state transitions to serialize incompatible operations
|
||||
- Short critical sections to keep shared software state and hardware registers updated atomically
|
||||
- Slot lifecycle protection for create/delete windows
|
||||
|
||||
The main lifecycle contract still applies: call :cpp:func:`ana_cmpr_disable` before :cpp:func:`ana_cmpr_del_unit`, and make sure no active dependent objects (for example ETM handles) remain when deleting the unit.
|
||||
|
||||
Kconfig Options
|
||||
---------------
|
||||
|
||||
The most relevant Kconfig options are:
|
||||
|
||||
- :ref:`CONFIG_ANA_CMPR_ISR_CACHE_SAFE`
|
||||
Keeps the default ISR path available when cache is disabled. Enable this if comparator interrupts must work in cache-off windows.
|
||||
- :ref:`CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM`
|
||||
Places ISR-callable runtime control APIs in IRAM, so they remain callable when cache is disabled.
|
||||
- :ref:`CONFIG_ANA_CMPR_ENABLE_DEBUG_LOG`
|
||||
Enables driver debug logs for troubleshooting and bring-up. This increases binary size and log verbosity.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_ANA_CMPR_SUPPORT_AUTO_SCAN: - :example:`peripherals/analog_comparator/auto_scan` shows auto-scan based threshold detection. After enabling the comparator, hardware scans continuously and updates output in real time, while monitor GPIO is driven via interrupt or ETM depending on target capabilities.
|
||||
:SOC_ANA_CMPR_SUPPORT_ETM_SCAN: - :example:`peripherals/analog_comparator/etm_periodic_scan` shows how to use GPTimer and ETM to trigger periodic comparator scans and drive a monitor GPIO from comparator crossing events.
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
Driver APIs
|
||||
-----------
|
||||
|
||||
.. include-build-file:: inc/ana_cmpr.inc
|
||||
|
||||
Driver Types
|
||||
------------
|
||||
|
||||
.. include-build-file:: inc/ana_cmpr_types.inc
|
||||
@@ -0,0 +1,317 @@
|
||||
=============================
|
||||
Asynchronous Color Conversion
|
||||
=============================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
This document introduces the Async Color Convert driver in ESP-IDF. The table of contents is as follows:
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
{IDF_TARGET_NAME} provides a DMA2D engine that can offload 2D copy and color conversion work from the CPU.
|
||||
|
||||
This driver is useful when your application needs to:
|
||||
|
||||
- convert an image from one pixel format to another
|
||||
- copy only a window of a larger image
|
||||
- queue multiple conversions without doing the work on the CPU
|
||||
- move between RGB and UYVY formats while selecting the RGB/YUV conversion standard
|
||||
|
||||
The Async Color Convert driver wraps DMA2D request preparation, queueing, and completion handling into a small API that supports both:
|
||||
|
||||
- asynchronous submission with an ISR callback
|
||||
- a simpler blocking API built on top of the same request path
|
||||
|
||||
Quick Start
|
||||
===========
|
||||
|
||||
If you are new to this driver, start with the simplest workflow:
|
||||
|
||||
1. Install the driver
|
||||
2. Prepare one :cpp:type:`async_color_convert_request_t`
|
||||
3. Submit the conversion through either the blocking or non-blocking API
|
||||
4. Consume the converted output buffer after the conversion completes
|
||||
5. Either submit another request or uninstall the driver when finished
|
||||
|
||||
The typical usage flow is:
|
||||
|
||||
.. mermaid::
|
||||
|
||||
flowchart TD
|
||||
install["Install driver<br/>esp_async_color_convert_install_dma2d"] --> request["Prepare request<br/>async_color_convert_request_t"]
|
||||
request --> blocking["Blocking path<br/>esp_color_convert_blocking"]
|
||||
request --> nonBlocking["Non-blocking path<br/>esp_async_color_convert"]
|
||||
nonBlocking --> callback["Wait for callback or task notification"]
|
||||
blocking --> result["Use converted buffer"]
|
||||
callback --> result
|
||||
result --> request
|
||||
result --> uninstall["Optional cleanup<br/>esp_async_color_convert_uninstall"]
|
||||
|
||||
Scenario 1: Start with One Blocking Conversion
|
||||
==============================================
|
||||
|
||||
The easiest way to learn the API is to convert one image and wait until the conversion is complete.
|
||||
|
||||
The following flow mirrors the :example:`peripherals/dma/async_color_convert` example. It converts one embedded UYVY422 image into BGR24 and then lets the application consume the converted output:
|
||||
|
||||
.. code:: c
|
||||
|
||||
async_color_convert_handle_t conv_hdl = NULL; // Driver handle returned by the install API
|
||||
async_color_convert_config_t config = {
|
||||
.backlog = 1, // One in-flight request is enough for this simple blocking example
|
||||
.dma_burst_size = 16, // Start with the default burst size used by the example
|
||||
};
|
||||
// Create one Async Color Convert driver instance backed by DMA2D.
|
||||
ESP_ERROR_CHECK(esp_async_color_convert_install_dma2d(&config, &conv_hdl));
|
||||
|
||||
async_color_convert_request_t req = {
|
||||
.src_buffer = sample_96x64_uyvy_yuv_start, // Source image can be in flash or RAM, as long as DMA can access it
|
||||
.src_stride = 96, // Source image row stride, in pixels
|
||||
.src_height = 64, // Source image height, in pixels
|
||||
.src_x = 0, // Start from the left edge of the source image
|
||||
.src_y = 0,
|
||||
.dst_buffer = dst_bgr, // Destination buffer in DMA-capable RAM
|
||||
.dst_stride = 96, // Destination image row stride, in pixels
|
||||
.dst_height = 64, // Destination image height, in pixels
|
||||
.dst_x = 0, // Write the converted output from the top-left corner
|
||||
.dst_y = 0,
|
||||
.copy_width = 96, // Convert the full image width, in pixels
|
||||
.copy_height = 64, // Convert the full image height, in pixels
|
||||
.src_color_format = ESP_COLOR_FOURCC_UYVY, // Source pixels are UYVY422
|
||||
.dst_color_format = ESP_COLOR_FOURCC_BGR24, // Destination pixels are BGR24 (used as RGB888 in this driver)
|
||||
.color_conv_std = COLOR_CONV_STD_RGB_YUV_BT601, // RGB/YUV standard used for this conversion pair
|
||||
};
|
||||
|
||||
// Wait until DMA2D finishes the conversion. -1 means wait forever.
|
||||
ESP_ERROR_CHECK(esp_color_convert_blocking(conv_hdl, &req, -1));
|
||||
|
||||
// Release the driver after all conversions are done.
|
||||
ESP_ERROR_CHECK(esp_async_color_convert_uninstall(conv_hdl));
|
||||
|
||||
This flow introduces the most important ideas:
|
||||
|
||||
- :cpp:func:`esp_async_color_convert_install_dma2d` creates the driver instance
|
||||
- :cpp:type:`async_color_convert_request_t` describes the source image, destination image, and conversion window
|
||||
- :cpp:func:`esp_color_convert_blocking` waits until the hardware finishes the conversion
|
||||
- :cpp:func:`esp_async_color_convert_uninstall` releases the driver resources
|
||||
|
||||
For the blocking API, ``timeout_ms = -1`` means wait forever. Other timeout values are currently unsupported and return ``ESP_ERR_INVALID_ARG``.
|
||||
|
||||
Understanding ``async_color_convert_request_t``
|
||||
-----------------------------------------------
|
||||
|
||||
Most application issues come from building the request incorrectly, so it is worth understanding the structure carefully.
|
||||
|
||||
.. important::
|
||||
|
||||
In :cpp:type:`async_color_convert_request_t`, all geometry fields are measured in **pixels**, not bytes. This includes ``src_stride``, ``src_height``, ``src_x``, ``src_y``, ``dst_stride``, ``dst_height``, ``dst_x``, ``dst_y``, ``copy_width``, and ``copy_height``.
|
||||
|
||||
``src_stride`` and ``dst_stride`` are row strides, not conversion widths. They describe how many pixels each full image row spans in memory, so they can be larger than ``copy_width`` when converting a window inside a larger image.
|
||||
|
||||
The structure describes two things at the same time:
|
||||
|
||||
- the full source and destination images in memory
|
||||
- the rectangular window that should be converted
|
||||
|
||||
The key fields are:
|
||||
|
||||
- :cpp:member:`async_color_convert_request_t::src_buffer`
|
||||
Base address of the source image
|
||||
- :cpp:member:`async_color_convert_request_t::src_stride`
|
||||
Source image row stride in pixels
|
||||
- :cpp:member:`async_color_convert_request_t::src_height`
|
||||
Source image height in pixels
|
||||
- :cpp:member:`async_color_convert_request_t::src_x` and :cpp:member:`async_color_convert_request_t::src_y`
|
||||
Top-left corner of the source window
|
||||
- :cpp:member:`async_color_convert_request_t::dst_buffer`
|
||||
Base address of the destination image
|
||||
- :cpp:member:`async_color_convert_request_t::dst_stride`
|
||||
Destination image row stride in pixels
|
||||
- :cpp:member:`async_color_convert_request_t::dst_height`
|
||||
Destination image height in pixels
|
||||
- :cpp:member:`async_color_convert_request_t::dst_x` and :cpp:member:`async_color_convert_request_t::dst_y`
|
||||
Top-left corner of where the converted window should be written
|
||||
- :cpp:member:`async_color_convert_request_t::copy_width` and :cpp:member:`async_color_convert_request_t::copy_height`
|
||||
Size of the rectangle to convert
|
||||
- :cpp:member:`async_color_convert_request_t::src_color_format` and :cpp:member:`async_color_convert_request_t::dst_color_format`
|
||||
Source and destination pixel formats
|
||||
- :cpp:member:`async_color_convert_request_t::color_conv_std`
|
||||
RGB/YUV conversion standard, used for RGB <-> YUV conversions
|
||||
|
||||
Both the source window and destination window must stay within the bounds of their corresponding images.
|
||||
|
||||
Supported Conversions
|
||||
---------------------
|
||||
|
||||
The following format pairs are currently supported by this driver:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Source format
|
||||
- Destination format
|
||||
- Conversion standard
|
||||
* - ``ESP_COLOR_FOURCC_RGB16``
|
||||
- ``ESP_COLOR_FOURCC_RGB16``
|
||||
- N/A
|
||||
* - ``ESP_COLOR_FOURCC_BGR24``
|
||||
- ``ESP_COLOR_FOURCC_BGR24``
|
||||
- N/A
|
||||
* - ``ESP_COLOR_FOURCC_RGB24``
|
||||
- ``ESP_COLOR_FOURCC_RGB24``
|
||||
- N/A
|
||||
* - ``ESP_COLOR_FOURCC_UYVY``
|
||||
- ``ESP_COLOR_FOURCC_UYVY``
|
||||
- N/A
|
||||
* - ``ESP_COLOR_FOURCC_BGR24``
|
||||
- ``ESP_COLOR_FOURCC_RGB24``
|
||||
- N/A
|
||||
* - ``ESP_COLOR_FOURCC_RGB24``
|
||||
- ``ESP_COLOR_FOURCC_BGR24``
|
||||
- N/A
|
||||
* - ``ESP_COLOR_FOURCC_RGB16``
|
||||
- ``ESP_COLOR_FOURCC_BGR24``
|
||||
- N/A
|
||||
* - ``ESP_COLOR_FOURCC_BGR24``
|
||||
- ``ESP_COLOR_FOURCC_RGB16``
|
||||
- N/A
|
||||
* - ``ESP_COLOR_FOURCC_RGB24``
|
||||
- ``ESP_COLOR_FOURCC_RGB16``
|
||||
- N/A
|
||||
* - ``ESP_COLOR_FOURCC_BGR24``
|
||||
- ``ESP_COLOR_FOURCC_UYVY``
|
||||
- BT.601
|
||||
* - ``ESP_COLOR_FOURCC_BGR24``
|
||||
- ``ESP_COLOR_FOURCC_UYVY``
|
||||
- BT.709
|
||||
* - ``ESP_COLOR_FOURCC_RGB24``
|
||||
- ``ESP_COLOR_FOURCC_UYVY``
|
||||
- BT.601
|
||||
* - ``ESP_COLOR_FOURCC_RGB24``
|
||||
- ``ESP_COLOR_FOURCC_UYVY``
|
||||
- BT.709
|
||||
* - ``ESP_COLOR_FOURCC_UYVY``
|
||||
- ``ESP_COLOR_FOURCC_BGR24``
|
||||
- BT.601
|
||||
* - ``ESP_COLOR_FOURCC_UYVY``
|
||||
- ``ESP_COLOR_FOURCC_BGR24``
|
||||
- BT.709
|
||||
|
||||
.. note::
|
||||
|
||||
Always set :cpp:member:`async_color_convert_request_t::src_color_format` and
|
||||
:cpp:member:`async_color_convert_request_t::dst_color_format`.
|
||||
Set :cpp:member:`async_color_convert_request_t::color_conv_std` when converting between RGB and YUV.
|
||||
|
||||
Scenario 2: Use the Asynchronous API with a Callback
|
||||
====================================================
|
||||
|
||||
Once the blocking flow is clear, the next step is to queue a request and let the driver notify you from interrupt context when it is finished.
|
||||
|
||||
.. code:: c
|
||||
|
||||
static bool color_conv_done_cb(async_color_convert_handle_t conv_hdl,
|
||||
async_color_convert_event_data_t *edata,
|
||||
void *cb_args)
|
||||
{
|
||||
BaseType_t high_task_wakeup = pdFALSE; // Required by FreeRTOS when an ISR wakes a task
|
||||
SemaphoreHandle_t sem = (SemaphoreHandle_t)cb_args; // User context passed at submit time
|
||||
// Notify a waiting task that the conversion has finished.
|
||||
xSemaphoreGiveFromISR(sem, &high_task_wakeup);
|
||||
// Return true when the unblocked task should run immediately after the ISR.
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
async_color_convert_request_t req = {
|
||||
.src_buffer = src_buf, // Source image base address
|
||||
.src_stride = src_width, // Source image row stride, in pixels
|
||||
.src_height = src_height,
|
||||
.src_x = 0,
|
||||
.src_y = 0,
|
||||
.dst_buffer = dst_buf, // Destination image base address
|
||||
.dst_stride = dst_width, // Destination image row stride, in pixels
|
||||
.dst_height = dst_height,
|
||||
.dst_x = 0,
|
||||
.dst_y = 0,
|
||||
.copy_width = copy_width,
|
||||
.copy_height = copy_height,
|
||||
.src_color_format = ESP_COLOR_FOURCC_RGB16,
|
||||
.dst_color_format = ESP_COLOR_FOURCC_BGR24,
|
||||
};
|
||||
|
||||
// Queue one asynchronous request. The callback runs later in ISR context.
|
||||
ESP_ERROR_CHECK(esp_async_color_convert(conv_hdl, &req, color_conv_done_cb, sem));
|
||||
// Wait in task context until the callback gives the semaphore.
|
||||
xSemaphoreTake(sem, portMAX_DELAY);
|
||||
|
||||
The callback runs in ISR context, so keep it short and only use ISR-safe APIs such as ``xSemaphoreGiveFromISR`` or ``xQueueSendFromISR``.
|
||||
|
||||
Operational Notes
|
||||
=================
|
||||
|
||||
Driver Configuration
|
||||
--------------------
|
||||
|
||||
The driver configuration fields are:
|
||||
|
||||
- :cpp:member:`async_color_convert_config_t::backlog`
|
||||
Maximum number of in-flight or pending requests. ``0`` uses a driver default.
|
||||
- :cpp:member:`async_color_convert_config_t::dma_burst_size`
|
||||
DMA burst size in bytes. ``0`` uses a driver default.
|
||||
- :cpp:member:`async_color_convert_config_t::intr_priority`
|
||||
DMA2D interrupt priority. ``0`` uses the default low/medium priority.
|
||||
|
||||
DMA Burst Size
|
||||
--------------
|
||||
|
||||
The ``dma_burst_size`` affects DMA transfer efficiency:
|
||||
|
||||
- Larger burst sizes may improve throughput
|
||||
- Larger burst sizes can also increase bus occupancy, so they are not always best for every workload
|
||||
- Common starting values are 16, 32, and 64 bytes
|
||||
|
||||
The best value depends on the chip's DMA controller capabilities and how much memory bandwidth is shared with other active components in the system.
|
||||
|
||||
Thread Safety and ISR Rules
|
||||
---------------------------
|
||||
|
||||
- The driver is thread-safe. Requests from different tasks are serialized through the internal queue.
|
||||
- :cpp:func:`esp_async_color_convert` can be called from tasks to enqueue requests.
|
||||
- The callback type :cpp:type:`async_color_convert_isr_cb_t` runs in ISR context.
|
||||
- Do not call blocking APIs from the callback.
|
||||
- :cpp:func:`esp_color_convert_blocking` must not be called from ISR context.
|
||||
|
||||
Uninstalling the Driver
|
||||
-----------------------
|
||||
|
||||
When the driver is no longer needed:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Uninstall only after all queued conversions have completed.
|
||||
ESP_ERROR_CHECK(esp_async_color_convert_uninstall(conv_hdl));
|
||||
|
||||
If requests are still pending, :cpp:func:`esp_async_color_convert_uninstall` returns :c:macro:`ESP_ERR_INVALID_STATE`.
|
||||
|
||||
Application Example
|
||||
===================
|
||||
|
||||
- :example:`peripherals/dma/async_color_convert` shows a beginner-friendly blocking conversion flow:
|
||||
|
||||
- an embedded ``.yuv`` image is read directly from mapped flash
|
||||
- DMA2D converts the image from UYVY422 to BGR24
|
||||
- the converted output is base64-encoded and printed to the console
|
||||
- pytest reconstructs the image as a PNG artifact and compares it against a golden reference image
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
Async Color Convert Driver Functions
|
||||
------------------------------------
|
||||
|
||||
.. include-build-file:: inc/esp_async_color_convert.inc
|
||||
@@ -0,0 +1,344 @@
|
||||
============================
|
||||
Asynchronous CRC Calculation
|
||||
============================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
This document introduces the features of the Asynchronous CRC (Async CRC) driver in ESP-IDF. The table of contents is as follows:
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
The Async CRC driver provides hardware-accelerated CRC calculation using the General DMA peripheral. It supports both AHB-GDMA and AXI-GDMA backends, offering flexible CRC computation with configurable polynomial, initial value, bit reversal options, and final XOR processing.
|
||||
|
||||
Key capabilities include:
|
||||
|
||||
- Hardware-accelerated CRC calculation using General DMA
|
||||
- Support for 8-bit, 16-bit, and 32-bit CRC algorithms
|
||||
- Asynchronous API with callback notifications
|
||||
- Blocking API with timeout support
|
||||
- Request queuing with configurable backlog size
|
||||
- Support for both AHB and AXI DMA backends
|
||||
|
||||
Application scenarios include:
|
||||
|
||||
- Data integrity verification for communication protocols
|
||||
- File and firmware checksum calculation
|
||||
- Network packet validation
|
||||
- Storage data verification
|
||||
|
||||
Quick Start
|
||||
===========
|
||||
|
||||
This section provides a concise overview of how to use the Async CRC driver. Through practical examples, it demonstrates how to initialize the driver, configure CRC parameters, and perform both asynchronous and blocking CRC calculations.
|
||||
|
||||
The typical usage flow for async CRC is as follows:
|
||||
|
||||
.. blockdiag::
|
||||
:scale: 100%
|
||||
:caption: Async CRC driver's general usage flow (click to enlarge)
|
||||
:align: center
|
||||
|
||||
blockdiag {
|
||||
default_fontsize = 14;
|
||||
node_width = 250;
|
||||
node_height = 80;
|
||||
class emphasis [color = pink, style = dashed];
|
||||
|
||||
install [label="esp_async_crc_install_gdma_*"];
|
||||
calc [label="esp_async_crc_calc"];
|
||||
wait [label="Wait for callback"];
|
||||
process [label="Process result"];
|
||||
uninstall [label="esp_async_crc_uninstall"];
|
||||
|
||||
install -> calc -> wait -> process;
|
||||
process -> calc [folded];
|
||||
calc -> uninstall [folded];
|
||||
}
|
||||
|
||||
Creating and Installing the Driver
|
||||
----------------------------------
|
||||
|
||||
First, you need to install the Async CRC driver. The driver supports both AHB-GDMA and AXI-GDMA backends, depending on your chip's capabilities:
|
||||
|
||||
.. code:: c
|
||||
|
||||
async_crc_handle_t crc_hdl = NULL;
|
||||
async_crc_config_t config = {
|
||||
.backlog = 8, // Maximum pending requests in queue
|
||||
.dma_burst_size = 16, // DMA burst size in bytes
|
||||
};
|
||||
|
||||
// Install with AHB-GDMA backend (if available)
|
||||
ESP_ERROR_CHECK(esp_async_crc_install_gdma_ahb(&config, &crc_hdl));
|
||||
|
||||
// Or install with AXI-GDMA backend (if available)
|
||||
// ESP_ERROR_CHECK(esp_async_crc_install_gdma_axi(&config, &crc_hdl));
|
||||
|
||||
.. note::
|
||||
|
||||
**Choosing Between AHB-GDMA and AXI-GDMA Backends**
|
||||
|
||||
The backend choice depends on your chip's capabilities and performance requirements:
|
||||
|
||||
- **AHB-GDMA**: Available on most ESP chips. Connects to the AHB bus, suitable for general-purpose DMA operations. Best for:
|
||||
|
||||
- Standard performance requirements
|
||||
- Compatibility across most ESP chip variants
|
||||
|
||||
- **AXI-GDMA**: Available on higher-end ESP chips with AXI bus support. Provides higher bandwidth and better performance for memory-intensive operations. Best for:
|
||||
|
||||
- High-throughput CRC calculations
|
||||
- Processing large amounts of data
|
||||
- Applications requiring maximum performance
|
||||
- Accessing external memory (PSRAM) with better efficiency
|
||||
|
||||
When creating a driver instance, you need to configure:
|
||||
|
||||
- **backlog**: Maximum number of pending CRC requests that can be queued. Higher values use more memory but provide better throughput for bursty workloads.
|
||||
- **intr_priority**: DMA interrupt priority. Set to ``0`` to use the default low/medium priority, or set a non-zero value to request a specific interrupt priority.
|
||||
- **dma_burst_size**: DMA transfer burst size in bytes.
|
||||
|
||||
The driver handle ``crc_hdl`` is an opaque pointer that you use for all subsequent operations.
|
||||
|
||||
Performing Asynchronous CRC Calculation
|
||||
---------------------------------------
|
||||
|
||||
The async API allows you to queue CRC calculations without blocking:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static bool crc_complete_callback(async_crc_handle_t crc_hdl, async_crc_event_data_t *edata, void *cb_args)
|
||||
{
|
||||
uint32_t result = edata->crc_result;
|
||||
// Further process the CRC result
|
||||
// e.g., send to a task via queue, log it, etc.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure CRC parameters for CRC-32
|
||||
async_crc_params_t params = {
|
||||
.width = 32,
|
||||
.polynomial = 0x04C11DB7,
|
||||
.init_value = 0xFFFFFFFF,
|
||||
.final_xor_value = 0xFFFFFFFF,
|
||||
.reverse_input = true,
|
||||
.reverse_output = true,
|
||||
};
|
||||
|
||||
// Start async CRC calculation
|
||||
const char *data = "Hello, World!";
|
||||
size_t data_len = strlen(data);
|
||||
|
||||
ESP_ERROR_CHECK(esp_async_crc_calc(crc_hdl, data, data_len, ¶ms, crc_complete_callback, NULL));
|
||||
|
||||
The callback function is invoked in interrupt context when the CRC calculation completes. The callback receives:
|
||||
|
||||
- **crc_hdl**: The driver handle
|
||||
- **edata**: Event data containing the CRC result
|
||||
- **cb_args**: User-defined argument passed during ``esp_async_crc_calc``
|
||||
|
||||
Performing Blocking CRC Calculation
|
||||
-----------------------------------
|
||||
|
||||
For simpler use cases or when async operations are not required, use the blocking API:
|
||||
|
||||
.. code:: c
|
||||
|
||||
uint32_t crc_result = 0;
|
||||
async_crc_params_t params = {
|
||||
.width = 32,
|
||||
.polynomial = 0x04C11DB7,
|
||||
.init_value = 0xFFFFFFFF,
|
||||
.final_xor_value = 0xFFFFFFFF,
|
||||
.reverse_input = true,
|
||||
.reverse_output = true,
|
||||
};
|
||||
|
||||
const char *data = "Hello, World!";
|
||||
size_t data_len = strlen(data);
|
||||
|
||||
// Blocking CRC, wait indefinitely
|
||||
ESP_ERROR_CHECK(esp_crc_calc_blocking(crc_hdl, data, data_len, ¶ms, -1, &crc_result));
|
||||
|
||||
printf("CRC result: 0x%08X\n", crc_result);
|
||||
|
||||
The blocking API only supports ``timeout_ms = -1``, which means waiting indefinitely until the CRC calculation completes.
|
||||
|
||||
Uninstalling the Driver
|
||||
------------------------
|
||||
|
||||
When the driver is no longer needed:
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_ERROR_CHECK(esp_async_crc_uninstall(crc_hdl));
|
||||
|
||||
The uninstall function returns :c:macro:`ESP_ERR_INVALID_STATE` if there are pending operations or if the CRC engine is busy. Ensure all operations complete before uninstalling.
|
||||
|
||||
CRC Parameter Configuration
|
||||
============================
|
||||
|
||||
The Async CRC driver supports flexible CRC algorithm configuration through the :cpp:type:`async_crc_params_t` structure.
|
||||
|
||||
CRC Width
|
||||
---------
|
||||
|
||||
The :cpp:member:`async_crc_params_t::width` field specifies the CRC bit width:
|
||||
|
||||
- **8**: 8-bit CRC (e.g., CRC-8, CRC-8/MAXIM)
|
||||
- **16**: 16-bit CRC (e.g., CRC-16/CCITT, CRC-16/IBM)
|
||||
- **32**: 32-bit CRC (e.g., CRC-32, CRC-32/BZIP2)
|
||||
|
||||
Polynomial
|
||||
----------
|
||||
|
||||
The :cpp:member:`async_crc_params_t::polynomial` field specifies the CRC polynomial in hexadecimal format. Common polynomial values include:
|
||||
|
||||
- CRC-32: ``0x04C11DB7``
|
||||
- CRC-16/CCITT: ``0x1021``
|
||||
- CRC-16/IBM: ``0x8005``
|
||||
- CRC-8/MAXIM: ``0x31``
|
||||
|
||||
Initial Value
|
||||
-------------
|
||||
|
||||
The :cpp:member:`async_crc_params_t::init_value` field sets the initial CRC value before processing. Common initial values:
|
||||
|
||||
- ``0xFFFFFFFF`` for CRC-32
|
||||
- ``0x0000`` for many CRC-16 variants
|
||||
- ``0x00`` for many CRC-8 variants
|
||||
|
||||
Final XOR Value
|
||||
---------------
|
||||
|
||||
The :cpp:member:`async_crc_params_t::final_xor_value` field specifies a value to XOR with the final CRC result. This is commonly ``0xFFFFFFFF`` for CRC-32 but can be ``0x0000`` for some variants.
|
||||
|
||||
Bit Reversal Options
|
||||
--------------------
|
||||
|
||||
- :cpp:member:`async_crc_params_t::reverse_input` If true, reverses the bit order of each input byte before processing
|
||||
- :cpp:member:`async_crc_params_t::reverse_output` If true, reverses the bit order of the final CRC result before applying the final XOR
|
||||
|
||||
These options affect the reflection settings for different CRC algorithms.
|
||||
|
||||
Common CRC Configurations
|
||||
-------------------------
|
||||
|
||||
The following table lists common CRC configurations:
|
||||
|
||||
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
|
||||
| CRC Algorithm | Width | Polynomial | Initial Value | Final XOR | Reverse Input | Reverse Output|
|
||||
+================+==========+===============+===============+==================+===============+===============+
|
||||
| CRC-32 | 32 | 0x04C11DB7 | 0xFFFFFFFF | 0xFFFFFFFF | true | true |
|
||||
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
|
||||
| CRC-16/CCITT | 16 | 0x1021 | 0x0000 | 0x0000 | false | false |
|
||||
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
|
||||
| CRC-16/IBM | 16 | 0x8005 | 0x0000 | 0x0000 | true | true |
|
||||
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
|
||||
| CRC-8/MAXIM | 8 | 0x31 | 0x00 | 0x00 | true | true |
|
||||
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
|
||||
|
||||
Thread Safety
|
||||
=============
|
||||
|
||||
The Async CRC driver is designed to be thread-safe and can be used from multiple tasks. The driver features a **race-free Finite State Machine (FSM)** architecture that ensures thread safety and proper handling of concurrent CRC requests.
|
||||
|
||||
Thread Safety Guarantees
|
||||
------------------------
|
||||
|
||||
- All public APIs can be called from different tasks simultaneously
|
||||
- The driver uses atomic operations and critical sections for internal state protection
|
||||
- Request queuing ensures that concurrent calls are properly serialized
|
||||
|
||||
ISR Context Restrictions
|
||||
------------------------
|
||||
|
||||
Neither the async API nor the blocking API can be called from interrupt context. Specifically:
|
||||
|
||||
- :cpp:func:`esp_async_crc_calc`: Involves memory allocation/free, DMA preparations, and logging functions that are not ISR-safe
|
||||
- :cpp:func:`esp_crc_calc_blocking`: Uses synchronization primitives that may block
|
||||
|
||||
Callback Restrictions
|
||||
---------------------
|
||||
|
||||
The callback function (:cpp:type:`async_crc_isr_cb_t`) is executed in interrupt context. Therefore:
|
||||
|
||||
- Do **not** perform blocking operations (e.g., ``vTaskDelay``, ``xQueueSend`` with timeout)
|
||||
- Keep execution time minimal to avoid impacting system interrupt latency
|
||||
- Do **not** allocate memory using ``malloc`` or similar functions
|
||||
- Use only ISR-safe FreeRTOS APIs (e.g., ``xQueueSendFromISR``, ``xSemaphoreGiveFromISR``)
|
||||
- Return ``true`` if the callback wakes a high-priority task
|
||||
|
||||
Example of callback using queue:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static bool crc_callback(async_crc_handle_t crc_hdl, async_crc_event_data_t *edata, void *cb_args)
|
||||
{
|
||||
QueueHandle_t queue = (QueueHandle_t)cb_args;
|
||||
BaseType_t high_task_awoken = pdFALSE;
|
||||
// Send result to a task via ISR-safe queue
|
||||
xQueueSendFromISR(queue, &edata->crc_result, &high_task_awoken);
|
||||
return high_task_awoken == pdTRUE;
|
||||
}
|
||||
|
||||
Buffer Requirements
|
||||
===================
|
||||
|
||||
The Async CRC driver has specific requirements for data buffers.
|
||||
|
||||
Memory Type
|
||||
-----------
|
||||
|
||||
Data buffers can be in internal memory (DRAM/IRAM) or external memory (PSRAM, Flash). The driver automatically handles both:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Internal RAM
|
||||
static char internal_data[] = "Data in internal RAM";
|
||||
esp_async_crc_calc(crc_hdl, internal_data, strlen(internal_data), ¶ms, callback, NULL);
|
||||
|
||||
// External Flash
|
||||
static const char *flash_data = "Data in external Flash";
|
||||
esp_async_crc_calc(crc_hdl, flash_data, strlen(flash_data), ¶ms, callback, NULL);
|
||||
|
||||
Performance Considerations
|
||||
==========================
|
||||
|
||||
Backlog Configuration
|
||||
---------------------
|
||||
|
||||
The ``backlog`` configuration affects performance:
|
||||
|
||||
- **Small backlog** (4-8): Lower memory usage, may cause backpressure under high load
|
||||
- **Large backlog** (16+): Better throughput for bursty workloads, higher memory usage
|
||||
|
||||
Choose based on your application's memory constraints and workload pattern.
|
||||
|
||||
DMA Burst Size
|
||||
--------------
|
||||
|
||||
The ``dma_burst_size`` affects DMA transfer efficiency:
|
||||
|
||||
- Larger burst sizes can improve throughput
|
||||
- Typical values: 16, 32, 64 bytes
|
||||
|
||||
The optimal value depends on your chip's DMA controller capabilities.
|
||||
|
||||
Application Examples
|
||||
====================
|
||||
|
||||
- :example:`peripherals/dma/async_crc` demonstrates how to use the Async CRC driver through an interactive console CLI.
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
Async CRC Driver Functions
|
||||
--------------------------
|
||||
|
||||
.. include-build-file:: inc/esp_async_crc.inc
|
||||
@@ -0,0 +1,96 @@
|
||||
Asynchronous Memory Copy
|
||||
========================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
{IDF_TARGET_NAME} has a DMA engine which can help to offload internal memory copy operations from the CPU in an asynchronous way.
|
||||
|
||||
The async memcpy API wraps all DMA configurations and operations. The signature of :cpp:func:`esp_async_memcpy` is almost the same as the standard libc ``memcpy`` function.
|
||||
|
||||
The DMA allows multiple memory copy requests to be queued up before the first one is completed, which allows overlap of computation and memory copy. Moreover, it is still possible to know the exact time when a memory copy request is completed by registering an event callback.
|
||||
|
||||
|
||||
Configure and Install Driver
|
||||
----------------------------
|
||||
|
||||
There are several ways to install the async memcpy driver, depending on the underlying DMA engine:
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_CP_DMA_SUPPORTED: - :cpp:func:`esp_async_memcpy_install_cpdma` is used to install the async memcpy driver based on the CP DMA engine.
|
||||
:SOC_AHB_GDMA_SUPPORTED: - :cpp:func:`esp_async_memcpy_install_gdma_ahb` is used to install the async memcpy driver based on the AHB GDMA engine.
|
||||
:SOC_AXI_GDMA_SUPPORTED: - :cpp:func:`esp_async_memcpy_install_gdma_axi` is used to install the async memcpy driver based on the AXI GDMA engine.
|
||||
- :cpp:func:`esp_async_memcpy_install` is a generic API to install the async memcpy driver with a default DMA engine. If the SoC has the CP DMA engine, the default DMA engine is CP DMA. Otherwise, the default DMA engine is AHB GDMA.
|
||||
|
||||
Driver configuration is described in :cpp:type:`async_memcpy_config_t`:
|
||||
|
||||
* :cpp:member:`backlog`: This is used to configure the maximum number of memory copy transactions that can be queued up before the first one is completed. If this field is set to zero, then the default value 4 will be applied.
|
||||
* :cpp:member:`dma_burst_size`: Set the burst size in a DMA burst transfer.
|
||||
* :cpp:member:`flags`: This is used to enable some special driver features.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
async_memcpy_config_t config = ASYNC_MEMCPY_DEFAULT_CONFIG();
|
||||
// update the maximum data stream supported by underlying DMA engine
|
||||
config.backlog = 8;
|
||||
async_memcpy_handle_t driver = NULL;
|
||||
ESP_ERROR_CHECK(esp_async_memcpy_install(&config, &driver)); // install driver with default DMA engine
|
||||
|
||||
Send Memory Copy Request
|
||||
------------------------
|
||||
|
||||
:cpp:func:`esp_async_memcpy` is the API to send memory copy request to DMA engine. It must be called after driver is installed successfully. This API is thread safe, so it can be called from different tasks.
|
||||
|
||||
Different from the libc version of ``memcpy``, you can optionally pass a callback to :cpp:func:`esp_async_memcpy`, so that you can be notified when the memory copy is finished. Note that the callback is executed in the ISR context, please make sure you will not call any blocking functions in the callback.
|
||||
|
||||
The prototype of the callback function is :cpp:type:`async_memcpy_isr_cb_t`. The callback function should only return true if it wakes up a high priority task by RTOS APIs like :cpp:func:`xSemaphoreGiveFromISR`.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// Callback implementation, running in ISR context
|
||||
static bool my_async_memcpy_cb(async_memcpy_handle_t mcp_hdl, async_memcpy_event_t *event, void *cb_args)
|
||||
{
|
||||
SemaphoreHandle_t sem = (SemaphoreHandle_t)cb_args;
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
xSemaphoreGiveFromISR(semphr, &high_task_wakeup); // high_task_wakeup set to pdTRUE if some high priority task unblocked
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
// Create a semaphore used to report the completion of async memcpy
|
||||
SemaphoreHandle_t semphr = xSemaphoreCreateBinary();
|
||||
|
||||
// Called from user's context
|
||||
ESP_ERROR_CHECK(esp_async_memcpy(driver_handle, to, from, copy_len, my_async_memcpy_cb, my_semaphore));
|
||||
// Do something else here
|
||||
xSemaphoreTake(my_semaphore, portMAX_DELAY); // Wait until the buffer copy is done
|
||||
|
||||
For simpler use cases where the task only needs to wait until one copy finishes, use :cpp:func:`esp_memcpy_blocking`. This API is built on top of the async request path and waits internally for the completion callback.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
ESP_ERROR_CHECK(esp_memcpy_blocking(driver_handle, to, from, copy_len, -1));
|
||||
|
||||
The blocking API must not be called from ISR context. Currently, it only supports ``timeout_ms = -1``, which means waiting indefinitely until the memory copy completes.
|
||||
|
||||
|
||||
Uninstall Driver
|
||||
----------------
|
||||
|
||||
:cpp:func:`esp_async_memcpy_uninstall` is used to uninstall asynchronous memcpy driver. It is not necessary to uninstall the driver after each memcpy operation. If you know your application will not use this driver anymore, then this API can recycle the memory and other hardware resources for you.
|
||||
|
||||
.. only:: SOC_ETM_SUPPORTED and SOC_GDMA_SUPPORT_ETM
|
||||
|
||||
ETM Event
|
||||
---------
|
||||
|
||||
Async memory copy is able to generate an event when one async memcpy operation is done. This event can be used to interact with the :doc:`ETM </api-reference/peripherals/etm>` module. You can call :cpp:func:`esp_async_memcpy_new_etm_event` to get the ETM event handle.
|
||||
|
||||
For how to connect the event to an ETM channel, please refer to the :doc:`ETM </api-reference/peripherals/etm>` documentation.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_async_memcpy.inc
|
||||
@@ -0,0 +1,245 @@
|
||||
BitScrambler Driver
|
||||
========================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The BitScrambler is a peripheral that applies various data transformation to a DMA stream using an user-supplied program. ESP-IDF provides an assembler, build system, and driver support for BitScrambler programs. The BitScrambler peripheral in the {IDF_TARGET_NAME} has independent TX and RX channels; both can be linked to the same or different peripherals.
|
||||
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
.. list::
|
||||
|
||||
- :ref:`bitscrambler-assembly` covers how a BitScrambler assembly program is structured.
|
||||
- :ref:`bitscrambler-build` covers how BitScrambler programs are integrated in the ESP-IDF build system.
|
||||
- :ref:`bitscrambler-load` covers how to allocate BitScrambler instances and how to load a program into them.
|
||||
- :ref:`bitscrambler-loopback` covers how to use the BitScrambler in loopback mode.
|
||||
|
||||
.. _bitscrambler-assembly:
|
||||
|
||||
BitScrambler Assembly
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The operations a BitScrambler performs on the data in the DMA stream are defined in a BitScrambler program. As a BitScrambler program is a binary blob that is hard to write by hand, ESP-IDF includes an assembler. This assembler converts easier-to-write text files into BitScrambler binary programs.
|
||||
|
||||
BitScrambler assembly files consist of comments, labels, instruction bundles, meta-instructions. The assembler ignores comments; Labels define a location in the program; Instructions can, e.g., jump to the location indicated by a label. Instruction bundles are a collection of sub-instructions that get assembled into one 257-bit binary BitScrambler instruction. Meta-instructions define global BitScrambler configuration, like the amount of trailing bytes, the prefetch mode, or the contents of the LUT RAM.
|
||||
|
||||
BitScrambler assembly files are case-insensitive as well as not sensitive to indentation. This documentation uses upper and lower caps for readability, but the assembler itself doesn't care. Fields that can contain an integer normally are given in a base-10 number, but can also contain a hex value if prepended with ``0x`` or a binary value if prepended with ``0b``.
|
||||
|
||||
Comments
|
||||
~~~~~~~~
|
||||
|
||||
A comment starts with a ``#`` and extends to the end of the line. It can appear anywhere a space is allowed, including within instruction bundles between sub-instructions.
|
||||
|
||||
Labels
|
||||
~~~~~~
|
||||
|
||||
Any string of (non-whitespace) characters followed by a colon is a label. The label is a symbolic reference to the next instruction bundle in the assembly file. Note that a label cannot be part of an instruction bundle; it needs to be located before the start of the instruction bundle.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: asm
|
||||
|
||||
loop_back:
|
||||
set 0..3 4..7,
|
||||
set 4..7 0..3,
|
||||
read 8,
|
||||
write 8,
|
||||
jmp loop_back
|
||||
|
||||
The ``jmp`` opcode in the instruction bundle will jump back to the start of itself, meaning that this instruction bundle will be executed over and over again in a tight loop.
|
||||
|
||||
Instruction Bundle
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
An instruction bundle consists of sub-instructions separated by commas. The entire bundle is assembled into one 257-bit instruction that the BitScrambler will execute in a single clock cycle. This means all sub-instructions within the bundle run in parallel, regardless of their order in the assembly source. The bundle ends with the last sub-instruction that is not followed by a comma.
|
||||
|
||||
The specific details of the BitScrambler can be found in the *{IDF_TARGET_NAME} Technical Reference Manual* > *BitScrambler (BITSCRM)* [`PDF <{IDF_TARGET_TRM_EN_URL}#bitscrm>`__].
|
||||
|
||||
In summary, BitScrambler contains a 32-bit output register, where each bit can take the value from any bit in any of the input sources. The sources are:
|
||||
|
||||
- a 64-bit input register fed by the incoming DMA stream
|
||||
- two 16-bit counters
|
||||
- A 30 bits register that contains the output of various comparisons
|
||||
- a fixed high and low bit
|
||||
- the output of a look-up table (LUT) RAM
|
||||
- the value of the output register in the previous cycle
|
||||
|
||||
Sub-instructions
|
||||
""""""""""""""""
|
||||
|
||||
``set [output] [source_bits]``: Routes one or more source bits to output bits. Note that it's possible to route multiple bits using the ``..`` operator: for instance ``set 0..3 O4..O7`` will have the same effect as ``set 0 O4, set 1 O5, set 2 O6, set 3 O7``. The first argument is the output bit or output bit range; output bits are numbered from 0 to 31. The second argument is one or a range of `source bits`_. Note that any output bits that do not have a corresponding ``set`` sub-instruction in an instruction bundle will be set to a low logic level.
|
||||
|
||||
``write [n]``: After routing all output bits, take the least significant ``n`` output register bits and push them into the output DMA pipeline. ``n`` can be one of 0, 8, 16 or 32. If an instruction bundle does not have a ``write`` sub-instruction, it will be equivalent to a ``write 0``.
|
||||
|
||||
``read [n]``: After routing all output bits and writing to the output register, take ``n`` bits from the input DMA pipeline and push them into the 64-bit input register. ``n`` can be one of 0, 8, 16 or 32. These bits will be shifted into the input FIFO starting from the MSB. As an example, a ``read 16`` shifts bits 63–16 of the input register down to bits 47–0 and the new 16 bits read from the input DMA pipeline will occupy bits 63–48 in the input register. If an instruction bundle does not have a ``read`` sub-instruction, it will be equivalent to a ``read 0``.
|
||||
|
||||
opcode
|
||||
""""""
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
- ``LOOP(A|B) end_val ctr_add tgt``: If the selected counter (A or B) is smaller than end_val, add ``ctr_add`` to the selected counter (A or B) and jump to the label ``tgt``. If not, continue execution.
|
||||
- ``ADD(A|B)[H|L] val``: Add ``val`` to the selected counter. If ``H`` or ``L`` is appended, only the high or low 8-bit, respectively, of the counter is written back.
|
||||
- ``IF[N] source_bit tgt``: If the source bit `source_bit` is 1 (for IF) or zero (for IFN), jump to the label ``tgt``.
|
||||
- ``LDCTD(A|B)[H|L] val``: Load ``val`` into the indicated counter. If H or L is appended, only the high or low 8-bit, respectively, will be updated.
|
||||
- ``LDCTI(A|B)[H|L]``: Load the indicated counter (A or B) with bits 16-31 from the output register. If ``H`` or ``L`` is appended, only the high or low 8-bit, respectively, will be updated.
|
||||
- ``JMP tgt``: Unconditional jump to label ``tgt``. This is equal to ``IF h tgt``.
|
||||
- ``NOP``: No operation. This is equal to ``ADDA 0``.
|
||||
|
||||
.. only:: esp32c5
|
||||
|
||||
- ``LOOP(A|B) end_val ctr_add tgt``: If the selected counter (A or B) is smaller than end_val, add ``ctr_add`` to the selected counter (A or B) and jump to the label ``tgt``. If not, continue execution.
|
||||
- ``ADD(A|B)[H|L] val``: Add ``val`` to the selected counter. If ``H`` or ``L`` is appended, only the high or low 8-bit, respectively, of the counter is written back.
|
||||
- ``IF[N] source_bit tgt``: If the source bit `source_bit` is one (for IF) or zero (for IFN), jump to the label ``tgt``.
|
||||
- ``LDCTD(A|B)[H|L] val``: Load ``val`` into the indicated counter. If H or L is appended, only the high or low 8-bit, respectively, will be updated.
|
||||
- ``LDCTI(A|B)[H|L]``: Load the indicated counter (A or B) with bits 16-31 sent to the output register. If ``H`` or ``L`` is appended, only the high or low 8-bit, respectively, will be updated.
|
||||
- ``ADDCTI(A|B)[H|L]``: Add bits 16–31 sent to the output register to the indicated counter (A or B). If ``H`` or ``L`` is appended, only the high or low 8-bit, respectively, will be evaluated and updated.
|
||||
- ``JMP tgt``: Unconditional jump to label ``tgt``. This is equal to ``IF h tgt``.
|
||||
- ``NOP``: No operation. This is equal to ``ADDA 0``.
|
||||
|
||||
.. note::
|
||||
|
||||
Note that an instruction bundle can only contain one opcode, one ``read``, and one ``write``. It can contain multiple ``set`` instructions, although multiple ``set`` instruction cannot assign a value to the same output bits.
|
||||
|
||||
Source bits
|
||||
"""""""""""
|
||||
|
||||
The ``set`` and ``if`` / ``ifn`` instructions have a ``source_bit`` field. The following values can be put there:
|
||||
|
||||
- ``0`` – ``63``: The bit selected is sourced from the selected bit in the input register.
|
||||
- ``O0`` – ``O31``: The bit selected is sourced from the value the output register was assigned in the previous cycle.
|
||||
- ``A0`` – ``A15``: The bit selected is sourced from the selected bit in the A counter register.
|
||||
- ``B0`` – ``B15``: The bit selected is sourced from the selected bit in the B counter register.
|
||||
- ``L0`` – ``L31``: The bit selected is sourced from the output from the LUT RAM. As described in the Technical Reference Manual, the LUT RAM output is the LUT item at the position indicated by the most significant N bits of the bits routed to the output register in the previous cycle, with N being 9, 10 or 11 for a LUT width of 32, 16 or 8-bit respectively.
|
||||
- A condition compares (a portion of) counter B with bits that were routed to the output register in the previous cycle. These conditions consist of three parts:
|
||||
|
||||
1. The first part specifies whether compare all bits of counter B or only the high or low 8 bits:
|
||||
|
||||
- ``B``: Compare the entire B register
|
||||
- ``BH``: Compare the high 8 bits of the B register
|
||||
- ``BL``: Compare the low 8 bits of the B register
|
||||
|
||||
2. The second part is the comparison operator, which supports ``<=``, ``>``, or ``=``.
|
||||
3. The third part specifies the offset in the output register for comparison with the selected part of the B register:
|
||||
|
||||
- For 16-bit comparisons, the offset can be ``O0`` or ``O16``
|
||||
- For 8-bit comparisons, the offset can be ``O0``, ``O8``, ``O16`` or ``O24``
|
||||
|
||||
- ``H`` or ``L``. These sources are fixed-high or fixed-low.
|
||||
|
||||
.. note::
|
||||
|
||||
Note that not all sources can be used together in the same instruction. For instance, it is not possible to use a bit from one of the two counters as well as a bit from the upper 32 bits of the input FIFO in the same instruction bundle. The assembler will generate an error if an instruction bundle tries to do this anyway.
|
||||
|
||||
Example
|
||||
"""""""
|
||||
|
||||
An example BitScrambler program might look like this:
|
||||
|
||||
.. code:: asm
|
||||
|
||||
loop_back:
|
||||
set 0..3 4..7,
|
||||
set 4..7 0..3,
|
||||
read 8,
|
||||
write 8,
|
||||
jmp loop_back
|
||||
|
||||
|
||||
This program only has one instruction (as only the line with the ``jmp`` does not end in a comma). It takes the lower 4 bits of the data read from memory and sends it to the upper 4 bits of the first byte of the output register. It also takes the next 4 bits of the input register and sends it to the lower 4 bits of the output register. It then writes 8 bits (one byte) to the output, while reading 8 bits from the input. Finally, the program continues by jumping back to the start of the instruction. Note that this all is executed in one BitScrambler cycle, and as the sub-instructions all are part of the same instruction, they could be specified in any order within the instruction. The end result of this small BitScrambler program is that it takes in data, e.g., ``01 23 45 67`` and swaps the high and low nibble of every bytes, resulting in an output of ``10 32 54 76``.
|
||||
|
||||
|
||||
Meta-instructions
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Meta-instructions set global BitScrambler configuration. Meta-instructions are allowed anywhere in the assembly file (except within an instruction bundle) and may also affect the preceding assembly code due to their nature. Currently, two meta-instructions are defined: ``cfg`` sets a global BitScrambler setting, and ``lut`` defines lookup table RAM content.
|
||||
|
||||
|
||||
Global configuration meta-instructions
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
- ``cfg prefetch true|false``: If prefetch is set to ``true``, the BitScrambler will read 64 bits from the input DMA stream into the input register at startup. If set to ``false``, the input register is initialized to zero. This setting defaults to ``true`` if not specified. Please note, if the prefetch is enabled while the input stream can't provide at least 64 bits of data, the BitScrambler will hang.
|
||||
- ``cfg eof_on upstream|downstream``: After the input stream ends, the BitScrambler will still process a certain amount of 'trailing' dummy bytes so it can flush any data contained in its registers. This setting indicates from where the data will be counted: ``upstream`` makes the bitscrambler count the bytes being read, ``downstream`` makes it count the bytes being written. This defaults to ``upstream`` if not specified.
|
||||
- ``cfg trailing_bytes N``: This indicates how many dummy bytes will be read or written (depending on the ``eof_on`` setting) before the BitScrambler indicates an end-of-stream on its output. This defaults to ``0`` if not specified.
|
||||
- ``cfg lut_width_bits 8|16|32``: This selects the bus width of the LUT output RAM, in bits. The LUT can be 2048x8 bits, 1024x16 bits or 512x32 bits in size. This defaults to ``32`` if not specified.
|
||||
|
||||
|
||||
LUT content meta-instructions
|
||||
"""""""""""""""""""""""""""""
|
||||
|
||||
``lut`` instructions are used to specify the contents of the LUT RAM. This meta-instruction is followed by one or more numerical values, separated by spaces or commas. LUT RAM locations are defined in the order they're encountered in the assembly program; the first value is always stored at location 0, the second value encountered is always stored at location 1, etc. The amount of arguments to a LUT meta-instruction is arbitrary as LUT meta-instructions can always be broken up or merged. For instance, ``lut 1,2,3,4`` is the same as ``lut 1,2`` on one line and ``lut 3,4`` on the next line. Note that LUT values must be within range with respect to the value given to the ``cfg lut_width_bits`` configuration meta-statement.
|
||||
|
||||
.. _bitscrambler-build:
|
||||
|
||||
Build System Integration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The BitScrambler has full ESP-IDF build system support. A component (including the main component) can have BitScrambler assembly source files in its source directories. These files generally have the suffix ``.bsasm``. To assemble and link such a file into the main application, the CMakeLists.txt file for the component can call ``target_bitscrambler_add_src("assembly_file.bsasm")``. For instance, for an assembly file called ``my_program.bsasm``, a CMakeLists.txt file may look like this:
|
||||
|
||||
.. code:: cmake
|
||||
|
||||
idf_component_register(SRCS "main.c" "some-file.c"
|
||||
INCLUDE_DIRS "./include")
|
||||
|
||||
target_bitscrambler_add_src("my_program.bsasm")
|
||||
|
||||
To use the assembled BitScrambler program, you would refer to it as such:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Create a variable 'my_bitscrambler_program' that resolves to
|
||||
// the binary bitscrambler program.
|
||||
// 2nd arg is same as name of assembly file without ".bsasm"
|
||||
BITSCRAMBLER_PROGRAM(my_bitscrambler_program, "my_program");
|
||||
|
||||
[...]
|
||||
|
||||
bitscrambler_handle_t bs;
|
||||
[...create bitscrambler instance]
|
||||
bitscrambler_enable(bs);
|
||||
bitscrambler_load_program(bs, my_bitscrambler_program);
|
||||
|
||||
[...]
|
||||
|
||||
bitscrambler_disable(bs);
|
||||
|
||||
.. _bitscrambler-loopback:
|
||||
|
||||
Loopback Mode
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The BitScrambler supports a loopback mode which is useful for data transformations that do not involve a peripheral. The loopback mode occupies both the TX and RX channels of the BitScrambler, although only the TX BitScrambler actually executes code. Note that even if loopback mode does not involve a peripheral, one still needs to be selected; the peripheral does not need to be initialized or used, but if it is, its DMA features will be unavailable.
|
||||
|
||||
.. _bitscrambler-load:
|
||||
|
||||
Resource Allocation and Program Loading
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In loopback mode, a BitScrambler object is created using :cpp:func:`bitscrambler_loopback_create`. If there is a BitScrambler peripheral matching the requested characteristics, this function will return a handle to it. You can then use :cpp:func:`bitscrambler_load_program` to load a program into it, then call :cpp:func:`bitscrambler_loopback_run` to transform a memory buffer using the loaded program. You can call :cpp:func:`bitscrambler_loopback_run` any number of times; it's also permissible to use :cpp:func:`bitscrambler_load_program` to change programs between calls. Finally, to free the hardware resources and clean up memory, call :cpp:func:`bitscrambler_free`.
|
||||
|
||||
Integrating BitScrambler with Peripheral Drivers
|
||||
------------------------------------------------
|
||||
|
||||
The BitScrambler can be used alongside other peripheral modules (with GDMA interface support) to perform data transformation and transmission tasks. Currently, the following peripheral modules are integrated with the BitScrambler:
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_PARLIO_SUPPORTED: - **Parlio TX Driver**: The BitScrambler acts as a transmission layer decorator function, which can be dynamically enabled during runtime. For details, refer to :ref:`parlio-tx-bitscrambler-decorator`.
|
||||
:SOC_RMT_SUPPORT_DMA: - **RMT TX Driver**: The BitScrambler functions similarly to an RMT encoder. A dedicated encoder has been designed using the BitScrambler. For details, refer to :ref:`rmt-bitscrambler-encoder`.
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
* :example:`peripherals/bitscrambler` demonstrates how to use BitScrambler loopback mode to transform a buffer of data into a different format.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/bitscrambler.inc
|
||||
.. include-build-file:: inc/bitscrambler_loopback.inc
|
||||
.. include-build-file:: inc/bitscrambler_peri_select.inc
|
||||
@@ -0,0 +1,366 @@
|
||||
Camera Controller Driver
|
||||
========================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
{IDF_TARGET_NAME} has the following hardware designed for communication with external camera sensors:
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_MIPI_CSI_SUPPORTED: - MIPI Camera Serial Interface (MIPI CSI)
|
||||
:SOC_ISP_DVP_SUPPORTED: - Digital Video Port via ISP module (ISP DVP)
|
||||
:SOC_LCDCAM_CAM_SUPPORTED: - Digital Video Port via LCD_CAM module (LCD_CAM DVP)
|
||||
|
||||
The camera controller driver provides an interface for these hardware peripherals.
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
.. list::
|
||||
|
||||
- :ref:`cam-resource-allocation` – Describes how to allocate and configure camera controller instances, and how to release resources when no longer needed.
|
||||
- :ref:`cam-enable-disable` – Explains how to enable and disable a camera controller.
|
||||
- :ref:`cam-start-stop` – Details how to start and stop a camera controller.
|
||||
- :ref:`cam-receive` – Explains how to receive signals from a camera sensor.
|
||||
- :ref:`cam-callback` – Describes how to register user-defined event callback functions.
|
||||
- :ref:`cam-thread-safety` – Lists APIs that are thread-safe.
|
||||
- :ref:`cam-kconfig-options` – Lists supported Kconfig options that affect driver behavior.
|
||||
- :ref:`cam-iram-safe` – Provides guidance on making CSI interrupt and control functions work reliably when the cache is disabled.
|
||||
|
||||
.. _cam-resource-allocation:
|
||||
|
||||
Resource Allocation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Install the Camera Controller Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can implement the camera controller driver using one of the following methods:
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_MIPI_CSI_SUPPORTED: - :cpp:func:`esp_cam_new_csi_ctlr`
|
||||
:SOC_ISP_DVP_SUPPORTED: - :cpp:func:`esp_cam_new_isp_dvp_ctlr`
|
||||
:SOC_LCDCAM_CAM_SUPPORTED: - :cpp:func:`esp_cam_new_lcd_cam_ctlr`
|
||||
|
||||
.. only:: SOC_MIPI_CSI_SUPPORTED
|
||||
|
||||
You can implement a camera controller driver using the CSI peripheral. This requires configuration via :cpp:type:`esp_cam_ctlr_csi_config_t`.
|
||||
|
||||
After specifying :cpp:type:`esp_cam_ctlr_csi_config_t`, call :cpp:func:`esp_cam_new_csi_ctlr` to allocate and initialize a CSI camera controller handle. If successful, this function returns a CSI camera controller handle. See the example below.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include "esp_cam_ctlr.h"
|
||||
#include "esp_cam_ctlr_types.h"
|
||||
#include "esp_cam_ctlr_csi.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_cam_ctlr_csi_config_t csi_config = {
|
||||
.ctlr_id = 0,
|
||||
.h_res = MIPI_CSI_DISP_HSIZE,
|
||||
.v_res = MIPI_CSI_DISP_VSIZE_640P,
|
||||
.lane_bit_rate_mbps = MIPI_CSI_LANE_BITRATE_MBPS,
|
||||
.input_data_color_type = CAM_CTLR_COLOR_RAW8,
|
||||
.output_data_color_type = CAM_CTLR_COLOR_RGB565,
|
||||
.data_lane_num = 2,
|
||||
.byte_swap_en = false,
|
||||
.queue_items = 1,
|
||||
};
|
||||
|
||||
esp_cam_ctlr_handle_t handle = NULL;
|
||||
ESP_ERROR_CHECK(esp_cam_new_csi_ctlr(&csi_config, &handle));
|
||||
}
|
||||
|
||||
The CSI peripheral in {IDF_TARGET_NAME} requires a stable 2.5 V power supply. Refer to the schematic diagram to ensure the power supply pins are connected to 2.5 V before using the MIPI CSI driver.
|
||||
|
||||
.. only:: SOC_GP_LDO_SUPPORTED
|
||||
|
||||
In {IDF_TARGET_NAME}, you can power the CSI using the internal adjustable LDO. Connect the LDO channel output pin to the CSI power supply pin. Before initializing the CSI driver, use the API in :doc:`/api-reference/peripherals/ldo_regulator` to configure the LDO to output 2.5 V.
|
||||
|
||||
.. only:: SOC_ISP_DVP_SUPPORTED
|
||||
|
||||
You can implement a camera controller driver using the ISP DVP peripheral. This requires configuration via :cpp:type:`esp_cam_ctlr_isp_dvp_cfg_t`.
|
||||
|
||||
After specifying :cpp:type:`esp_cam_ctlr_isp_dvp_cfg_t`, call :cpp:func:`esp_cam_new_isp_dvp_ctlr` to allocate and initialize an ISP DVP camera controller handle. If successful, this function returns an ISP DVP camera controller handle. See the example below.
|
||||
|
||||
Before calling :cpp:func:`esp_cam_new_isp_dvp_ctlr`, create an ISP handle using :cpp:func:`esp_isp_new_processor`.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_cam_ctlr.h"
|
||||
#include "esp_cam_ctlr_isp_dvp.h"
|
||||
#include "driver/isp.h"
|
||||
|
||||
#define MIPI_CSI_DISP_HSIZE 800 // example value, replace with actual resolution
|
||||
#define MIPI_CSI_DISP_VSIZE 600 // example value, replace with actual resolution
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
isp_proc_handle_t isp_proc = NULL;
|
||||
esp_isp_processor_cfg_t isp_config = {
|
||||
.clk_hz = 120 * 1000 * 1000,
|
||||
.input_data_source = ISP_INPUT_DATA_SOURCE_DVP,
|
||||
.input_data_color_type = ISP_COLOR_RAW8,
|
||||
.output_data_color_type = ISP_COLOR_RGB565,
|
||||
.has_line_start_packet = false,
|
||||
.has_line_end_packet = false,
|
||||
.h_res = MIPI_CSI_DISP_HSIZE,
|
||||
.v_res = MIPI_CSI_DISP_VSIZE,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_new_processor(&isp_config, &isp_proc));
|
||||
|
||||
esp_cam_ctlr_handle_t cam_handle = NULL;
|
||||
esp_cam_ctlr_isp_dvp_cfg_t dvp_ctlr_config = {
|
||||
.data_width = 8,
|
||||
.data_io = {53, 54, 52, 0, 1, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
.pclk_io = 21,
|
||||
.hsync_io = 5,
|
||||
.vsync_io = 23,
|
||||
.de_io = 22,
|
||||
.io_flags.vsync_invert = 1,
|
||||
.queue_items = 10,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_cam_new_isp_dvp_ctlr(isp_proc, &dvp_ctlr_config, &cam_handle));
|
||||
}
|
||||
|
||||
.. only:: SOC_LCDCAM_CAM_SUPPORTED
|
||||
|
||||
You can implement a camera controller driver using the DVP port of LCD_CAM. This requires configuration via :cpp:type:`esp_cam_ctlr_dvp_config_t`.
|
||||
|
||||
:cpp:member:`esp_cam_ctlr_dvp_config_t::exexternal_xtal`: Set this to use an externally generated xclk. Otherwise, the camera driver generates it internally.
|
||||
|
||||
After specifying :cpp:type:`esp_cam_ctlr_lcd_cam_cfg_t`, call :cpp:func:`esp_cam_new_lcd_cam_ctlr` to allocate and initialize a DVP camera controller handle. If successful, this function returns a DVP camera controller handle. See the example below.
|
||||
|
||||
After calling :cpp:func:`esp_cam_new_dvp_ctlr`, allocate a camera buffer that meets alignment constraints, or call :cpp:func:`esp_cam_ctlr_alloc_buffer` to allocate automatically.
|
||||
|
||||
To configure format conversion, call :cpp:func:`esp_cam_ctlr_format_conversion`. The driver supports the following conversion types:
|
||||
|
||||
* YUV to RGB
|
||||
* RGB to YUV
|
||||
* YUV to YUV
|
||||
|
||||
Supported color ranges:
|
||||
* Full range: 0-255 for both RGB and YUV
|
||||
* Limited range: RGB 16-240, YUV Y:16-240, U-V:16-235
|
||||
|
||||
.. note::
|
||||
|
||||
- When :cpp:member:`esp_cam_ctlr_dvp_config_t::cam_data_width` is set to 8:
|
||||
|
||||
- The CAM_PCLK frequency is recommended to be less than 80 MHz.
|
||||
- If YUV-RGB format conversion is also configured via :cpp:func:`esp_cam_ctlr_format_conversion`, the CAM_PCLK frequency is recommended to be less than 60 MHz.
|
||||
|
||||
- When :cpp:member:`esp_cam_ctlr_dvp_config_t::cam_data_width` is set to 16:
|
||||
|
||||
- The CAM_PCLK frequency is recommended to be less than 40 MHz.
|
||||
- If YUV-RGB format conversion is also configured via :cpp:func:`esp_cam_ctlr_format_conversion`, the CAM_PCLK frequency is recommended to be less than 30 MHz.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_cam_ctlr.h"
|
||||
#include "esp_cam_ctlr_types.h"
|
||||
#include "esp_cam_ctlr_isp_dvp.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_cam_ctlr_handle_t cam_handle = NULL;
|
||||
|
||||
esp_cam_ctlr_dvp_pin_config_t pin_cfg = {
|
||||
.data_width = EXAMPLE_DVP_CAM_DATA_WIDTH,
|
||||
.data_io = {
|
||||
EXAMPLE_DVP_CAM_D0_IO,
|
||||
EXAMPLE_DVP_CAM_D1_IO,
|
||||
EXAMPLE_DVP_CAM_D2_IO,
|
||||
EXAMPLE_DVP_CAM_D3_IO,
|
||||
EXAMPLE_DVP_CAM_D4_IO,
|
||||
EXAMPLE_DVP_CAM_D5_IO,
|
||||
EXAMPLE_DVP_CAM_D6_IO,
|
||||
EXAMPLE_DVP_CAM_D7_IO,
|
||||
},
|
||||
.vsync_io = EXAMPLE_DVP_CAM_VSYNC_IO,
|
||||
.de_io = EXAMPLE_DVP_CAM_DE_IO,
|
||||
.pclk_io = EXAMPLE_DVP_CAM_PCLK_IO,
|
||||
.xclk_io = EXAMPLE_DVP_CAM_XCLK_IO, // Set XCLK pin to generate XCLK signal
|
||||
};
|
||||
|
||||
esp_cam_ctlr_dvp_config_t dvp_config = {
|
||||
.ctlr_id = 0,
|
||||
.clk_src = CAM_CLK_SRC_DEFAULT,
|
||||
.h_res = CONFIG_EXAMPLE_CAM_HRES,
|
||||
.v_res = CONFIG_EXAMPLE_CAM_VRES,
|
||||
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
|
||||
.output_data_color_type = CAM_CTLR_COLOR_RGB565,
|
||||
.dma_burst_size = 128,
|
||||
.pin = &pin_cfg,
|
||||
.bk_buffer_dis = 1,
|
||||
.xclk_freq = EXAMPLE_DVP_CAM_XCLK_FREQ_HZ,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_cam_new_dvp_ctlr(&dvp_config, &cam_handle));
|
||||
}
|
||||
|
||||
Uninstall the Camera Controller Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To release resources for a camera controller driver that is no longer needed, call :cpp:func:`esp_cam_ctlr_del`. This releases the underlying hardware.
|
||||
|
||||
.. _cam-enable-disable:
|
||||
|
||||
Enable and Disable the Camera Controller Driver
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Before operating the camera controller, enable the driver by calling :cpp:func:`esp_cam_ctlr_enable`. This switches the driver state from **init** to **enable**.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include "esp_cam_ctlr.h"
|
||||
#include "esp_cam_ctlr_types.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_cam_ctlr_handle_t handle;
|
||||
ESP_ERROR_CHECK(esp_cam_ctlr_enable(handle));
|
||||
}
|
||||
|
||||
To disable the driver and return to the **init** state, call :cpp:func:`esp_cam_ctlr_disable`.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_cam_ctlr.h"
|
||||
#include "esp_cam_ctlr_types.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_cam_ctlr_handle_t handle;
|
||||
ESP_ERROR_CHECK(esp_cam_ctlr_disable(handle));
|
||||
}
|
||||
|
||||
.. _cam-start-stop:
|
||||
|
||||
Start and Stop the Camera Controller Driver
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Before receiving signals from a camera sensor, start the driver by calling :cpp:func:`esp_cam_ctlr_start`. This switches the driver state from **enable** to **start**.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_cam_ctlr.h"
|
||||
#include "esp_cam_ctlr_types.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_cam_ctlr_handle_t handle = NULL;
|
||||
ESP_ERROR_CHECK(esp_cam_ctlr_start(handle));
|
||||
ESP_LOGI("CAM", "Camera controller started successfully");
|
||||
}
|
||||
|
||||
To stop the driver and return to the **enable** state, call :cpp:func:`esp_cam_ctlr_stop`.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_cam_ctlr.h"
|
||||
#include "esp_cam_ctlr_types.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_cam_ctlr_handle_t handle = NULL;
|
||||
ESP_ERROR_CHECK(esp_cam_ctlr_stop(handle));
|
||||
}
|
||||
|
||||
.. _cam-receive:
|
||||
|
||||
Receive Data from a Camera Sensor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To receive data from a camera sensor, call :cpp:func:`esp_cam_ctlr_receive`.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_cam_ctlr.h"
|
||||
#include "esp_cam_ctlr_types.h"
|
||||
|
||||
ESP_ERROR_CHECK(esp_cam_ctlr_receive(handle, &my_trans, ESP_CAM_CTLR_MAX_DELAY));
|
||||
|
||||
.. _cam-callback:
|
||||
|
||||
Register Event Callbacks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After the camera controller driver starts receiving data, it can generate events dynamically. To execute user-defined functions when events occur, register your callback function using :cpp:func:`esp_cam_ctlr_register_event_callbacks`. Supported event callbacks are listed in :cpp:type:`esp_cam_ctlr_evt_cbs_t`:
|
||||
|
||||
- :cpp:member:`esp_cam_ctlr_evt_cbs_t::on_get_new_trans` – Called after the driver finishes a transaction and attempts to get a new transaction descriptor. Also called in :cpp:func:`s_ctlr_csi_start`. If this callback does not provide a new transaction descriptor, the driver uses the internal backup buffer if the ``bk_buffer_dis`` flag is set.
|
||||
|
||||
- :cpp:member:`esp_cam_ctlr_evt_cbs_t::on_trans_finished` – Called when the driver finishes a transaction. This function runs in the ISR context. Ensure that it does not block (for example, only use FreeRTOS APIs with the ``ISR`` suffix).
|
||||
|
||||
.. _cam-thread-safety:
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The following factory functions are thread-safe and can be called from different RTOS tasks without additional locking:
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_MIPI_CSI_SUPPORTED: - :cpp:func:`esp_cam_new_csi_ctlr`
|
||||
:SOC_ISP_DVP_SUPPORTED: - :cpp:func:`esp_cam_new_isp_dvp_ctlr`
|
||||
- :cpp:func:`esp_cam_ctlr_del`
|
||||
|
||||
.. _cam-kconfig-options:
|
||||
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The following Kconfig options affect interrupt handler behavior when the cache is disabled:
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_MIPI_CSI_SUPPORTED: - :ref:`CONFIG_CAM_CTLR_MIPI_CSI_ISR_CACHE_SAFE`, see :ref:`cam-thread-safety` for details.
|
||||
:SOC_ISP_DVP_SUPPORTED: - :ref:`CONFIG_CAM_CTLR_ISP_DVP_ISR_CACHE_SAFE`, see :ref:`cam-thread-safety` for details.
|
||||
|
||||
.. _cam-iram-safe:
|
||||
|
||||
IRAM Safety
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
By default, CSI interrupts are delayed when the cache is disabled during flash write or erase operations. They are handled after the cache is enabled again.
|
||||
|
||||
The following Kconfig options:
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_MIPI_CSI_SUPPORTED: - :ref:`CONFIG_CAM_CTLR_MIPI_CSI_ISR_CACHE_SAFE`
|
||||
:SOC_ISP_DVP_SUPPORTED: - :ref:`CONFIG_CAM_CTLR_ISP_DVP_ISR_CACHE_SAFE`
|
||||
|
||||
- Enable the interrupt being serviced even when the cache is disabled.
|
||||
- Place all functions used by the ISR into IRAM.
|
||||
- Place driver object into DRAM (to avoid mapping to PSRAM).
|
||||
|
||||
This allows interrupts to run while the cache is disabled, but increases IRAM usage. Ensure that user callbacks and related code/data are IRAM-safe or DRAM-safe when the cache is disabled.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
* :example:`peripherals/camera/mipi_isp_dsi` – Demonstrates how to use the ``esp_driver_cam`` component to capture signals from a MIPI CSI camera sensor via the ISP module and display them on an LCD screen via a DSI interface.
|
||||
* :example:`peripherals/camera/dvp_isp_dsi` – Demonstrates how to use the ``esp_driver_cam`` component to capture signals from a DVP camera sensor via the ISP module and display them on an LCD screen via a DSI interface.
|
||||
* :example:`peripherals/camera/dvp_dsi` – Demonstrates how to use the ``esp_driver_cam`` component to capture DVP camera sensor data and display it on a MIPI DSI LCD.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_cam_ctlr.inc
|
||||
.. include-build-file:: inc/esp_cam_ctlr_types.inc
|
||||
.. include-build-file:: inc/esp_cam_ctlr_csi.inc
|
||||
.. include-build-file:: inc/esp_cam_ctlr_isp_dvp.inc
|
||||
@@ -0,0 +1,508 @@
|
||||
Capacitive Touch Sensor
|
||||
=========================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
{IDF_TARGET_TOUCH_SENSOR_VERSION:default="NOT_UPDATED", esp32="v1", esp32s2="v2", esp32s3="v2", esp32p4="v3", esp32h4="v3", esp32s31="v3"}
|
||||
|
||||
Introduction
|
||||
---------------
|
||||
|
||||
A touch sensor system is built on a substrate which carries electrodes and relevant connections under a protective flat surface. When the surface is touched, the capacitance variation is used to evaluate if the touch was valid.
|
||||
|
||||
The sensing pads can be arranged in different combinations (e.g., matrix, slider), so that a larger area or more points can be detected. The touch pad sensing process is under the control of a hardware-implemented finite-state machine (FSM) which is initiated by software or a dedicated hardware timer.
|
||||
|
||||
For design, operation, and control registers of a touch sensor, see **{IDF_TARGET_NAME} Technical Reference Manual** > **On-Chip Sensors and Analog Signal Processing** [`PDF <{IDF_TARGET_TRM_EN_URL}#sensor>`__].
|
||||
|
||||
In-depth design details of touch sensors and firmware development guidelines for the {IDF_TARGET_NAME} are available in `Touch Sensor Application Note <https://github.com/espressif/esp-iot-solution/blob/release/v1.0/documents/touch_pad_solution/touch_sensor_design_en.md>`_.
|
||||
|
||||
Overview of Capacitive Touch Sensor Versions
|
||||
-----------------------------------------------
|
||||
|
||||
+------------------+-----------+---------------------------------------------------------------------------------------+
|
||||
| Hardware Version | Chip | Main Features |
|
||||
+==================+===========+=======================================================================================+
|
||||
| V1 | ESP32 | Version 1, the channel value decreases when it is touched |
|
||||
+------------------+-----------+---------------------------------------------------------------------------------------+
|
||||
| V2 | ESP32-S2 | Version 2, the channel value increases when it is touched |
|
||||
| | | Supports hardware filter, benchmark, waterproof, proximity sensing and sleep wake-up |
|
||||
| +-----------+---------------------------------------------------------------------------------------+
|
||||
| | ESP32-S3 | Version 2, support proximity measurement done interrupt |
|
||||
+------------------+-----------+---------------------------------------------------------------------------------------+
|
||||
| V3 | ESP32-P4 | Version 3, support frequency hopping |
|
||||
| | ESP32-H4 | |
|
||||
| | ESP32-S31 | |
|
||||
+------------------+-----------+---------------------------------------------------------------------------------------+
|
||||
|
||||
Measurement Principle
|
||||
---------------------
|
||||
|
||||
The touch sensor will charge and discharge the touch channel by the internal current or voltage bias. Due to the internal capacitance and the stray capacitance in the circuit, the signals on the touch pins will present as a sawtooth wave. When the finger is approaching or touched on the touch pad, the capacitance of the touch channel increases, which leads to a slower charge and discharge, and a longer sawtooth period.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
The touch sensor charges and discharges the touch channel within a fixed time and counts the number of charge-discharge cycles, and the count result serves as the raw data. The duration of a single charge-discharge measurement can be specified by :cpp:member:`touch_sensor_sample_config_t::charge_times`, and the interval between two measurements can be specified by :cpp:member:`touch_sensor_config_t::meas_interval_us`.
|
||||
|
||||
.. figure:: ../../../_static/touch_pad-measurement-parameters.jpg
|
||||
:align: center
|
||||
:alt: Touch Pad - relationship between measurement parameters
|
||||
:figclass: align-center
|
||||
|
||||
Touch Sensor Working Principle
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
The touch sensor counts the number of clock cycles spent for a fixed number of charge-discharge cycles, and the count result serves as the raw data. The number of charge-discharge cycles for a single measurement can be specified by :cpp:member:`touch_sensor_sample_config_t::charge_duration_ms`, and the interval between two measurements can be specified by :cpp:member:`touch_sensor_config_t::meas_interval_us`.
|
||||
|
||||
.. figure:: ../../../_static/touch_pad-measurement-parameters-version2.png
|
||||
:align: center
|
||||
:alt: Touch Pad - relationship between measurement parameters
|
||||
:figclass: align-center
|
||||
|
||||
Touch Sensor Working Principle
|
||||
|
||||
Overview of Touch Sensor Channels
|
||||
------------------------------------
|
||||
|
||||
.. include:: cap_touch_sens/{IDF_TARGET_PATH_NAME}.inc
|
||||
:start-after: touch-chan-mapping
|
||||
:end-before: ---
|
||||
|
||||
Terminology in the Driver
|
||||
----------------------------
|
||||
|
||||
- **Touch Sensor Controller**: The controller of the touch sensor, responsible for configuring and managing the touch sensor.
|
||||
- **Touch Sensor Channel**: A specific touch sensor sampling channel. A touch sensor module has multiple touch channels, which are usually connected to the touch pad for measuring the capacitance change. In the driver, sampling of **one** channel is called one ``measurement`` and the scanning of **all** registered channels is called one ``scan``.
|
||||
|
||||
.. only:: not esp32 and not esp32s2 and not esp32s3
|
||||
|
||||
- **Touch Sensor Sampling Configuration**: Touch sensor sampling configuration refers to all the hardware configurations that related to the sampling. It can determine how the touch channels sample by setting the number of charging times, charging frequency, measurement interval, etc. The {IDF_TARGET_NAME} supports multiple sets of sample configuration, which means it can support frequency hopping.
|
||||
|
||||
.. only:: esp32 or esp32s2 or esp32s3
|
||||
|
||||
- **Touch Sensor Sampling Configuration**: Touch sensor sampling configuration refers to all the hardware configurations that related to the sampling. It can determine how the touch channels sample by setting the number of charging times, charging frequency, measurement interval, etc. The {IDF_TARGET_NAME} only support one set of sample configuration, so it doesn't support frequency hopping.
|
||||
|
||||
File Structure
|
||||
-----------------
|
||||
|
||||
.. figure:: ../../../_static/diagrams/cap_touch_sens/touch_file_structure.svg
|
||||
:align: center
|
||||
:alt: File Structure of Touch Sensor Driver
|
||||
|
||||
File Structure of Touch Sensor Driver
|
||||
|
||||
Finite-state Machine
|
||||
---------------------
|
||||
|
||||
The following diagram shows the state machine of the touch sensor driver, which describes the driver state after calling a function, and the constraint of the state transition.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/cap_touch_sens/touch_state_machine.svg
|
||||
:align: center
|
||||
:alt: Finite-state Machine of Touch Sensor Driver
|
||||
|
||||
Finite-state Machine of Touch Sensor Driver
|
||||
|
||||
The diagram above is the finite-state machine of the touch sensor driver, which describes how the state transferred by invoking different APIs. ``<other_configurations>`` in the diagram stands for the other optional configurations, like reconfigurations to the touch sensor controller or channels, callback registration, filter, and so on.
|
||||
|
||||
.. note::
|
||||
|
||||
:cpp:func:`touch_channel_read_data` can be called at any time after the channel is registered (i.e., since ``INIT`` state), but please take care of the validation of the data.
|
||||
|
||||
Functionality Introduction
|
||||
------------------------------
|
||||
|
||||
Categorized by functionality, the APIs of Capacitive Touch Sensor mainly include:
|
||||
|
||||
.. list::
|
||||
|
||||
- :ref:`touch-ctrl`
|
||||
- :ref:`touch-chan`
|
||||
- :ref:`touch-filter`
|
||||
- :ref:`touch-callback`
|
||||
- :ref:`touch-enable`
|
||||
- :ref:`touch-conti-scan`
|
||||
- :ref:`touch-oneshot-scan`
|
||||
- :ref:`touch-read`
|
||||
:SOC_TOUCH_SUPPORT_BENCHMARK: - :ref:`touch-benchmark`
|
||||
:SOC_TOUCH_SUPPORT_WATERPROOF: - :ref:`touch-waterproof`
|
||||
:SOC_TOUCH_SUPPORT_PROX_SENSING: - :ref:`touch-prox-sensing`
|
||||
:SOC_TOUCH_SUPPORT_SLEEP_WAKEUP: - :ref:`touch-sleep-wakeup`
|
||||
:SOC_TOUCH_SUPPORT_DENOISE_CHAN: - :ref:`touch-denoise-chan`
|
||||
|
||||
.. _touch-ctrl:
|
||||
|
||||
Touch Sensor Controller Management
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Touch Sensor is controlled by controller handle :cpp:type:`touch_sensor_handle_t`, it can be initialized and allocated by :cpp:func:`touch_sensor_new_controller`.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// Some target has multiple sets of sample configuration can be set, here take one for example
|
||||
#define SAMPLE_NUM 1
|
||||
touch_sensor_handle_t sens_handle = NULL;
|
||||
// sample configuration
|
||||
touch_sensor_sample_config_t sample_cfg[SAMPLE_NUM] = {
|
||||
// Specify sample configuration or apply the default sample configuration via `TOUCH_SENSOR_Vn_DEFAULT_SAMPLE_CONFIG`
|
||||
// ...
|
||||
};
|
||||
// Use the default touch controller configuration
|
||||
touch_sensor_config_t touch_cfg = TOUCH_SENSOR_DEFAULT_BASIC_CONFIG(SAMPLE_NUM, sample_cfg);
|
||||
// Allocate a new touch sensor controller handle
|
||||
ESP_ERROR_CHECK(touch_sensor_new_controller(&touch_cfg, &sens_handle));
|
||||
|
||||
To delete the controller handle and free the software and hardware resources, please call :cpp:func:`touch_sensor_del_controller`. But note that you need to delete the other resources that based on the controller first, like the registered touch channels, otherwise it can't be deleted directly.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
ESP_ERROR_CHECK(touch_sensor_del_controller(sens_handle));
|
||||
|
||||
You can also update the configurations via :cpp:func:`touch_sensor_reconfig_controller` before the controller is enabled.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
touch_sensor_config_t touch_cfg = {
|
||||
// New controller configurations
|
||||
// ...
|
||||
};
|
||||
ESP_ERROR_CHECK(touch_sensor_reconfig_controller(sens_handle, &touch_cfg));
|
||||
|
||||
.. _touch-chan:
|
||||
|
||||
Touch Sensor Channel Management
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are multiple touch channels in the touch sensor module, the touch sensor channel is controlled by the channel handle :cpp:type:`touch_channel_handle_t`. It can be initialized and allocated by :cpp:func:`touch_sensor_new_channel`.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// ...
|
||||
touch_channel_config_t chan_cfg = {
|
||||
// Touch channel configurations
|
||||
// ...
|
||||
};
|
||||
touch_channel_handle_t chan_handle = NULL;
|
||||
int chan_id = 0;
|
||||
// Allocate a new touch sensor controller handle
|
||||
ESP_ERROR_CHECK(touch_sensor_new_channel(sens_handle, chan_id, &chan_cfg, &chan_handle));
|
||||
|
||||
To delete the touch channel handle and free the software and hardware resources, please call :cpp:func:`touch_sensor_del_channel`.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
ESP_ERROR_CHECK(touch_sensor_del_channel(chan_handle));
|
||||
|
||||
You can also update the configurations via :cpp:func:`touch_sensor_reconfig_channel` before the controller is enabled.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
touch_channel_config_t chan_cfg = {
|
||||
// New touch channel configurations
|
||||
// ...
|
||||
};
|
||||
ESP_ERROR_CHECK(touch_sensor_reconfig_channel(chan_handle, &chan_cfg));
|
||||
|
||||
.. _touch-filter:
|
||||
|
||||
Filter Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The filter can help to increase the stability in different use cases. The filter can be registered by calling :cpp:func:`touch_sensor_config_filter` and specify the configurations :cpp:type:`touch_sensor_filter_config_t`. These configurations mainly determine how to filter and update the benchmark and read data. Please note that all touch channels will share this filter.
|
||||
|
||||
To deregister the filter, you can call :cpp:func:`touch_sensor_config_filter` again, and set the second parameter (i.e. :cpp:type:`touch_sensor_filter_config_t` pointer) to ``NULL``.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
The touch sensor version {IDF_TARGET_TOUCH_SENSOR_VERSION} does not natively support the hardware filter, but the driver can set up a periodically triggered software filter based on ``esp_timer``. The interval for the software filter can be specified by :cpp:member:`touch_sensor_filter_config_t::interval_ms`. Additionally, the :cpp:member:`touch_sensor_filter_config_t::data_filter_fn` supports to specify a custom filtering function. If there are no special filter requirements, this interface can be set to ``NULL`` to use the default filter in the driver.
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
The touch sensor version {IDF_TARGET_TOUCH_SENSOR_VERSION} supports the hardware filter. The filtering and updating strategy for the benchmark can be configured by :cpp:member:`touch_sensor_filter_config_t::benchmark`, while the filtering of read values can be configured by :cpp:member:`touch_sensor_filter_config_t::data`.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// ...
|
||||
touch_sensor_filter_config_t filter_config = {
|
||||
// Filter configurations
|
||||
// ...
|
||||
};
|
||||
// Register the filter
|
||||
ESP_ERROR_CHECK(touch_sensor_config_filter(sens_handle, &filter_config));
|
||||
// ...
|
||||
// Deregister the filter
|
||||
ESP_ERROR_CHECK(touch_sensor_config_filter(sens_handle, NULL));
|
||||
|
||||
.. _touch-callback:
|
||||
|
||||
Callback
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`touch_sensor_register_callbacks` to register the touch sensor event callbacks. Once the touch sensor events (like ``on_active``, ``on_inactive``) trigger, the corresponding callbacks will be invoked, so that to deal with the event in the upper application.
|
||||
|
||||
For the general example, when the measured data of the current touch channel exceed the ``benchmark`` + ``active_threshold``, this channel is activated, and the driver will call ``on_active`` callback to inform the application layer. Similar, when the active channel measured a lower data than ``benchmark`` + ``active_threshold``, then this channel will be inactivated, and ``on_inactive`` will be called to inform this channel is released.
|
||||
|
||||
.. note::
|
||||
|
||||
To ensure the stability of the triggering and releasing, ``active_hysteresis`` and ``debounce_cnt`` can be configured to avoid the frequent triggering that caused by jitter and noise.
|
||||
|
||||
Please refer to :cpp:type:`touch_event_callbacks_t` for the details about the supported callbacks.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
touch_event_callbacks_t callbacks = {
|
||||
.on_active = example_touch_on_active_cb,
|
||||
// Other callbacks
|
||||
// ...
|
||||
};
|
||||
// Register callbacks
|
||||
ESP_ERROR_CHECK(touch_sensor_register_callbacks(sens_handle, &callbacks, NULL));
|
||||
|
||||
// To deregister callbacks, set the corresponding callback to NULL
|
||||
callbacks.on_active = NULL;
|
||||
// Other callbacks to deregister
|
||||
// ...
|
||||
ESP_ERROR_CHECK(touch_sensor_register_callbacks(sens_handle, &callbacks, NULL));
|
||||
|
||||
.. _touch-enable:
|
||||
|
||||
Enable and Disable
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After finished the configuration of the touch controller and touch channels, :cpp:func:`touch_sensor_enable` can be called to enable the touch sensor controller. It will enter ``READY`` status and power on the registered channels, then you can start scanning and sampling the touch data. Note that you can only do scanning and reading operation once the controller is enabled. If you want to update the controller or channel configurations, you need to call :cpp:func:`touch_sensor_disable` first.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// Enable touch sensor
|
||||
ESP_ERROR_CHECK(touch_sensor_enable(sens_handle));
|
||||
// ...
|
||||
// Disable touch sensor
|
||||
ESP_ERROR_CHECK(touch_sensor_disable(sens_handle));
|
||||
|
||||
.. _touch-conti-scan:
|
||||
|
||||
Continuous Scan
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
With the touch controller enabled, :cpp:func:`touch_sensor_start_continuous_scanning` can be called to start the continuous scanning to all the registered touch channels. The read data of these touch channels will be updated automatically in each scan. Calling :cpp:func:`touch_sensor_stop_continuous_scanning` can stop the continuous scan.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// Start continuous scan
|
||||
ESP_ERROR_CHECK(touch_sensor_start_continuous_scanning(sens_handle));
|
||||
// ...
|
||||
// Stop continuous scan
|
||||
ESP_ERROR_CHECK(touch_sensor_stop_continuous_scanning(sens_handle));
|
||||
|
||||
.. _touch-oneshot-scan:
|
||||
|
||||
Oneshot Scan
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
With the touch controller enabled, :cpp:func:`touch_sensor_trigger_oneshot_scanning` can be called to trigger an one-time scan to all the registered touch channels. Note that oneshot scan is a blocking function, it will keep blocking and only return when the scan is finished. Moreover, you can't trigger an oneshot scan after the continuous scan has started.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// Trigger an oneshot scan with timeout 1000 ms
|
||||
ESP_ERROR_CHECK(touch_sensor_trigger_oneshot_scanning(sens_handle, 1000));
|
||||
|
||||
.. _touch-read:
|
||||
|
||||
Read Measurement Data
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Call :cpp:func:`touch_channel_read_data` to read the data with different types. Like, benchmark, smooth data, etc. You can refer to :cpp:type:`touch_chan_data_type_t` for the supported data types.
|
||||
|
||||
.. only:: not esp32 and not esp32s2 and not esp32s3
|
||||
|
||||
The {IDF_TARGET_NAME} supports frequency hopping by configuring multiple set of sample configurations, :cpp:func:`touch_channel_read_data` can read out the data of each sample configuration in a single call, you can determine the sample configuration number by :cpp:member:`touch_sensor_config_t::sample_cfg_num`, and pass an array (which length is not smaller than the configuration number) to the third parameter ``*data``, so that all the measured data of this channel will be stored in the array.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define SAMPLE_NUM 1 // Take one sample configuration set for example
|
||||
uint32_t smooth_data[SAMPLE_NUM] = {};
|
||||
// Read the smooth data
|
||||
ESP_ERROR_CHECK(touch_channel_read_data(chan_handle, TOUCH_CHAN_DATA_TYPE_SMOOTH, smooth_data));
|
||||
|
||||
.. only:: SOC_TOUCH_SUPPORT_BENCHMARK
|
||||
|
||||
.. _touch-benchmark:
|
||||
|
||||
Benchmark Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Normally, you don't have to set the benchmark manually, but you can force reset the benchmark to the current smooth value by calling :cpp:func:`touch_channel_config_benchmark` when necessary.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
touch_chan_benchmark_config_t benchmark_cfg = {
|
||||
// Benchmark operations
|
||||
// ...
|
||||
};
|
||||
ESP_ERROR_CHECK(touch_channel_config_benchmark(chan_handle, &benchmark_cfg));
|
||||
|
||||
.. only:: SOC_TOUCH_SUPPORT_WATERPROOF
|
||||
|
||||
.. _touch-waterproof:
|
||||
|
||||
Waterproof Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The {IDF_TARGET_NAME} supports waterproof. Waterproof can be registered by calling :cpp:func:`touch_sensor_config_waterproof` and specify the configurations :cpp:type:`touch_waterproof_config_t`. There are two parts of the waterproof function:
|
||||
|
||||
- Immersion (in-water) proof: :cpp:member:`touch_waterproof_config_t::guard_chan` can be specified for detecting immersion. It is usually designed as a ring on the PCB, which surrounds all the other touch pads. When this guard ring channel is triggered, that means the touch panel is immersed by water, all the touch channels will stop measuring to avoid falsely triggering.
|
||||
- Moisture (water-drop) proof: :cpp:member:`touch_waterproof_config_t::shield_chan` can be specified for detecting moisture. It usually uses the grid layout on the PCB, which covers the whole touch panel. The shield channel will charge and discharge synchronously with the current touch channel, when there is a water droplet covers both shield channel and normal touch channel, :cpp:member:`touch_waterproof_config_t::shield_drv` can strengthen the electrical coupling caused by the water droplets, and then reconfigure the active threshold based on the disturbance to eliminate the influence that introduced by the water droplet.
|
||||
|
||||
To deregister the waterproof function, you can call :cpp:func:`touch_sensor_config_waterproof` again, and set the second parameter (i.e. :cpp:type:`touch_waterproof_config_t` pointer) to ``NULL``.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
touch_waterproof_config_t waterproof_cfg = {
|
||||
// Waterproof configurations
|
||||
// ...
|
||||
};
|
||||
// Register waterproof function
|
||||
ESP_ERROR_CHECK(touch_sensor_config_waterproof(sens_handle, &waterproof_cfg));
|
||||
// ...
|
||||
// Deregister waterproof function
|
||||
ESP_ERROR_CHECK(touch_sensor_config_waterproof(sens_handle, NULL));
|
||||
|
||||
.. only:: SOC_TOUCH_SUPPORT_PROX_SENSING
|
||||
|
||||
.. _touch-prox-sensing:
|
||||
|
||||
Proximity Sensing Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The {IDF_TARGET_NAME} supports proximity sensing. Proximity sensing can be registered by calling :cpp:func:`touch_sensor_config_proximity_sensing` and specify the configurations :cpp:type:`touch_proximity_config_t`.
|
||||
|
||||
.. only:: not esp32s2 and not esp32s3
|
||||
|
||||
Since the capacitance change caused by proximity sensing is far less than that caused by physical touch, large area of copper foil is often used on PCB to increase the sensing area. In addition, multiple rounds of scans are needed and the result of each scan will be accumulated in the driver to improve the measurement sensitivity. The scan times (rounds) can be determined by :cpp:member:`touch_proximity_config_t::scan_times` and the charging times of the proximity channel in one scan can be determined by :cpp:member:`touch_proximity_config_t::charge_times`. Generally, the larger the scan times and charging times is, the higher the sensitivity will be, however, the read data will be unstable if the sensitivity is too high. Proper parameters should be determined regarding the application.
|
||||
|
||||
.. only:: esp32s2 or esp32s3
|
||||
|
||||
Since the capacitance change caused by proximity sensing is far less than that caused by physical touch, large area of copper foil is often used on PCB to increase the sensing area. In addition, multiple rounds of scans are needed and the result of each scan will be accumulated in the driver to improve the measurement sensitivity. The scan times (rounds) can be determined by :cpp:member:`touch_proximity_config_t::scan_times`. Generally, the larger the scan times and charging times is, the higher the sensitivity will be, however, the read data will be unstable if the sensitivity is too high. Proper parameters should be determined regarding the application.
|
||||
|
||||
The accumulated proximity data can be read by :cpp:func:`touch_channel_read_data` with the data type :cpp:enumerator:`TOUCH_CHAN_DATA_TYPE_PROXIMITY`
|
||||
|
||||
To deregister the proximity sensing, you can call :cpp:func:`touch_sensor_config_proximity_sensing` again, and set the second parameter (i.e. :cpp:type:`touch_proximity_config_t` pointer) to ``NULL``.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
touch_proximity_config_t prox_cfg = {
|
||||
// Proximity sensing configuration
|
||||
// ...
|
||||
};
|
||||
// Register the proximity sensing
|
||||
ESP_ERROR_CHECK(touch_sensor_config_proximity_sensing(sens_handle, &prox_cfg));
|
||||
// ...
|
||||
// Deregister the proximity sensing
|
||||
ESP_ERROR_CHECK(touch_sensor_config_proximity_sensing(sens_handle, NULL));
|
||||
|
||||
|
||||
.. only:: SOC_TOUCH_SUPPORT_SLEEP_WAKEUP
|
||||
|
||||
.. _touch-sleep-wakeup:
|
||||
|
||||
Sleep Wake-up Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The {IDF_TARGET_NAME} supports waking-up the chip from light sleep or deep sleep with the touch sensor as a wake-up source. The wake-up functionality can be registered by calling :cpp:func:`touch_sensor_config_sleep_wakeup` and specifying the configurations :cpp:type:`touch_sleep_config_t`.
|
||||
|
||||
After registering the touch sensor sleep wake-up, the chip will continue to sample the touch channels after sleep, which will increase the power consumption during the sleep. To reduce the sleep power consumption, you can reduce the number of charging and discharging times, increase the sampling interval, etc.
|
||||
|
||||
Moreover, please note that the operations like sampling, wake-up are all done by hardware when the main core is sleeping. Since this driver runs on the main core, it cannot provide functions such as reading or configuring during the sleep.
|
||||
|
||||
.. only:: SOC_RISCV_COPROC_SUPPORTED
|
||||
|
||||
If you want to read or configure the touch sensor during the sleep, you can turn to the driver ``components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_touch_ulp_core.h`` which based on the :doc:`Ultra Low Power (ULP) Coprocessor <../system/ulp>`.
|
||||
|
||||
.. list::
|
||||
|
||||
- Light sleep wake-up: you need to set :cpp:member:`slp_wakeup_lvl` to :cpp:enumerator:`TOUCH_LIGHT_SLEEP_WAKEUP` to enable the light sleep wake-up by touch sensor. Note that any registered touch channel can wake-up the chip from light sleep.
|
||||
:esp32: - Deep sleep wake-up: you need to set :cpp:member:`slp_wakeup_lvl` to :cpp:enumerator:`TOUCH_DEEP_SLEEP_WAKEUP` to enable the deep sleep wake-up by touch sensor. Note that in version {IDF_TARGET_TOUCH_SENSOR_VERSION}, enabling Deep-sleep wake-up will keep the RTC domain power on during the deep-sleep to maintain the operation of the touch sensor. At this time, any registered touch sensor channels can continue sampling and support waking up from Deep-sleep.
|
||||
:not esp32: - Deep sleep wake-up: beside setting :cpp:member:`slp_wakeup_lvl` to :cpp:enumerator:`TOUCH_DEEP_SLEEP_WAKEUP`, you need to specify :cpp:member:`deep_slp_chan` additionally. In order to reduce the power consumption, only the specified channel can wake-up the chip from the deep sleep when RTC_PREI power domain off. And also, the driver supports to store another set of configurations for the deep sleep via :cpp:member:`deep_slp_sens_cfg`, this set of configurations only takes effect during the deep sleep, you can customize the configurations to save more power. The configurations will be reset to the previous set after waking-up from the deep sleep. Please be aware that, not only deep sleep wake-up, but also light sleep wake-up will be enabled when the :cpp:member:`slp_wakeup_lvl` is :cpp:enumerator:`TOUCH_DEEP_SLEEP_WAKEUP`.
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
You can decide whether allow to power down RTC_PERIPH domain during the Deep-sleep by :cpp:member:`touch_sleep_config_t::deep_slp_allow_pd`. If allowed, the RTC_PERIPH domain will be powered down after the chip enters Deep-sleep, and only the specified :cpp:member:`touch_sleep_config_t::deep_slp_chan` can wake-up the chip from Deep-sleep. If not allowed, all enabled touch channels can wake-up the chip from Deep-sleep.
|
||||
|
||||
To deregister the sleep wake-up function, you can call :cpp:func:`touch_sensor_config_sleep_wakeup` again, and set the second parameter (i.e. :cpp:type:`touch_sleep_config_t` pointer) to ``NULL``.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
touch_sleep_config_t light_slp_cfg = TOUCH_SENSOR_DEFAULT_LSLP_CONFIG();
|
||||
// Register the light sleep wake-up
|
||||
ESP_ERROR_CHECK(touch_sensor_config_sleep_wakeup(sens_handle, &light_slp_cfg));
|
||||
// ...
|
||||
// Deregister the light sleep wake-up
|
||||
ESP_ERROR_CHECK(touch_sensor_config_sleep_wakeup(sens_handle, NULL));
|
||||
// Default Deep-sleep wake-up configurations: RTC_PERIPH will keep power on during the Deep-sleep,
|
||||
// All enabled touch channel can wake-up the chip from Deep-sleep
|
||||
touch_sleep_config_t deep_slp_cfg = TOUCH_SENSOR_DEFAULT_DSLP_CONFIG();
|
||||
// Default Deep-sleep wake-up power down configurations: RTC_PERIPH will be powered down during the Deep-sleep,
|
||||
// only the specified sleep pad can wake-up the chip from Deep-sleep
|
||||
// touch_sleep_config_t deep_slp_cfg = TOUCH_SENSOR_DEFAULT_DSLP_PD_CONFIG(sleep_channel, slp_chan_thresh1, ...);
|
||||
// Register the deep sleep wake-up
|
||||
ESP_ERROR_CHECK(touch_sensor_config_sleep_wakeup(sens_handle, &deep_slp_cfg));
|
||||
|
||||
.. only:: SOC_TOUCH_SUPPORT_DENOISE_CHAN
|
||||
|
||||
.. _touch-denoise-chan:
|
||||
|
||||
Denoise Channel Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The {IDF_TARGET_NAME} supports the internal background noise suppression by the denoise channel. Denoise channel can be registered by calling :cpp:func:`touch_sensor_config_denoise_channel` and specify the configurations :cpp:type:`touch_denoise_chan_config_t`.
|
||||
|
||||
Denoise channel is an internal channel that not fanned out. After the denoise channel is enabled, the sampled data of the other touch channels will minus the data of the denoise channel automatically, so the final measurement result of the touch channels will be attenuated compare to the original data.
|
||||
|
||||
Aside of the common touch channel configuration, the reference capacitance that attached to the denoise channel can be set by :cpp:member:`touch_denoise_chan_config_t::ref_cap`. And the noise suppression resolution can be set by :cpp:member:`touch_denoise_chan_config_t::resolution`. The higher the resolution, the greater and more accuracy the denoise channel sample data will be, and the better suppression effect it takes. But at the same time, the attenuation of other touch channel sampling values also increases.
|
||||
|
||||
For example, the denoise channel resolution is :cpp:enumerator:`touch_denoise_chan_resolution_t::TOUCH_DENOISE_CHAN_RESOLUTION_BIT8`, i.e., maximum sample data is ``255``. Assuming the actual sample data of a normal touch channel is ``10000``, and the denoise channel sample data is ``100``, then the final measurement result of the touch channel will be ``10000 - 100 = 9900``; If we increase the resolution to :cpp:enumerator:`touch_denoise_chan_resolution_t::TOUCH_DENOISE_CHAN_RESOLUTION_BIT12`, i.e., maximum sample data is ``4095``, the resolution is ``16`` times greater. So the denoise channel sample data will be about ``100 * 16 = 1600``, then the final measurement result of this touch channel will be ``10000 - 1600 = 8400.``
|
||||
|
||||
To deregister the denoise channel, you can call :cpp:func:`touch_sensor_config_denoise_channel` again, and set the second parameter (i.e. :cpp:type:`touch_denoise_chan_config_t` pointer) to ``NULL``.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
touch_denoise_chan_config_t denoise_cfg = {
|
||||
// Denoise channel configurations
|
||||
// ...
|
||||
}
|
||||
// Register the denoise channel
|
||||
ESP_ERROR_CHECK(touch_sensor_config_denoise_channel(sens_handle, &denoise_cfg));
|
||||
// ...
|
||||
// Deregister the denoise channel
|
||||
ESP_ERROR_CHECK(touch_sensor_config_denoise_channel(sens_handle, NULL));
|
||||
|
||||
Application Examples
|
||||
------------------------
|
||||
|
||||
- :example:`peripherals/touch_sensor/touch_sens_basic` demonstrates how to register touch channels and read the data, including hardware requirements and project configuration instructions.
|
||||
- :example:`peripherals/touch_sensor/touch_sens_sleep` demonstrates how to wake up the chip from the light or deep sleep by the touch sensor.
|
||||
|
||||
Application Notes
|
||||
-----------------
|
||||
|
||||
Touch Sensor Power Consumption Issues
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Due to the capacitive charging and discharging operation in the touch sensor measurements, it is a relatively high power-consuming peripheral. In applications with high power consumption requirements, the following methods can be helpful to reduce the power consumption.
|
||||
|
||||
.. list::
|
||||
|
||||
- Reduce the number of touch channels: Multiple functions (such as single-press, double-press, long press, etc.) can be multiplexed on the same channel, thereby reducing the number of touch sensors.
|
||||
- Increase the measurement interval: By increasing the measurement interval :cpp:member:`touch_sensor_config_t::meas_interval_us`, the measurement frequency will be reduced, thereby lowering down the power consumption.
|
||||
:esp32: - Reduce single measurement duration: By decreasing the single measurement duration :cpp:member:`touch_sensor_sample_config_t::charge_duration_ms`, the number of charging and discharging cycles is reduced, resulting in lower power consumption.
|
||||
:not esp32: - Reduce single measurement charging and discharging cycles: By lowering down the single measurement charging and discharging cycles :cpp:member:`touch_sensor_sample_config_t::charge_times`, power consumption will be decreased.
|
||||
:esp32s2 or esp32s3: - Set the current bias type to self-bias: By configuring :cpp:member:`touch_sensor_sample_config_t::bias_type` to :cpp:enumerator:`touch_bias_type_t::TOUCH_BIAS_TYPE_SELF` to use self-bias, which is more power-saving but less stable.
|
||||
:esp32 or esp32s2 or esp32s3: - Lower down the charging and discharging amplitudes: :cpp:member:`touch_sensor_sample_config_t::charge_volt_lim_l` and :cpp:member:`touch_sensor_sample_config_t::charge_volt_lim_h` can specify the lower voltage limit during the discharging and the upper voltage limit during the charging. Reducing both voltage limitations and the voltage error between them can also decrease the power consumption.
|
||||
:esp32 or esp32s2 or esp32s3: - Reduce the current bias intensity: Lowering down :cpp:member:`touch_channel_config_t::charge_speed` (i.e., the current magnitude of the current bias) can help to reduce the power consumption.
|
||||
:not esp32 and not esp32s2 and not esp32s3: - Lower down the LDO voltage biasing intensity: Decreasing :cpp:member:`touch_channel_config_t::bias_volt` can reduce the power consumption.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/touch_sens.inc
|
||||
.. include-build-file:: inc/touch_sens_types.inc
|
||||
.. include-build-file:: inc/touch_version_types.inc
|
||||
@@ -0,0 +1,49 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. touch-chan-mapping
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20
|
||||
|
||||
* - Channel
|
||||
- GPIO
|
||||
|
||||
* - CH0
|
||||
- IO4
|
||||
|
||||
* - CH1
|
||||
- IO0
|
||||
|
||||
* - CH2
|
||||
- IO2
|
||||
|
||||
* - CH3
|
||||
- IO15
|
||||
|
||||
* - CH4
|
||||
- IO13
|
||||
|
||||
* - CH5
|
||||
- IO12
|
||||
|
||||
* - CH6
|
||||
- IO14
|
||||
|
||||
* - CH7
|
||||
- IO27
|
||||
|
||||
* - CH8
|
||||
- IO33
|
||||
|
||||
* - CH9
|
||||
- IO32
|
||||
|
||||
---
|
||||
@@ -0,0 +1,64 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. touch-chan-mapping
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20
|
||||
|
||||
* - Channel
|
||||
- GPIO
|
||||
|
||||
* - CH0
|
||||
- IO0
|
||||
|
||||
* - CH1
|
||||
- IO1
|
||||
|
||||
* - CH2
|
||||
- IO2
|
||||
|
||||
* - CH3
|
||||
- IO3
|
||||
|
||||
* - CH4
|
||||
- IO29
|
||||
|
||||
* - CH5
|
||||
- IO30
|
||||
|
||||
* - CH6
|
||||
- IO31
|
||||
|
||||
* - CH7
|
||||
- IO32
|
||||
|
||||
* - CH8
|
||||
- IO33
|
||||
|
||||
* - CH9
|
||||
- IO34
|
||||
|
||||
* - CH10
|
||||
- IO35
|
||||
|
||||
* - CH11
|
||||
- IO36
|
||||
|
||||
* - CH12
|
||||
- IO37
|
||||
|
||||
* - CH13
|
||||
- IO38
|
||||
|
||||
* - CH14
|
||||
- IO39
|
||||
|
||||
---
|
||||
@@ -0,0 +1,64 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. touch-chan-mapping
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20
|
||||
|
||||
* - Channel
|
||||
- GPIO
|
||||
|
||||
* - CH1
|
||||
- IO2
|
||||
|
||||
* - CH2
|
||||
- IO3
|
||||
|
||||
* - CH3
|
||||
- IO4
|
||||
|
||||
* - CH4
|
||||
- IO5
|
||||
|
||||
* - CH5
|
||||
- IO6
|
||||
|
||||
* - CH6
|
||||
- IO7
|
||||
|
||||
* - CH7
|
||||
- IO8
|
||||
|
||||
* - CH8
|
||||
- IO9
|
||||
|
||||
* - CH9
|
||||
- IO10
|
||||
|
||||
* - CH10
|
||||
- IO11
|
||||
|
||||
* - CH11
|
||||
- IO12
|
||||
|
||||
* - CH12
|
||||
- IO13
|
||||
|
||||
* - CH13
|
||||
- IO14
|
||||
|
||||
* - CH14
|
||||
- IO15
|
||||
|
||||
* - CH15
|
||||
- Internal
|
||||
|
||||
---
|
||||
@@ -0,0 +1,64 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. touch-chan-mapping
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20
|
||||
|
||||
* - Channel
|
||||
- GPIO
|
||||
|
||||
* - CH0
|
||||
- Internal
|
||||
|
||||
* - CH1
|
||||
- IO1
|
||||
|
||||
* - CH2
|
||||
- IO2
|
||||
|
||||
* - CH3
|
||||
- IO3
|
||||
|
||||
* - CH4
|
||||
- IO4
|
||||
|
||||
* - CH5
|
||||
- IO5
|
||||
|
||||
* - CH6
|
||||
- IO6
|
||||
|
||||
* - CH7
|
||||
- IO7
|
||||
|
||||
* - CH8
|
||||
- IO8
|
||||
|
||||
* - CH9
|
||||
- IO9
|
||||
|
||||
* - CH10
|
||||
- IO10
|
||||
|
||||
* - CH11
|
||||
- IO11
|
||||
|
||||
* - CH12
|
||||
- IO12
|
||||
|
||||
* - CH13
|
||||
- IO13
|
||||
|
||||
* - CH14
|
||||
- IO14
|
||||
|
||||
---
|
||||
@@ -0,0 +1,64 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. touch-chan-mapping
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20
|
||||
|
||||
* - Channel
|
||||
- GPIO
|
||||
|
||||
* - CH0
|
||||
- Internal
|
||||
|
||||
* - CH1
|
||||
- IO1
|
||||
|
||||
* - CH2
|
||||
- IO2
|
||||
|
||||
* - CH3
|
||||
- IO3
|
||||
|
||||
* - CH4
|
||||
- IO4
|
||||
|
||||
* - CH5
|
||||
- IO5
|
||||
|
||||
* - CH6
|
||||
- IO6
|
||||
|
||||
* - CH7
|
||||
- IO7
|
||||
|
||||
* - CH8
|
||||
- IO8
|
||||
|
||||
* - CH9
|
||||
- IO9
|
||||
|
||||
* - CH10
|
||||
- IO10
|
||||
|
||||
* - CH11
|
||||
- IO11
|
||||
|
||||
* - CH12
|
||||
- IO12
|
||||
|
||||
* - CH13
|
||||
- IO13
|
||||
|
||||
* - CH14
|
||||
- IO14
|
||||
|
||||
---
|
||||
@@ -0,0 +1,61 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. touch-chan-mapping
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20
|
||||
|
||||
* - Channel
|
||||
- GPIO
|
||||
|
||||
* - CH0
|
||||
- IO6
|
||||
|
||||
* - CH1
|
||||
- IO7
|
||||
|
||||
* - CH2
|
||||
- IO8
|
||||
|
||||
* - CH3
|
||||
- IO9
|
||||
|
||||
* - CH4
|
||||
- IO10
|
||||
|
||||
* - CH5
|
||||
- IO11
|
||||
|
||||
* - CH6
|
||||
- IO12
|
||||
|
||||
* - CH7
|
||||
- IO13
|
||||
|
||||
* - CH8
|
||||
- IO14
|
||||
|
||||
* - CH9
|
||||
- IO15
|
||||
|
||||
* - CH10
|
||||
- IO16
|
||||
|
||||
* - CH11
|
||||
- IO17
|
||||
|
||||
* - CH12
|
||||
- IO18
|
||||
|
||||
* - CH13
|
||||
- IO19
|
||||
|
||||
---
|
||||
@@ -0,0 +1,98 @@
|
||||
Clock Tree
|
||||
==========
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
{IDF_TARGET_RC_FAST_VAGUE_FREQ: default="17.5", esp32="8", esp32s2="8", esp32h2="8", esp32h21="20", esp32h4="20"}
|
||||
|
||||
{IDF_TARGET_RC_FAST_ADJUSTED_FREQ: default="17.5", esp32="8.5", esp32s2="8.5", esp32h2="8.5", esp32h21="20", esp32h4="20"}
|
||||
|
||||
{IDF_TARGET_XTAL_FREQ: default="40", esp32="2 ~ 40", esp32c2="40/26", esp32h2="32", esp32c5="48", esp32h21="32", esp32h4="32"}
|
||||
|
||||
{IDF_TARGET_RC_SLOW_VAGUE_FREQ: default="136", esp32="150", esp32s2="90", esp32h21="600", esp32h4="600"}
|
||||
|
||||
{IDF_TARGET_OSC_SLOW_PIN: default="GPIO0", esp32c2="pin0 (when its frequency is no more than 136 kHz)", "esp32c6="GPIO0", esp32h2="GPIO13", esp32h21="GPIO11", esp32h4="GPIO5"}
|
||||
|
||||
The clock subsystem of {IDF_TARGET_NAME} is used to source and distribute system/module clocks from a range of root clocks. The clock tree driver maintains the basic functionality of the system clock and the intricate relationship among module clocks.
|
||||
|
||||
This document starts with the introduction to root and module clocks. Then it covers the clock tree APIs that can be called to monitor the status of the module clocks at runtime.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This section lists definitions of {IDF_TARGET_NAME}'s supported root clocks and module clocks. These definitions are commonly used in the driver configuration, to help select a proper source clock for the peripheral.
|
||||
|
||||
Root Clocks
|
||||
^^^^^^^^^^^
|
||||
|
||||
Root clocks generate reliable clock signals. These clock signals then pass through various gates, muxes, dividers, or multipliers to become the clock sources for every functional module: the CPU core(s), Wi-Fi, Bluetooth, the RTC, and the peripherals.
|
||||
|
||||
{IDF_TARGET_NAME}'s root clocks are listed in :cpp:type:`soc_root_clk_t`:
|
||||
|
||||
.. list::
|
||||
|
||||
- Internal {IDF_TARGET_RC_FAST_VAGUE_FREQ} MHz RC Oscillator (RC_FAST)
|
||||
|
||||
This RC oscillator generates a about {IDF_TARGET_RC_FAST_ADJUSTED_FREQ} MHz clock signal output as the ``RC_FAST_CLK``.
|
||||
|
||||
.. only:: SOC_CLK_RC_FAST_D256_SUPPORTED
|
||||
|
||||
The about {IDF_TARGET_RC_FAST_ADJUSTED_FREQ} MHz signal output is also passed into a configurable divider, which by default divides the input clock frequency by 256, to generate a ``RC_FAST_D256_CLK``.
|
||||
|
||||
The exact frequency of ``RC_FAST_CLK`` can be computed in runtime through calibration on the ``RC_FAST_D256_CLK``.
|
||||
|
||||
.. only:: not SOC_CLK_RC_FAST_D256_SUPPORTED and SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
|
||||
|
||||
The exact frequency of ``RC_FAST_CLK`` can be computed in runtime through calibration.
|
||||
|
||||
.. only:: not SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
|
||||
|
||||
The exact frequency of ``RC_FAST_CLK`` cannot be computed in runtime through calibration, but it is still possible to get its frequency through an oscilloscope or a logic analyzer by routing the clock signal to a GPIO pin.
|
||||
|
||||
- External {IDF_TARGET_XTAL_FREQ} MHz Crystal (XTAL)
|
||||
|
||||
- Internal {IDF_TARGET_RC_SLOW_VAGUE_FREQ} kHz RC Oscillator (RC_SLOW)
|
||||
|
||||
This RC oscillator generates a about {IDF_TARGET_RC_SLOW_VAGUE_FREQ}kHz clock signal output as the ``RC_SLOW_CLK``. The exact frequency of this clock can be computed in runtime through calibration.
|
||||
|
||||
.. only:: SOC_CLK_XTAL32K_SUPPORTED
|
||||
|
||||
- External 32 kHz Crystal - optional (XTAL32K)
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
The clock source for this ``XTAL32K_CLK`` can be either a 32 kHz crystal connecting to the ``32K_XP`` and ``32K_XN`` pins or a 32 kHz clock signal generated by an external circuit. The external signal must be connected to the ``32K_XN`` pin. Additionally, a 1 nF capacitor must be placed between the ``32K_XP`` pin and ground. In this case, the ``32K_XP`` pin cannot be used as a GPIO pin.
|
||||
|
||||
.. only:: esp32p4 or esp32s31
|
||||
|
||||
The clock source for this ``XTAL32K_CLK`` is a 32 kHz crystal connecting to the ``XTAL_32K_P`` and ``XTAL_32K_N`` pins.
|
||||
|
||||
.. only:: not esp32 and not esp32p4 and not esp32s31
|
||||
|
||||
The clock source for this ``XTAL32K_CLK`` can be either a 32 kHz crystal connecting to the ``XTAL_32K_P`` and ``XTAL_32K_N`` pins or a 32 kHz clock signal generated by an external circuit. The external signal must be connected to the ``XTAL_32K_P`` pin.
|
||||
|
||||
``XTAL32K_CLK`` can also be calibrated to get its exact frequency.
|
||||
|
||||
.. only:: SOC_CLK_OSC_SLOW_SUPPORTED
|
||||
|
||||
- External Slow Clock - optional (OSC_SLOW)
|
||||
|
||||
A clock signal generated by an external circuit can be connected to {IDF_TARGET_OSC_SLOW_PIN} to be the clock source for the ``RTC_SLOW_CLK``. This clock can also be calibrated to get its exact frequency.
|
||||
|
||||
Typically, the frequency of the signal generated from an RC oscillator circuit is less accurate and more sensitive to the environment compared to the signal generated from a crystal. {IDF_TARGET_NAME} provides several clock source options for the ``RTC_SLOW_CLK``, and it is possible to make the choice based on the requirements for system time accuracy and power consumption. For more details, please refer to :ref:`rtc-clock-source-choice`.
|
||||
|
||||
Module Clocks
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
{IDF_TARGET_NAME}'s available module clocks are listed in :cpp:type:`soc_module_clk_t`. Each module clock has a unique ID. You can get more information on each clock by checking the documented enum value.
|
||||
|
||||
API Usage
|
||||
---------
|
||||
|
||||
The clock tree driver provides an all-in-one API to get the frequency of the module clocks, :cpp:func:`esp_clk_tree_src_get_freq_hz`. This function allows you to obtain the clock frequency at any time by providing the clock name :cpp:enum:`soc_module_clk_t` and specifying the desired precision level for the returned frequency value :cpp:enum:`esp_clk_tree_src_freq_precision_t`.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/clk_tree_defs.inc
|
||||
.. include-build-file:: inc/esp_clk_tree.inc
|
||||
@@ -0,0 +1,320 @@
|
||||
=============================================
|
||||
Coordinate Rotation Digital Computer (CORDIC)
|
||||
=============================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
CORDIC (Coordinate Rotation Digital Computer) is an iterative method for computing trigonometric, hyperbolic, and arctangent functions. The algorithm converts these operations into simple additions and shifts through iterative rotations and scaling, enabling efficient hardware-based computation.
|
||||
|
||||
CORDIC is primarily implemented in hardware with the following characteristics:
|
||||
|
||||
- High computation speed
|
||||
- High precision
|
||||
- Lower resource usage
|
||||
|
||||
The table below compares the performance of computing the cosine of a random value using a CORDIC driver and the software IQmath library, measured in CPU cycles.
|
||||
|
||||
+------------------+----------------+----------------+
|
||||
| Function | IQmath | CORDIC |
|
||||
+==================+================+================+
|
||||
| cos | 29640 | 8080 |
|
||||
+------------------+----------------+----------------+
|
||||
|
||||
Driver Introduction
|
||||
===================
|
||||
|
||||
Before using the CORDIC driver, you should understand its calculation methods and parameter meanings.
|
||||
|
||||
Calculation Methods
|
||||
-------------------
|
||||
|
||||
The CORDIC function is selected via :cpp:member:`cordic_calculate_config_t::function`. Supported functions are listed in the table below. Some functions take two arguments, others one; some produce two results, others one.
|
||||
|
||||
The input scaling factor can be set with :cpp:member:`cordic_calculate_config_t::scale_exp` (effective scaling is :math:`2^n`). In the table below, :math:`n` denotes this parameter.
|
||||
|
||||
:math:`arg_{1}` and :math:`arg_{2}` are the inputs, corresponding to :cpp:member:`cordic_input_buffer_desc_t::p_data_arg1` and :cpp:member:`cordic_input_buffer_desc_t::p_data_arg2`. :math:`res_{1}` and :math:`res_{2}` are the outputs, corresponding to :cpp:member:`cordic_output_buffer_desc_t::p_data_res1` and :cpp:member:`cordic_output_buffer_desc_t::p_data_res2`. The mapping is summarized in the following table.
|
||||
|
||||
.. list-table:: CORDIC Supported Functions and Formulas
|
||||
:header-rows: 1
|
||||
:widths: 18 22 32 11 24
|
||||
:stub-columns: 0
|
||||
|
||||
* - Function
|
||||
- Input
|
||||
- Output
|
||||
- Scale exponent :math:`n`
|
||||
- Input/Output range (scale = 1)
|
||||
* - :cpp:enumerator:`ESP_CORDIC_FUNC_COS` Cosine
|
||||
- :math:`\frac{\theta}{\pi}, \quad \theta \text{ in radians}`
|
||||
- :math:`res_1 = \cos(\theta)`, :math:`res_2 = \sin(\theta)`
|
||||
- 0
|
||||
- :math:`arg_1, res_1, res_2 \in \left[-1, 1\right)`
|
||||
* - :cpp:enumerator:`ESP_CORDIC_FUNC_SIN` Sine
|
||||
- :math:`\frac{\theta}{\pi}, \quad \theta \text{ in radians}`
|
||||
- :math:`res_1 = \sin(\theta)`, :math:`res_2 = \cos(\theta)`
|
||||
- 0
|
||||
- :math:`arg_1, res_1, res_2 \in \left[-1, 1\right)`
|
||||
* - :cpp:enumerator:`ESP_CORDIC_FUNC_PHASE` Phase
|
||||
- :math:`arg_1 = x \cdot 2^{-n}`, :math:`arg_2 = y \cdot 2^{-n}`
|
||||
- :math:`res_1 = \arctan(y/x) \cdot 2^{-n}/\pi`, :math:`res_2 = \sqrt{x^2+y^2} \cdot 2^{-n}`
|
||||
- [0, 15]
|
||||
- :math:`arg_{1,2} \in \left[-1, 1\right)`, :math:`res_1 \in \left[-1, 1\right)`, :math:`res_2 \in \left[0, 1\right)`
|
||||
* - :cpp:enumerator:`ESP_CORDIC_FUNC_MODULUS` Modulus
|
||||
- :math:`arg_1 = x \cdot 2^{-n}`, :math:`arg_2 = y \cdot 2^{-n}`
|
||||
- :math:`res_1 = \sqrt{x^2+y^2} \cdot 2^{-n}`, :math:`res_2 = \arctan(y/x) \cdot 2^{-n}/\pi`
|
||||
- [0, 15]
|
||||
- :math:`arg_{1,2} \in \left[-1, 1\right)`, :math:`res_1 \in \left[0, 1\right)`, :math:`res_2 \in \left[-1, 1\right)`
|
||||
* - :cpp:enumerator:`ESP_CORDIC_FUNC_ARCTAN` Arctangent
|
||||
- :math:`arg_1 = x \cdot 2^{-n}`
|
||||
- :math:`res_1 = \arctan(x) \cdot 2^{-n}/\pi`
|
||||
- [0, 15]
|
||||
- :math:`arg_1, res_1 \in \left[-1, 1\right)`
|
||||
* - :cpp:enumerator:`ESP_CORDIC_FUNC_COSH` Hyperbolic cosine
|
||||
- :math:`arg_1 = x \cdot 2^{-n}`
|
||||
- :math:`res_1 = \cosh(x) \cdot 2^{-n}`, :math:`res_2 = \sinh(x) \cdot 2^{-n}`
|
||||
- 1
|
||||
- :math:`arg_1 \in \left[-0.559, 0.559\right]`, :math:`res_1 \in \left[0.5, 0.846\right]`, :math:`res_2 \in \left[-0.683, 0.683\right]`
|
||||
* - :cpp:enumerator:`ESP_CORDIC_FUNC_SINH` Hyperbolic sine
|
||||
- :math:`arg_1 = x \cdot 2^{-n}`
|
||||
- :math:`res_1 = \sinh(x) \cdot 2^{-n}`, :math:`res_2 = \cosh(x) \cdot 2^{-n}`
|
||||
- 1
|
||||
- :math:`arg_1 \in \left[-0.559, 0.559\right]`, :math:`res_1 \in \left[-0.683, 0.683\right]`, :math:`res_2 \in \left[0.5, 0.846\right]`
|
||||
* - :cpp:enumerator:`ESP_CORDIC_FUNC_ARCHTANH` Inverse hyperbolic tangent
|
||||
- :math:`arg_1 = x \cdot 2^{-n}`
|
||||
- :math:`res_1 = \mathrm{arctanh}(x) \cdot 2^{-n}`
|
||||
- 1
|
||||
- :math:`arg_1 \in \left[-0.403, 0.403\right]`, :math:`res_1 \in \left[-0.559, 0.559\right]`
|
||||
* - :cpp:enumerator:`ESP_CORDIC_FUNC_LOGE` Natural logarithm
|
||||
- :math:`arg_1 = (x-1)/(x+1) \cdot 2^{-1}`
|
||||
- :math:`res_1 = \ln(x) \cdot 2^{-2}`
|
||||
- 1
|
||||
- :math:`arg_1 \in \left[-0.403, 0.403\right]`, :math:`res_1 \in \left[-0.559, 0.559\right]`
|
||||
* - :cpp:enumerator:`ESP_CORDIC_FUNC_SQUARE_ROOT` Square root
|
||||
- :math:`arg_1 = x \cdot 2^{-n}`
|
||||
- :math:`res_1 = \sqrt{x} \cdot 2^{-n}`
|
||||
- [0, 4]
|
||||
- :math:`arg_1 \in \left[0.1069, 1\right)`, :math:`res_1 \in \left[0.177, 1\right)`
|
||||
|
||||
Data Format
|
||||
-----------
|
||||
|
||||
Input and output data format is selected via :cpp:member:`cordic_calculate_config_t::iq_format`. Supported formats:
|
||||
|
||||
- :cpp:enumerator:`ESP_CORDIC_FORMAT_Q15` Q15 format
|
||||
|
||||
In Q15 format, data has one sign bit plus 15 fractional bits. Range: [-1 (0x8000), 1 - 2^-15 (0x7FFF)].
|
||||
|
||||
.. figure:: ../../../_static/diagrams/cordic/cordic_q15.svg
|
||||
:align: center
|
||||
:alt: Q15 format
|
||||
|
||||
Q15 format
|
||||
|
||||
.. math::
|
||||
|
||||
value = \sum_{i=0}^{14} \frac{1}{2^{(15-i)}} \times B_i
|
||||
|
||||
- :cpp:enumerator:`ESP_CORDIC_FORMAT_Q31` Q31 format
|
||||
|
||||
In Q31 format, data has one sign bit plus 31 fractional bits. Range: [-1 (0x80000000), 1 - 2^-31 (0x7FFFFFFF)].
|
||||
|
||||
.. figure:: ../../../_static/diagrams/cordic/cordic_q31.svg
|
||||
:align: center
|
||||
:alt: Q31 format
|
||||
|
||||
Q31 format
|
||||
|
||||
.. math::
|
||||
|
||||
value = \sum_{i=0}^{30} \frac{1}{2^{(31-i)}} \times B_i
|
||||
|
||||
Precision
|
||||
---------
|
||||
|
||||
The number of iterations (precision) is set via :cpp:member:`cordic_calculate_config_t::iteration_count`. Supported values are given below.
|
||||
|
||||
**Phase, Modulus, Arctan functions**
|
||||
|
||||
+------------------+----------------+----------------+--------------------------+--------------------------+
|
||||
| Function | Iterations | Cycles | Max residual (q1.31) | Max residual (q1.15) |
|
||||
+==================+================+================+==========================+==========================+
|
||||
| Phase, Modulus, | 4 | 1 | :math:`2^{-4}` | :math:`2^{-4}` |
|
||||
| Arctan +----------------+----------------+--------------------------+--------------------------+
|
||||
| | 8 | 2 | :math:`2^{-8}` | :math:`2^{-8}` |
|
||||
| +----------------+----------------+--------------------------+--------------------------+
|
||||
| | 12 | 3 | :math:`2^{-12}` | :math:`2^{-12}` |
|
||||
| +----------------+----------------+--------------------------+--------------------------+
|
||||
| | 16 | 4 | :math:`2^{-16}` | :math:`2^{-15}` |
|
||||
| +----------------+----------------+--------------------------+--------------------------+
|
||||
| | 20 | 5 | :math:`2^{-20}` | :math:`2^{-15}` |
|
||||
| +----------------+----------------+--------------------------+--------------------------+
|
||||
| | 24 | 6 | :math:`2^{-24}` | :math:`2^{-15}` |
|
||||
+------------------+----------------+----------------+--------------------------+--------------------------+
|
||||
|
||||
**Sin, Cos, Sinh, Cosh, Arctanh, Ln functions**
|
||||
|
||||
+------------------+----------------+----------------+--------------------------+--------------------------+
|
||||
| Function | Iterations | Cycles | Max residual (q1.31) | Max residual (q1.15) |
|
||||
+==================+================+================+==========================+==========================+
|
||||
| Sin, Cos, | 4 | 1 | :math:`2^{-3}` | :math:`2^{-3}` |
|
||||
| Sinh, Cosh, +----------------+----------------+--------------------------+--------------------------+
|
||||
| Arctanh, Ln | 8 | 2 | :math:`2^{-7}` | :math:`2^{-7}` |
|
||||
| +----------------+----------------+--------------------------+--------------------------+
|
||||
| | 12 | 3 | :math:`2^{-11}` | :math:`2^{-11}` |
|
||||
| +----------------+----------------+--------------------------+--------------------------+
|
||||
| | 16 | 4 | :math:`2^{-14}` | :math:`2^{-14}` |
|
||||
| +----------------+----------------+--------------------------+--------------------------+
|
||||
| | 20 | 5 | :math:`2^{-18}` | :math:`2^{-15}` |
|
||||
| +----------------+----------------+--------------------------+--------------------------+
|
||||
| | 24 | 6 | :math:`2^{-22}` | :math:`2^{-15}` |
|
||||
+------------------+----------------+----------------+--------------------------+--------------------------+
|
||||
|
||||
**Sqrt function**
|
||||
|
||||
+------------------+----------------+----------------+--------------------------+--------------------------+
|
||||
| Function | Iterations | Cycles | Max residual (q1.31) | Max residual (q1.15) |
|
||||
+==================+================+================+==========================+==========================+
|
||||
| Sqrt | 4 | 1 | :math:`2^{-7}` | :math:`2^{-7}` |
|
||||
| +----------------+----------------+--------------------------+--------------------------+
|
||||
| | 8 | 2 | :math:`2^{-14}` | :math:`2^{-14}` |
|
||||
| +----------------+----------------+--------------------------+--------------------------+
|
||||
| | 12 | 3 | :math:`2^{-22}` | :math:`2^{-15}` |
|
||||
+------------------+----------------+----------------+--------------------------+--------------------------+
|
||||
|
||||
.. note::
|
||||
|
||||
More iterations give higher precision but longer computation time. Once precision reaches the limit of the data format, further increasing iterations does not improve results and only increases latency.
|
||||
|
||||
Quick Start
|
||||
===========
|
||||
|
||||
This section shows how to create a CORDIC engine and run calculations. Typical usage flow:
|
||||
|
||||
Create CORDIC Engine
|
||||
--------------------
|
||||
|
||||
Create a CORDIC engine as follows:
|
||||
|
||||
.. code:: c
|
||||
|
||||
cordic_engine_handle_t engine = NULL;
|
||||
cordic_engine_config_t config = {
|
||||
.clock_source = CORDIC_CLK_SRC_DEFAULT, // Select clock source
|
||||
};
|
||||
ESP_ERROR_CHECK(cordic_new_engine(&config, &engine));
|
||||
|
||||
Run CORDIC Calculation
|
||||
-----------------------
|
||||
|
||||
Use the engine to perform a calculation:
|
||||
|
||||
.. code:: c
|
||||
|
||||
cordic_calculate_config_t calc_config = {
|
||||
.function = ESP_CORDIC_FUNC_COS, // Select function
|
||||
.iq_format = ESP_CORDIC_FORMAT_Q15, // Input/output data format
|
||||
.iteration_count = 4, // Iteration count (precision)
|
||||
.scale_exp = 0, // Input scale exponent n (effective scale 2^n)
|
||||
};
|
||||
|
||||
size_t buffer_depth = 60;
|
||||
|
||||
uint32_t data_x[buffer_depth] = {}; // Input 1, length 60
|
||||
uint32_t data_y[buffer_depth] = {}; // Input 2, length 60
|
||||
|
||||
uint32_t res1[buffer_depth] = {}; // Output 1, length 60
|
||||
uint32_t res2[buffer_depth] = {}; // Output 2, length 60
|
||||
|
||||
// Configure input
|
||||
cordic_input_buffer_desc_t input_buffer = {
|
||||
.p_data_arg1 = data_x, // Input 1
|
||||
.p_data_arg2 = data_y, // Input 2
|
||||
};
|
||||
|
||||
// Configure output
|
||||
cordic_output_buffer_desc_t output_buffer = {
|
||||
.p_data_res1 = res1, // Output 1
|
||||
.p_data_res2 = res2, // Output 2
|
||||
};
|
||||
|
||||
// Run calculation
|
||||
ESP_ERROR_CHECK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, buffer_depth));
|
||||
|
||||
Format Conversion
|
||||
-----------------
|
||||
|
||||
The driver provides conversion between fixed-point and floating-point via :cpp:func:`cordic_convert_fixed_to_float` and :cpp:func:`cordic_convert_float_to_fixed`.
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Fixed-point to float
|
||||
// res1[0], res2[0] are CORDIC fixed-point outputs
|
||||
float res1_float = cordic_convert_fixed_to_float(res1[0], ESP_CORDIC_FORMAT_Q15);
|
||||
float res2_float = cordic_convert_fixed_to_float(res2[0], ESP_CORDIC_FORMAT_Q15);
|
||||
|
||||
// Float to fixed-point
|
||||
uint32_t res1_fixed = cordic_convert_float_to_fixed(res1_float, ESP_CORDIC_FORMAT_Q15);
|
||||
uint32_t res2_fixed = cordic_convert_float_to_fixed(res2_float, ESP_CORDIC_FORMAT_Q15);
|
||||
|
||||
Resource Cleanup
|
||||
----------------
|
||||
|
||||
When the CORDIC engine is no longer needed, call :cpp:func:`cordic_delete_engine` to release the underlying hardware.
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_ERROR_CHECK(cordic_delete_engine(engine));
|
||||
|
||||
Advanced
|
||||
========
|
||||
|
||||
Thread Safety
|
||||
-------------
|
||||
|
||||
The following CORDIC APIs are thread-safe and can be called from different RTOS tasks without extra locking:
|
||||
|
||||
Factory functions:
|
||||
|
||||
- :cpp:func:`cordic_new_engine`
|
||||
- :cpp:func:`cordic_delete_engine`
|
||||
|
||||
.. note::
|
||||
|
||||
:cpp:func:`cordic_calculate_polling` is **not** thread-safe. Do not call it from multiple tasks without mutex protection.
|
||||
|
||||
Cache Safety
|
||||
------------
|
||||
|
||||
:cpp:func:`cordic_calculate_polling` can be used safely in an ISR. For correct behavior when cache is disabled (e.g., in cache-safe ISRs), enable the Kconfig option `CONFIG_CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM` to place the control functions in IRAM.
|
||||
|
||||
Kconfig Options
|
||||
---------------
|
||||
|
||||
The following Kconfig option configures the CORDIC driver:
|
||||
|
||||
- :ref:`CONFIG_CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM` - Place calculation control functions in IRAM.
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
CORDIC Driver API
|
||||
-----------------
|
||||
|
||||
.. include-build-file:: inc/cordic.inc
|
||||
|
||||
CORDIC HAL API
|
||||
--------------
|
||||
|
||||
.. include-build-file:: inc/components/esp_hal_cordic/include/hal/cordic_types.inc
|
||||
|
||||
CORDIC Types
|
||||
------------
|
||||
|
||||
.. include-build-file:: inc/components/esp_driver_cordic/include/driver/cordic_types.inc
|
||||
@@ -0,0 +1,162 @@
|
||||
Digital To Analog Converter (DAC)
|
||||
=================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
{IDF_TARGET_DAC_CH_1: default = "Not Updated!", esp32 = "GPIO25", esp32s2 = "GPIO17"}
|
||||
{IDF_TARGET_DAC_CH_2: default = "Not Updated!", esp32 = "GPIO26", esp32s2 = "GPIO18"}
|
||||
{IDF_TARGET_DAC_REF_PIN: default = "Not Updated!", esp32 = "VDD3P3_RTC", esp32s2 = "VDD3P3_RTC_IO"}
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
{IDF_TARGET_NAME} has two 8-bit DAC (digital to analog converter) channels respectively connected to {IDF_TARGET_DAC_CH_1} (Channel 1) and {IDF_TARGET_DAC_CH_2} (Channel 2). Each DAC channel can convert the digital value 0~255 to the analog voltage 0~Vref (The reference voltage 'Vref' here is input from the pin {IDF_TARGET_DAC_REF_PIN}, which ideally equals to the power supply VDD). The output voltage can be calculated as the following::
|
||||
|
||||
out_voltage = Vref * digi_val / 255
|
||||
|
||||
The DAC peripheral supports outputting analog signal in the following ways:
|
||||
|
||||
1. Outputting a voltage directly. The DAC channel keeps outputting a specified voltage.
|
||||
2. Outputting continuous analog signal by DMA. The DAC converts the data in a buffer at a specified frequency.
|
||||
3. Outputting a cosine wave by the cosine wave generator. The DAC channel can output a cosine wave with specified frequency and amplitude.
|
||||
|
||||
For other analog output options, see :doc:`Sigma-Delta Modulation <sdm>` and :doc:`LED Control <ledc>`. Both modules produce high-frequency PWM/PDM output, which can be hardware low-pass filtered in order to generate a lower frequency analog output.
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
Resources Management
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The DAC on {IDF_TARGET_NAME} has two channels. The channels have separate software resources and can be managed by :cpp:type:`dac_oneshot_handle_t`, :cpp:type:`dac_cosine_handle_t`, or :cpp:type:`dac_continuous_handle_t` according to the usage. Registering different modes on a same DAC channel is not allowed.
|
||||
|
||||
Direct Voltage Output (One-shot/Direct Mode)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The DAC channels in the group can convert an 8-bit digital value into the analog when :cpp:func:`dac_oneshot_output_voltage` is called (it can be called in ISR). The analog voltage is kept on the DAC channel until the next conversion starts. To start the voltage conversion, the DAC channels need to be enabled first through registering by :cpp:func:`dac_oneshot_new_channel`.
|
||||
|
||||
Continuous Wave Output (Continuous/DMA Mode)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
DAC channels can convert digital data continuously via the DMA. There are three ways to write the DAC data:
|
||||
|
||||
1. Normal writing (synchronous): Data can be transmitted at one time and kept blocked until all the data has been loaded into the DMA buffer, and the voltage is kept as the last conversion value while no more data is inputted. It is usually used to transport a long signal like an audio. To convert data continuously, the continuous channel handle need to be allocated by calling :cpp:func:`dac_continuous_new_channels` and the DMA conversion should be enabled by calling :cpp:func:`dac_continuous_enable`. Then data can be written by :cpp:func:`dac_continuous_write` synchronously. Refer to :example:`peripherals/dac/dac_continuous/dac_audio` for examples.
|
||||
2. Cyclical writing: A piece of data can be converted cyclically without blocking, and no more operation is needed after the data are loaded into the DMA buffer. But note that the inputted buffer size is limited by the number of descriptors and the DMA buffer size. It is usually used to transport short signals that need to be repeated, e.g., a sine wave. To achieve cyclical writing, call :cpp:func:`dac_continuous_write_cyclically` after the DAC continuous mode is enabled. Refer to :example:`peripherals/dac/dac_continuous/signal_generator` for examples.
|
||||
3. Asynchronous writing: Data can be transmitted asynchronously based on the event callback. :cpp:member:`dac_event_callbacks_t::on_convert_done` must be registered to use asynchronous mode. Users can get the :cpp:type:`dac_event_data_t` in the callback which contains the DMA buffer address and length, allowing them to load the data into the buffer directly. To use the asynchronous writing, call :cpp:func:`dac_continuous_register_event_callback` to register the :cpp:member:`dac_event_callbacks_t::on_convert_done` before enabling, and then :cpp:func:`dac_continuous_start_async_writing` to start the asynchronous writing. Note that once the asynchronous writing is started, the callback function will be triggered continuously. Call :cpp:func:`dac_continuous_write_asynchronously` to load the data either in a separate task or in the callback directly. Refer to :example:`peripherals/dac/dac_continuous/dac_audio` for examples.
|
||||
|
||||
The following diagram illustrates the life cycle of the DAC continuous driver and the state transitions associated with each API:
|
||||
|
||||
.. mermaid::
|
||||
|
||||
flowchart TD
|
||||
NC(["Idle (Initial State)"]) -->|"new_channels()"| REG[Registered]
|
||||
REG -->|"del_channels()"| NC
|
||||
REG -->|"enable()"| EN[Enabled]
|
||||
EN -->|"disable()"| REG
|
||||
|
||||
EN -->|"start_async_writing()"| ASYNC[Async Writing]
|
||||
ASYNC -->|"stop_async_writing()"| EN
|
||||
|
||||
EN -->|"write_cyclically()"| CYCLIC[Cyclic Writing]
|
||||
CYCLIC -->|"stop_cyclically()"| EN
|
||||
|
||||
EN -->|"write()"| SYNC[Sync Writing]
|
||||
SYNC -->|"write()"| SYNC
|
||||
SYNC -->|"on transmission complete"| EN
|
||||
|
||||
subgraph REG_APIS [Registered State APIs]
|
||||
REGCB["register_event_callbacks()"]
|
||||
end
|
||||
|
||||
subgraph ASYNC_APIS [Async Writing State APIs]
|
||||
AWRITE["write_asynchronously()"]
|
||||
end
|
||||
|
||||
REG -. can call .-> REGCB
|
||||
ASYNC -. when receiving a callback .-> AWRITE
|
||||
|
||||
.. note::
|
||||
|
||||
- For brevity, the prefix ``dac_continuous_`` is omitted from all function names in the diagram.
|
||||
- For backward compatibility, calling :cpp:func:`dac_continuous_stop_cyclically` to exit cyclic writing is optional — any API that transitions away from the Enabled state will automatically stop an ongoing cyclic conversion. However, explicitly calling :cpp:func:`dac_continuous_stop_cyclically` is recommended.
|
||||
- Sync writing requires no explicit exit — any API that transitions away from the Enabled state will automatically stop the ongoing sync writing immediately (the data not yet converted is discarded).
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
On ESP32, the DAC digital controller can be connected internally to the I2S0 and use its DMA for continuous conversion. Although the DAC only needs 8-bit data for conversion, it has to be the left-shifted 8 bits (i.e., the high 8 bits in a 16-bit slot) to satisfy the I2S communication format. By default, the driver helps to expand the data to 16-bit wide automatically. To expand manually, please disable :ref:`CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` in the menuconfig.
|
||||
|
||||
The clock of the DAC digital controller comes from I2S0 as well, so there are two clock sources for selection:
|
||||
|
||||
- :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_PLL_D2` supports frequency between 19.6 KHz to several MHz. It is the default clock which can also be selected by :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT`.
|
||||
- :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APLL` supports frequency between 648 Hz to several MHz. However, it might be occupied by other peripherals, thus not providing the required frequency. In such case, this clock source is available only if APLL still can be correctly divided into the target DAC DMA frequency.
|
||||
|
||||
.. only:: esp32s2
|
||||
|
||||
On ESP32-S2, the DAC digital controller can be connected internally to the SPI3 and use its DMA for continuous conversion.
|
||||
|
||||
The clock sources of the DAC digital controller include:
|
||||
|
||||
- :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APB` supports frequency between 77 Hz to several MHz. It is the default clock which can also be selected by :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT`.
|
||||
- :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APLL` supports frequency between 6 Hz to several MHz. However, it might be occupied by other peripherals, thus not providing the required frequency. In such case, this clock source is available only if APLL still can be correctly divided into the target DAC DMA frequency.
|
||||
|
||||
|
||||
Cosine Wave Output (Cosine Mode)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The DAC peripheral has a cosine wave generator, which can generate cosine wave on the channels. Users can specify the frequency, amplitude, and phase of the cosine wave. To output the cosine wave, please acquire the DAC to cosine mode using :cpp:func:`dac_cosine_new_channel`, and then start the cosine wave generator by :cpp:func:`dac_cosine_start`.
|
||||
|
||||
Currently, the clock source of the cosine wave generator only comes from ``RTC_FAST`` which can be selected by :cpp:enumerator:`dac_cosine_clk_src_t::DAC_COSINE_CLK_SRC_RTC_FAST`. It is also the default clock source which is the same as :cpp:enumerator:`dac_cosine_clk_src_t::DAC_COSINE_CLK_SRC_RTC_DEFAULT`.
|
||||
|
||||
Power Management
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
When the power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust or stop the clock source of DAC before entering Light-sleep mode, thus potential influence to the DAC signals may lead to false data conversion.
|
||||
|
||||
When using DAC driver in continuous mode, it can prevent the system from changing or stopping the clock source in DMA or cosine mode by acquiring a power management lock. When the clock source is generated from APB, the lock type will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`. When the clock source is APLL (only in DMA mode), it will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`. Whenever the DAC is converting (i.e., DMA or cosine wave generator is working), the driver guarantees that the power management lock is acquired after calling :cpp:func:`dac_continuous_enable`. Likewise, the driver will release the lock when :cpp:func:`dac_continuous_disable` is called.
|
||||
|
||||
.. _dac-iram-safe:
|
||||
|
||||
IRAM Safe
|
||||
^^^^^^^^^
|
||||
|
||||
By default, the DAC DMA interrupt will be deferred when the cache is disabled for reasons like writing/erasing Flash. Thus the DMA EOF interrupt will not get executed in time.
|
||||
|
||||
To avoid such case in real-time applications, you can enable the Kconfig option :ref:`CONFIG_DAC_ISR_IRAM_SAFE` which:
|
||||
|
||||
1. Enables the interrupt being serviced even when cache is disabled;
|
||||
|
||||
2. Places driver object into DRAM (in case it is linked to PSRAM by accident).
|
||||
|
||||
This allows the interrupt to run while the cache is disabled but comes at the cost of increased IRAM consumption.
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
All the public DAC APIs are guaranteed to be thread safe by the driver, which means users can call them from different RTOS tasks without protection by extra locks. Notice that the DAC driver uses mutex lock to ensure the thread safety, thus the APIs except :cpp:func:`dac_oneshot_output_voltage` are not allowed to be used in ISR.
|
||||
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_DAC_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled. See :ref:`dac-iram-safe` for more information.
|
||||
- :ref:`CONFIG_DAC_ENABLE_DEBUG_LOG` is used to enable the debug log output. Enable this option increases the firmware binary size.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
- :ref:`CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` auto expands the 8-bit data to 16-bit data in the driver to satisfy the I2S DMA format.
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
- :example:`peripherals/dac/dac_continuous/signal_generator` demonstrates how to use the DAC driver on {IDF_TARGET_NAME} to output continuous voltage in two ways: by DMA transmission and by timer interrupt, generating different waveforms such as sine, triangle, saw tooth and square wave.
|
||||
- :example:`peripherals/dac/dac_continuous/dac_audio` demonstrates how to use the DAC driver on {IDF_TARGET_NAME} to play a piece of audio stored in a buffer, with the audio being played every one second from a speaker or earphone.
|
||||
- :example:`peripherals/dac/dac_cosine_wave` demonstrates how to use the DAC driver on an {IDF_TARGET_NAME} board to output a cosine wave on both channels, which can be monitored using an oscilloscope or the ADC channels internally.
|
||||
- :example:`peripherals/dac/dac_oneshot` demonstrates how to use the DAC driver on {IDF_TARGET_NAME} to output a voltage that increases stepwise every 500 ms and resets to 0 periodically, with the output monitored via ADC or an optional oscilloscope.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/dac_oneshot.inc
|
||||
.. include-build-file:: inc/dac_cosine.inc
|
||||
.. include-build-file:: inc/dac_continuous.inc
|
||||
.. include-build-file:: inc/components/esp_driver_dac/include/driver/dac_types.inc
|
||||
.. include-build-file:: inc/components/esp_hal_ana_conv/include/hal/dac_types.inc
|
||||
@@ -0,0 +1,157 @@
|
||||
Dedicated GPIO
|
||||
==============
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The dedicated GPIO is designed for CPU interaction with GPIO matrix and IO MUX. Any GPIO that is configured as "dedicated" can be access by CPU instructions directly, which makes it easy to achieve a high GPIO flip speed, and simulate serial/parallel interface in a bit-banging way. As toggling a GPIO in this "CPU Dedicated" way costs few overhead, it would be great for cases like performance measurement using an oscilloscope.
|
||||
|
||||
|
||||
Create/Destroy GPIO Bundle
|
||||
--------------------------
|
||||
|
||||
A GPIO bundle is a group of GPIOs, which can be manipulated at the same time in one CPU cycle. The maximal number of GPIOs that a bundle can contain is limited by each CPU. What's more, the GPIO bundle has a strong relevance to the CPU which it derives from. **Any operations on the GPIO bundle should be put inside a task which is running on the same CPU core to the GPIO bundle belongs to.** Likewise, only those ISRs who are installed on the same CPU core are allowed to do operations on that GPIO bundle.
|
||||
|
||||
.. note::
|
||||
|
||||
Dedicated GPIO is more like a CPU peripheral, it has a strong relationship with CPU core. It's highly recommended to install and operate GPIO bundle in the same task, and the task should be pined to a CPU core. For example, if GPIO_A is connected to CPU_0, but the dedicated GPIO instruction is issued from CPU_1, then it's impossible to control GPIO_A.
|
||||
|
||||
To install a GPIO bundle, one needs to call :cpp:func:`dedic_gpio_new_bundle` to allocate the software resources and connect the dedicated channels to user selected GPIOs. Configurations for a GPIO bundle are covered in :cpp:type:`dedic_gpio_bundle_config_t` structure:
|
||||
|
||||
- :cpp:member:`dedic_gpio_bundle_config_t::gpio_array`: An array that contains GPIO number.
|
||||
- :cpp:member:`dedic_gpio_bundle_config_t::array_size`: Element number of :cpp:member:`dedic_gpio_bundle_config_t::gpio_array`.
|
||||
- :cpp:member:`dedic_gpio_bundle_config_t::in_en` and :cpp:member:`dedic_gpio_bundle_config_t::out_en` are used to configure whether to enable the input and output ability of the GPIO(s).
|
||||
- :cpp:member:`dedic_gpio_bundle_config_t::in_invert` and :cpp:member:`dedic_gpio_bundle_config_t::out_invert` are used to configure whether to invert the GPIO signal.
|
||||
|
||||
The following code shows how to install an output only GPIO bundle:
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
// Create bundleA, output only
|
||||
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));
|
||||
|
||||
To uninstall the GPIO bundle, you should call :cpp:func:`dedic_gpio_del_bundle`.
|
||||
|
||||
|
||||
GPIO Bundle Operations
|
||||
----------------------
|
||||
|
||||
.. list-table::
|
||||
:widths: 50 50
|
||||
:header-rows: 1
|
||||
|
||||
* - Operations
|
||||
- Functions
|
||||
* - Write to GPIOs in the bundle by mask
|
||||
- :cpp:func:`dedic_gpio_bundle_write`
|
||||
* - Read the value that output from the given GPIO bundle
|
||||
- :cpp:func:`dedic_gpio_bundle_read_out`
|
||||
* - Read the value that input to the given GPIO bundle
|
||||
- :cpp:func:`dedic_gpio_bundle_read_in`
|
||||
|
||||
.. note::
|
||||
|
||||
Using the above functions might not get a high GPIO flip speed because of the overhead of function calls and the bit operations involved inside. Users can try :ref:`manipulate_gpios_by_writing_assembly_code` instead to reduce the overhead but should take care of the thread safety by themselves.
|
||||
|
||||
.. _manipulate_gpios_by_writing_assembly_code:
|
||||
|
||||
Manipulate GPIOs by Writing Assembly Code
|
||||
------------------------------------------
|
||||
|
||||
For advanced users, they can always manipulate the GPIOs by writing assembly code or invoking CPU Low Level APIs. The usual procedure could be:
|
||||
|
||||
1. Allocate a GPIO bundle: :cpp:func:`dedic_gpio_new_bundle`
|
||||
2. Query the mask occupied by that bundle: :cpp:func:`dedic_gpio_get_out_mask` or/and :cpp:func:`dedic_gpio_get_in_mask`
|
||||
3. Call CPU LL apis (e.g., `dedic_gpio_cpu_ll_write_mask`) or write assembly code with that mask
|
||||
4. The fastest way of toggling IO is to use the dedicated "set/clear" instructions:
|
||||
|
||||
.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
|
||||
- Set bits of GPIO: ``set_bit_gpio_out imm[7:0]``
|
||||
- Clear bits of GPIO: ``clr_bit_gpio_out imm[7:0]``
|
||||
- Note: Immediate value width depends on the number of dedicated GPIO channels
|
||||
|
||||
.. only:: CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
|
||||
- Set bits of GPIO: ``csrrsi rd, csr, imm[4:0]``
|
||||
- Clear bits of GPIO: ``csrrci rd, csr, imm[4:0]``
|
||||
- Note: Can only control the lowest 4 GPIO channels
|
||||
|
||||
.. only:: esp32s2
|
||||
|
||||
For details of supported dedicated GPIO instructions, please refer to **{IDF_TARGET_NAME} Technical Reference Manual** > **IO MUX and GPIO Matrix (GPIO, IO_MUX)** [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
.. only:: esp32s3
|
||||
|
||||
For details of supported dedicated GPIO instructions, please refer to **{IDF_TARGET_NAME} Technical Reference Manual** > **Processor Instruction Extensions (PIE) (to be added later)** [`PDF <{IDF_TARGET_TRM_EN_URL}#pie>`__].
|
||||
|
||||
.. only:: not (esp32s2 or esp32s3)
|
||||
|
||||
For details of supported dedicated GPIO instructions, please refer to **{IDF_TARGET_NAME} Technical Reference Manual** > **ESP-RISC-V CPU** [`PDF <{IDF_TARGET_TRM_EN_URL}#riscvcpu>`__].
|
||||
|
||||
Some of the dedicated CPU instructions are also wrapped inside ``hal/dedic_gpio_cpu_ll.h`` as helper inline functions.
|
||||
|
||||
.. note::
|
||||
|
||||
Writing assembly code in application could make your code hard to port between targets, because those customized instructions are not guaranteed to remain the same format on different targets.
|
||||
|
||||
.. only:: SOC_DEDIC_GPIO_HAS_INTERRUPT
|
||||
|
||||
Interrupt Handling
|
||||
------------------
|
||||
|
||||
Dedicated GPIO can also trigger interrupt on specific input event. All supported events are defined in :cpp:type:`dedic_gpio_intr_type_t`.
|
||||
|
||||
One can enable and register interrupt callback by calling :cpp:func:`dedic_gpio_bundle_set_interrupt_and_callback`. The prototype of the callback function is defined in :cpp:type:`dedic_gpio_isr_callback_t`. Keep in mind, the callback should return true if there's some high priority task woken up.
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
// user defined ISR callback
|
||||
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;
|
||||
}
|
||||
|
||||
// enable positive edge interrupt on the second GPIO in the bundle (i.e., index 1)
|
||||
ESP_ERROR_CHECK(dedic_gpio_bundle_set_interrupt_and_callback(bundle, BIT(1), DEDIC_GPIO_INTR_POS_EDGE, dedic_gpio_isr_callback, sem));
|
||||
|
||||
// wait for done semaphore
|
||||
xSemaphoreTake(sem, portMAX_DELAY);
|
||||
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
.. list::
|
||||
|
||||
* Software emulation (bit banging) of the UART/I2C/SPI protocols in assembly using the dedicated GPIOs and their associated CPU instructions: :example:`peripherals/dedicated_gpio`.
|
||||
:SOC_DEDIC_GPIO_HAS_INTERRUPT: * :example:`peripherals/gpio/matrix_keyboard` demonstrates how to drive a matrix keyboard using the dedicated GPIO APIs, including manipulating the level on a group of GPIOs, triggering edge interrupt, and reading level on a group of GPIOs.
|
||||
* :example:`peripherals/dedicated_gpio/soft_i2c` demonstrates how to configure and use dedicated/fast GPIOs to emulate an I2C master, perform write-read transactions on the bus, and handle strict timing requirements by placing certain functions in IRAM.
|
||||
* :example:`peripherals/dedicated_gpio/soft_uart` demonstrates how to emulate a UART bus using dedicated/fast GPIOs on {IDF_TARGET_NAME}, which can send and receive characters on the UART bus using a TX pin and an RX pin, with the baud rate and other configurations adjustable via `menuconfig`.
|
||||
|
||||
.. only:: CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
|
||||
* :example:`peripherals/dedicated_gpio/soft_spi` demonstrates how to configure and use dedicated/fast GPIOs to emulate a full-duplex SPI bus on {IDF_TARGET_NAME}.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/dedic_gpio.inc
|
||||
@@ -0,0 +1,137 @@
|
||||
RSA Digital Signature Peripheral (RSA_DS)
|
||||
==========================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
The RSA Digital Signature Peripheral (RSA_DS) provides hardware acceleration of signing messages based on RSA. It uses pre-encrypted parameters to calculate a signature. The parameters are encrypted using HMAC as a key-derivation function. In turn, the HMAC uses eFuses as the input key.
|
||||
|
||||
.. only:: SOC_KEY_MANAGER_SUPPORTED
|
||||
|
||||
On {IDF_TARGET_NAME}, the RSA Digital Signature Peripheral (RSA_DS) can also use a key stored in the Key Manager instead of an eFuse key block. The AES encryption key can be directly deployed in the Key Manager with the type :cpp:enumerator:`ESP_KEY_MGR_DS_KEY`. Refer to :ref:`key-manager` for more details.
|
||||
|
||||
The whole process happens in hardware so that neither the decryption key for the RSA parameters nor the input key for the HMAC key derivation function can be seen by the software while calculating the signature.
|
||||
|
||||
For more detailed information on the hardware involved in the signature calculation and the registers used, see **{IDF_TARGET_NAME} Technical Reference Manual** > **RSA Digital Signature Peripheral (RSA_DS)** [`PDF <{IDF_TARGET_TRM_EN_URL}#digsig>`__].
|
||||
|
||||
|
||||
Private Key Parameters
|
||||
----------------------
|
||||
|
||||
The private key parameters for the RSA signature are stored in flash. To prevent unauthorized access, they are AES-encrypted. The HMAC module is used as a key-derivation function to calculate the AES encryption key for the private key parameters. In turn, the HMAC module uses a key from the eFuses key block which can be read-protected to prevent unauthorized access as well.
|
||||
|
||||
Upon signature calculation invocation, the software only specifies which eFuse key to use, the corresponding eFuse key purpose, the location of the encrypted RSA parameters, and the message.
|
||||
|
||||
Key Generation
|
||||
--------------
|
||||
|
||||
Both the HMAC key and the RSA private key have to be created and stored before the RSA_DS peripheral can be used. This needs to be done in software on the {IDF_TARGET_NAME} or alternatively on a host. For this context, ESP-IDF provides :cpp:func:`esp_efuse_write_block` to set the HMAC key and :cpp:func:`esp_hmac_calculate` to encrypt the private RSA key parameters.
|
||||
|
||||
You can find instructions on how to calculate and assemble the private key parameters in **{IDF_TARGET_NAME} Technical Reference Manual** > **RSA Digital Signature Peripheral (RSA_DS)** [`PDF <{IDF_TARGET_TRM_EN_URL}#digsig>`__].
|
||||
|
||||
Signature Calculation with ESP-IDF
|
||||
----------------------------------
|
||||
|
||||
For more detailed information on the workflow and the registers used, see **{IDF_TARGET_NAME} Technical Reference Manual** > **RSA Digital Signature Peripheral (RSA_DS)** [`PDF <{IDF_TARGET_TRM_EN_URL}#digsig>`__].
|
||||
|
||||
Three parameters need to be prepared to calculate the digital signature:
|
||||
|
||||
#. The eFuse key block ID which is used as the key for the HMAC
|
||||
#. The location of the encrypted private key parameters
|
||||
#. The message to be signed
|
||||
|
||||
**Low-level API (plain RSA)**
|
||||
|
||||
Since the signature calculation takes some time, there are two possible API versions to use in ESP-IDF. The first one is :cpp:func:`esp_ds_sign` and simply blocks until the calculation is finished. If software needs to do something else during the calculation, :cpp:func:`esp_ds_start_sign` can be called, followed by periodic calls to :cpp:func:`esp_ds_is_busy` to check when the calculation has finished. Once the calculation has finished, :cpp:func:`esp_ds_finish_sign` can be called to get the resulting signature.
|
||||
|
||||
The APIs :cpp:func:`esp_ds_sign` and :cpp:func:`esp_ds_start_sign` calculate a plain RSA signature with the help of the RSA_DS peripheral. This signature must be converted to an appropriate format (e.g., PKCS#1 v1.5 or PSS) for use in TLS or other protocols.
|
||||
|
||||
.. note::
|
||||
|
||||
This is only the basic RSA_DS building block; the message length is fixed. To create signatures of arbitrary messages, the input is normally a hash of the actual message, padded up to the required length.
|
||||
|
||||
**PSA Crypto driver**
|
||||
|
||||
The RSA_DS peripheral is also exposed via the **PSA Crypto RSA_DS driver**, so you can use standard PSA APIs for signing (PKCS#1 v1.5 or PSS) and RSA decryption (PKCS#1 v1.5 or OAEP). Enable ``CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL`` in ``Component config`` > ``mbedTLS``. For using the RSA_DS peripheral with ESP-TLS (e.g. TLS client authentication), see :ref:`digital-signature-with-esp-tls` in the ESP-TLS documentation.
|
||||
|
||||
.. _configure-the-ds-peripheral:
|
||||
|
||||
Configure the RSA_DS Peripheral for a TLS Connection
|
||||
----------------------------------------------------
|
||||
|
||||
The RSA_DS peripheral on {IDF_TARGET_NAME} chip must be configured before it can be used for a TLS connection. The configuration involves the following steps:
|
||||
|
||||
1) Randomly generate a 256-bit value called the ``Initialization Vector`` (IV).
|
||||
2) Randomly generate a 256-bit value called the ``HMAC_KEY``.
|
||||
3) Calculate the encrypted private key parameters from the client private key (RSA) and the parameters generated in the above steps.
|
||||
4) Then burn the 256-bit ``HMAC_KEY`` on the eFuse, which can only be read by the RSA_DS peripheral.
|
||||
|
||||
For more details, see **{IDF_TARGET_NAME} Technical Reference Manual** > **RSA Digital Signature Peripheral (RSA_DS)** [`PDF <{IDF_TARGET_TRM_EN_URL}#digsig>`__].
|
||||
|
||||
To configure the RSA_DS peripheral for development purposes, you can use the `esp-secure-cert-tool <https://pypi.org/project/esp-secure-cert-tool>`_.
|
||||
|
||||
The encrypted private key parameters obtained after the RSA_DS peripheral configuration should be stored in flash. The application needs to read the RSA_DS data from flash (e.g., through the APIs provided by the `esp_secure_cert_mgr <https://github.com/espressif/esp_secure_cert_mgr>`_ component; see the `component/README <https://github.com/espressif/esp_secure_cert_mgr#readme>`_ for more details). For using the RSA_DS peripheral with ESP-TLS, see :ref:`digital-signature-with-esp-tls`.
|
||||
|
||||
Using RSA_DS with PSA Crypto
|
||||
-----------------------------
|
||||
|
||||
To use the RSA_DS peripheral for signing or decryption in application code (outside of ESP-TLS), enable ``CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL``. Populate an ``esp_ds_data_ctx_t`` with the encrypted key data (:cpp:type:`esp_ds_data_t`), the eFuse key block ID, and the RSA key length in bits. Ensure the ``rsa_length`` field of :cpp:type:`esp_ds_data_t` is set when the key is created (e.g. via :cpp:func:`esp_ds_encrypt_params` or the RSA_DS provisioning tool). Then wrap the context in an ``esp_rsa_ds_opaque_key_t``, import it as a PSA opaque key using ``PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE``, and call ``psa_sign_hash()`` or ``psa_asymmetric_decrypt()``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "psa/crypto.h"
|
||||
#include "psa_crypto_driver_esp_rsa_ds.h"
|
||||
|
||||
// ds_ctx points to esp_ds_data_ctx_t (e.g. from secure cert or NVS)
|
||||
esp_ds_data_ctx_t *ds_ctx = ...;
|
||||
esp_rsa_ds_opaque_key_t rsa_ds_opaque_key = {
|
||||
.ds_data_ctx = ds_ctx,
|
||||
};
|
||||
|
||||
psa_key_attributes_t attrs = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_set_key_type(&attrs, PSA_KEY_TYPE_RSA_KEY_PAIR);
|
||||
psa_set_key_bits(&attrs, ds_ctx->rsa_length_bits);
|
||||
psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_HASH);
|
||||
psa_set_key_algorithm(&attrs, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256));
|
||||
psa_set_key_lifetime(&attrs, PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE);
|
||||
|
||||
psa_key_id_t key_id;
|
||||
psa_status_t status = psa_import_key(&attrs,
|
||||
(const uint8_t *)&rsa_ds_opaque_key,
|
||||
sizeof(rsa_ds_opaque_key),
|
||||
&key_id);
|
||||
psa_reset_key_attributes(&attrs);
|
||||
if (status != PSA_SUCCESS) {
|
||||
// handle error
|
||||
}
|
||||
|
||||
// Sign a hash (e.g. SHA-256 of your message)
|
||||
uint8_t hash[32] = { ... };
|
||||
uint8_t signature[256];
|
||||
size_t sig_len;
|
||||
status = psa_sign_hash(key_id, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256),
|
||||
hash, sizeof(hash), signature, sizeof(signature), &sig_len);
|
||||
|
||||
psa_destroy_key(key_id);
|
||||
|
||||
Persistent vs. Volatile RSA_DS PSA Keys
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The driver supports two PSA key lifetimes for RSA_DS keys:
|
||||
|
||||
- ``PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE`` (used in the example above) stores only pointers to the caller-supplied ``esp_ds_data_ctx_t`` and any Key Manager recovery info in the PSA key slot. The referenced buffers must remain valid until :cpp:func:`psa_destroy_key` is called. This avoids deep-copying large blobs such as :cpp:type:`esp_ds_data_t` (≈1200-1600 bytes, chip-dependent) when they already live in mmap'd flash via ``esp_secure_cert_mgr``.
|
||||
|
||||
- ``PSA_KEY_LIFETIME_ESP_RSA_DS`` (persistent) deep-copies the encrypted key material into the PSA key slot at :cpp:func:`psa_import_key` time and PSA persists it to NVS together with the rest of the key attributes. The caller is free to release the import-time buffers once :cpp:func:`psa_import_key` returns; subsequent :cpp:func:`psa_sign_hash` / :cpp:func:`psa_asymmetric_decrypt` calls retrieve the bytes back from NVS automatically. Use this lifetime when the application wants the key to survive reboots without having to reload the ``esp_ds_data_ctx_t`` from external storage on every boot.
|
||||
|
||||
Example for SSL Mutual Authentication Using RSA_DS
|
||||
---------------------------------------------------
|
||||
|
||||
The SSL mutual authentication example that previously lived under ``examples/protocols/mqtt/ssl_ds`` is now shipped with the standalone `espressif/mqtt <https://components.espressif.com/components/espressif/mqtt>`__ component. Follow the component documentation to fetch the SSL RSA_DS example and build it together with ESP-MQTT. The example continues to use ``mqtt_client`` (implemented by ESP-MQTT) to connect to ``test.mosquitto.org`` over mutual-authenticated TLS, with the TLS portion handled by ESP-TLS.
|
||||
|
||||
.. only:: SOC_KEY_MANAGER_SUPPORTED
|
||||
|
||||
In case both the :cpp:member:`esp_ds_data_ctx_t::efuse_key_id` and :cpp:member:`esp_rsa_ds_opaque_key_t::key_recovery_info` are set, the ESP-DS PSA driver prefers using the Key Manager-based DS key over the eFuse-based DS key.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/psa_crypto_driver_esp_rsa_ds_contexts.inc
|
||||
@@ -0,0 +1,158 @@
|
||||
ECDSA Digital Signature Peripheral (ECDSA_DS)
|
||||
==============================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
The ECDSA Digital Signature Peripheral (ECDSA_DS) implements the Elliptic Curve Digital Signature Algorithm (ECDSA), a variant of the Digital Signature Algorithm (DSA) that uses elliptic-curve cryptography.
|
||||
|
||||
{IDF_TARGET_NAME}'s ECDSA_DS peripheral provides a secure and efficient environment for computing ECDSA signatures. It offers fast computations while ensuring the confidentiality of the signing process to prevent information leakage. ECDSA private key used in the signing process is accessible only to the hardware peripheral, and it is not readable by software.
|
||||
|
||||
The ECDSA_DS peripheral can help to establish **Secure Device Identity** for TLS mutual authentication and similar use-cases.
|
||||
|
||||
Supported Features
|
||||
------------------
|
||||
|
||||
.. list::
|
||||
|
||||
- ECDSA digital signature generation and verification
|
||||
:SOC_ECDSA_SUPPORT_CURVE_P384: - Three different elliptic curves, namely P-192, P-256 and P-384 (FIPS 186-3 specification)
|
||||
:not SOC_ECDSA_SUPPORT_CURVE_P384: - Two different elliptic curves, namely P-192 and P-256 (FIPS 186-3 specification)
|
||||
:SOC_ECDSA_SUPPORT_CURVE_P384: - Three hash algorithms for message hash in the ECDSA operation, namely SHA-224, SHA-256 and SHA-384 (FIPS PUB 180-4 specification)
|
||||
:not SOC_ECDSA_SUPPORT_CURVE_P384: - Two hash algorithms for message hash in the ECDSA operation, namely SHA-224 and SHA-256 (FIPS PUB 180-4 specification)
|
||||
|
||||
|
||||
ECDSA_DS on {IDF_TARGET_NAME}
|
||||
-----------------------------
|
||||
|
||||
On {IDF_TARGET_NAME}, the ECDSA_DS module works with a secret key burnt into an eFuse block.
|
||||
|
||||
.. only:: SOC_KEY_MANAGER_SUPPORTED
|
||||
|
||||
On {IDF_TARGET_NAME}, the ECDSA_DS module also supports storing a secret key in the Key Manager. Refer to :ref:`key-manager` for more details.
|
||||
|
||||
This key is made completely inaccessible (default mode) for any resources outside the cryptographic modules, thus avoiding key leakage.
|
||||
|
||||
|
||||
ECDSA Key Storage
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
|
||||
ECDSA private keys are stored in eFuse key blocks. The number of key blocks required depends on the curve size:
|
||||
|
||||
- **P-256 curve**: Require one eFuse key block (256 bits)
|
||||
- **P-384 curve**: Requires two eFuse key blocks (512 bits total)
|
||||
|
||||
For curves requiring two key blocks (like P-384), configure the following fields:
|
||||
|
||||
- Set :cpp:member:`esp_tls_cfg_t::ecdsa_key_efuse_blk` to the low block number
|
||||
- Set :cpp:member:`esp_tls_cfg_t::ecdsa_key_efuse_blk_high` to the high block number
|
||||
|
||||
For single-block curves (like P-256), only set :cpp:member:`esp_tls_cfg_t::ecdsa_key_efuse_blk` and leave :cpp:member:`esp_tls_cfg_t::ecdsa_key_efuse_blk_high` as 0 or unassigned.
|
||||
|
||||
.. only:: not SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
|
||||
ECDSA private keys are stored in eFuse key blocks. One eFuse key block (256 bits) is required for P-256 curve.
|
||||
|
||||
Configure the following field:
|
||||
|
||||
- Set :cpp:member:`esp_tls_cfg_t::ecdsa_key_efuse_blk` to the block number and leave :cpp:member:`esp_tls_cfg_t::ecdsa_key_efuse_blk_high` as 0 or unassigned.
|
||||
|
||||
ECDSA key can be programmed externally through ``idf.py`` script. Here is an example of how to program the ECDSA key:
|
||||
|
||||
Using eFuses to store the ECDSA key:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
idf.py efuse-burn-key <BLOCK_NUM> </path/to/ecdsa_private_key.pem> ECDSA_KEY
|
||||
|
||||
.. only:: SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK
|
||||
|
||||
.. note::
|
||||
|
||||
Five physical eFuse blocks can be used as keys for the ECDSA_DS module: block 4 ~ block 8. E.g., for block 4 (which is the first key block) , the argument should be ``BLOCK_KEY0``.
|
||||
|
||||
.. only:: not SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK
|
||||
|
||||
.. note::
|
||||
|
||||
Six physical eFuse blocks can be used as keys for the ECDSA_DS module: block 4 ~ block 9. E.g., for block 4 (which is the first key block) , the argument should be ``BLOCK_KEY0``.
|
||||
|
||||
.. only:: SOC_KEY_MANAGER_SUPPORTED
|
||||
|
||||
Using the Key Manager to store the ECDSA key:
|
||||
|
||||
ECDSA private keys can be stored in the Key Manager. Refer to :ref:`key-manager` for more details.
|
||||
|
||||
Deploy an ECDSA key into the Key Manager and store the generated Key Recovery info in the flash memory for persistent keys.
|
||||
|
||||
Alternatively the ECDSA key can also be programmed through the application running on the target.
|
||||
|
||||
Using eFuses to store the ECDSA key:
|
||||
|
||||
Following code snippet uses :cpp:func:`esp_efuse_write_key` to set physical key block 0 in the eFuse with key purpose as :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY`:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "esp_efuse.h"
|
||||
|
||||
const uint8_t key_data[32] = { ... };
|
||||
|
||||
esp_err_t status = esp_efuse_write_key(EFUSE_BLK_KEY0,
|
||||
ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY,
|
||||
key_data, sizeof(key_data));
|
||||
|
||||
if (status == ESP_OK) {
|
||||
// written key
|
||||
} else {
|
||||
// writing key failed, maybe written already
|
||||
}
|
||||
|
||||
|
||||
.. only:: SOC_ECDSA_P192_CURVE_DEFAULT_DISABLED
|
||||
|
||||
ECDSA Curve Configuration
|
||||
-------------------------
|
||||
|
||||
.. only:: esp32h2
|
||||
|
||||
The ECDSA_DS peripheral of the ESP32-H2 supports both ECDSA-P192 and ECDSA-P256 operations. However, starting with ESP32-H2 revision 1.2, only ECDSA-P256 operations are enabled by default. You can enable ECDSA-P192 operations using the following configuration options:
|
||||
|
||||
.. only:: not esp32h2
|
||||
|
||||
The ECDSA_DS peripheral of {IDF_TARGET_NAME} supports both ECDSA-P192 and ECDSA-P256 operations, but only ECDSA-P256 operations are enabled by default. You can enable ECDSA-P192 operations through the following configuration options:
|
||||
|
||||
- :ref:`CONFIG_ESP_ECDSA_ENABLE_P192_CURVE` enables support for ECDSA-P192 curve operations, allowing the device to perform ECDSA operations with both 192-bit and 256-bit curves. However, if ECDSA-P192 operations have already been permanently disabled during eFuse write protection, enabling this option can not re-enable ECDSA-P192 curve operations.
|
||||
|
||||
- :cpp:func:`esp_efuse_enable_ecdsa_p192_curve_mode()` enables ECDSA-P192 curve operations programmatically by writing the appropriate value to the eFuse, allowing both P-192 and P-256 curve operations. Note that this API will fail if the eFuse is already write-protected.
|
||||
|
||||
.. only:: SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE
|
||||
|
||||
Deterministic Signature Generation
|
||||
-----------------------------------
|
||||
|
||||
The ECDSA_DS peripheral of {IDF_TARGET_NAME} also supports generation of deterministic signatures using deterministic derivation of the parameter K as specified in the `RFC 6979 <https://tools.ietf.org/html/rfc6979>`_ section 3.2.
|
||||
|
||||
Non-Determinisitic Signature Generation
|
||||
---------------------------------------
|
||||
|
||||
Dependency on TRNG
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ECDSA_DS peripheral relies on the hardware True Random Number Generator (TRNG) for its internal entropy requirement for generating non-deterministic signatures. During ECDSA signature creation, the algorithm requires a random integer to be generated as specified in the `RFC 6090 <https://tools.ietf.org/html/rfc6090>`_ section 5.3.2.
|
||||
|
||||
Please ensure that hardware :doc:`RNG <../system/random>` is enabled before starting ECDSA computations (primarily signing) in the application.
|
||||
|
||||
Application Outline
|
||||
-------------------
|
||||
|
||||
Please refer to the :ref:`ecdsa-peri-with-esp-tls` guide for details on how-to use the ECDSA_DS peripheral for establishing a mutually authenticated TLS connection.
|
||||
|
||||
The ECDSA_DS peripheral in Mbed TLS stack is integrated by overriding the ECDSA signing and verifying APIs. Please note that, the ECDSA_DS peripheral does not support all curves or hash algorithms, and hence for cases where the hardware requirements are not met, the implementation falls back to the software.
|
||||
|
||||
For a particular TLS context, additional APIs have been supplied to populate certain fields (e.g., private key ctx) to differentiate routing to hardware. ESP-TLS layer integrates these APIs internally and hence no additional work is required at the application layer. However, for custom use-cases please refer to API details below.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/psa_crypto_driver_esp_ecdsa_contexts.inc
|
||||
@@ -0,0 +1,174 @@
|
||||
Event Task Matrix (ETM)
|
||||
=======================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Normally, if a peripheral X needs to notify peripheral Y of a particular event, this could only be done via a CPU interrupt from peripheral X, where the CPU notifies peripheral Y on behalf of peripheral X. However, in time-critical applications, the latency introduced by CPU interrupts is non-negligible.
|
||||
|
||||
With the help of the Event Task Matrix (ETM) module, some peripherals can directly notify other peripherals of events through pre-set connections without the intervention of CPU interrupts. This allows precise and low latency synchronization between peripherals, and lessens the CPU's workload as the CPU no longer needs to handle these events.
|
||||
|
||||
.. blockdiag:: /../_static/diagrams/etm/etm_channel.diag
|
||||
:caption: ETM channels Overview
|
||||
:align: center
|
||||
|
||||
The ETM module has multiple programmable channels, they are used to connect a particular **Event** to a particular **Task**. When an event is activated, the ETM channel will trigger the corresponding task automatically.
|
||||
|
||||
Peripherals that support ETM functionality provide their or unique set of events and tasks to be connected by the ETM. An ETM channel can connect any event to any task, even looping back an event to a task on the same peripheral. However, an ETM channel can only connect one event to one task at a time (i.e., 1 to 1 relation). If you want to use different events to trigger the same task, you can set up more ETM channels.
|
||||
|
||||
Typically, with the help of the ETM module, you can implement features like:
|
||||
|
||||
- Toggle the GPIO when a timer alarm event happens
|
||||
- Start an ADC conversion when a pulse edge is detected on a GPIO
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
The following sections of this document cover the typical steps to configure and use the ETM module.
|
||||
|
||||
- :ref:`etm-channel-allocation` - describes how to install and uninstall the ETM channel.
|
||||
- :ref:`etm-event` - describes how to allocate a new ETM event handle or fetch an existing handle from various peripherals.
|
||||
- :ref:`etm-task` - describes how to allocate a new ETM task handle or fetch an existing handle from various peripherals.
|
||||
- :ref:`etm-channel-control` - describes common ETM channel control functions.
|
||||
- :ref:`etm-power-management` - describes the options and strategies provided by the driver in order to save power.
|
||||
- :ref:`etm-thread-safety` - lists which APIs are guaranteed to be thread-safe by the driver.
|
||||
- :ref:`etm-kconfig-options` - lists the supported Kconfig options that can be used to make a different effect on driver behavior.
|
||||
|
||||
.. _etm-channel-allocation:
|
||||
|
||||
ETM Channel Allocation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are many identical ETM channels in {IDF_TARGET_NAME} [1]_, and each channel is represented by :cpp:type:`esp_etm_channel_handle_t` in the software. The ETM core driver manages all available hardware resources in a pool so that you do not need to care about which channel is in use and which is not. The ETM core driver will allocate a channel for you when you call :cpp:func:`esp_etm_new_channel` and delete it when you call :cpp:func:`esp_etm_del_channel`. All requirements needed for allocating a channel are provided in :cpp:type:`esp_etm_channel_config_t`.
|
||||
|
||||
Before deleting an ETM channel, please disable it by :cpp:func:`esp_etm_channel_disable` in advance or make sure it has not been enabled yet by :cpp:func:`esp_etm_channel_enable`.
|
||||
|
||||
.. _etm-event:
|
||||
|
||||
ETM Event
|
||||
^^^^^^^^^
|
||||
|
||||
ETM Event abstracts the event source, masking the details of specific event sources, and is represented by :cpp:type:`esp_etm_event_handle_t` in the software, allowing applications to handle different types of events more easily. ETM events can be generated from a variety of peripherals, thus the way to get the event handle differs from peripherals. When an ETM event is no longer used, you should call :cpp:func:`esp_etm_channel_connect` with a ``NULL`` event handle to disconnect it and then call :cpp:func:`esp_etm_del_event` to free the event resource.
|
||||
|
||||
GPIO Events
|
||||
~~~~~~~~~~~
|
||||
|
||||
GPIO **edge** event is the most common event type, it can be generated by any GPIO pin. You can call :cpp:func:`gpio_new_etm_event` to create a GPIO event handle, with the configurations provided in :cpp:type:`gpio_etm_event_config_t`:
|
||||
|
||||
- :cpp:member:`gpio_etm_event_config_t::edge` or :cpp:member:`gpio_etm_event_config_t::edges` decides which edge(s) to trigger the event(s), supported edge types are listed in the :cpp:type:`gpio_etm_event_edge_t`.
|
||||
|
||||
You need to build a connection between the GPIO ETM event handle and the GPIO number. So you should call :cpp:func:`gpio_etm_event_bind_gpio` afterwards. Please note, only the ETM event handle that created by :cpp:func:`gpio_new_etm_event` can set a GPIO number. Calling this function with other kinds of ETM events returns :c:macro:`ESP_ERR_INVALID_ARG` error. Needless to say, this function does not help with the GPIO initialization, you still need to call :cpp:func:`gpio_config` to set the property like direction, pull up/down mode separately.
|
||||
|
||||
Other Peripheral Events
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_SYSTIMER_SUPPORT_ETM: - You can call :cpp:func:`esp_systick_new_etm_alarm_event` to get the ETM event from RTOS Systick, one per CPU core.
|
||||
:SOC_SYSTIMER_SUPPORT_ETM: - Refer to :doc:`/api-reference/system/esp_timer` for how to get the ETM event handle from esp_timer.
|
||||
:SOC_TIMER_SUPPORT_ETM: - Refer to :ref:`gptimer-etm-event-and-task` for how to get the ETM event handle from GPTimer.
|
||||
:SOC_GDMA_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/async_memcpy` for how to get the ETM event handle from async memcpy.
|
||||
:SOC_MCPWM_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/mcpwm` for how to get the ETM event handle from MCPWM.
|
||||
:SOC_ANA_CMPR_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/ana_cmpr` for how to get the ETM event handle from analog comparator.
|
||||
:SOC_TEMPERATURE_SENSOR_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/temp_sensor` for how to get the ETM event handle from temperature sensor.
|
||||
:SOC_I2S_SUPPORTS_ETM: - Refer to :doc:`/api-reference/peripherals/i2s` for how to get the ETM event handle from I2S.
|
||||
:SOC_LEDC_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/ledc` for how to get the ETM event handle from LEDC.
|
||||
|
||||
.. _etm-task:
|
||||
|
||||
ETM Task
|
||||
^^^^^^^^
|
||||
|
||||
ETM Task abstracts the task action and is represented by :cpp:type:`esp_etm_task_handle_t` in the software, allowing tasks to be managed and represented in the same way. ETM tasks can be assigned to a variety of peripherals, thus the way to get the task handle differs from peripherals. When an ETM task is no longer used, you should call :cpp:func:`esp_etm_channel_connect` with a ``NULL`` task handle to disconnect it and then call :cpp:func:`esp_etm_del_task` to free the task resource.
|
||||
|
||||
GPIO Tasks
|
||||
~~~~~~~~~~
|
||||
|
||||
GPIO task is the most common task type. One GPIO can take one or more GPIO ETM task actions, and one GPIO ETM task action can even manage multiple GPIOs. When the task gets activated by the ETM channel, all managed GPIOs can set/clear/toggle at the same time. You can call :cpp:func:`gpio_new_etm_task` to create a GPIO task handle, with the configurations provided in :cpp:type:`gpio_etm_task_config_t`:
|
||||
|
||||
- :cpp:member:`gpio_etm_task_config_t::action` or :cpp:member:`gpio_etm_task_config_t::actions` decides what GPIO action(s) would be taken by the ETM task. Supported actions are listed in the :cpp:type:`gpio_etm_task_action_t`. If one GPIO needs to take more than one actions, the action tasks have to be created in one :cpp:func:`gpio_new_etm_task` call with filling the actions into the array of :cpp:member:`gpio_etm_task_config_t::actions`.
|
||||
|
||||
To build a connection between the GPIO ETM task and the GPIO number, you should call :cpp:func:`gpio_etm_task_add_gpio`. You can call this function by several times if you want the task handle to manage more GPIOs. Please note, only the ETM task handle that created by :cpp:func:`gpio_new_etm_task` can manage a GPIO. Calling this function with other kinds of ETM tasks returns :c:macro:`ESP_ERR_INVALID_ARG` error. Needless to say, this function does not help with the GPIO initialization, you still need to call :cpp:func:`gpio_config` to set the property like direction, pull up/down mode separately.
|
||||
|
||||
Before you call :cpp:func:`esp_etm_del_task` to delete the GPIO ETM task, make sure that all previously added GPIOs are removed by :cpp:func:`gpio_etm_task_rm_gpio` in advance.
|
||||
|
||||
Other Peripheral Tasks
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_TIMER_SUPPORT_ETM: - Refer to :ref:`gptimer-etm-event-and-task` for how to get the ETM task handle from GPTimer.
|
||||
:SOC_TEMPERATURE_SENSOR_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/temp_sensor` for how to get the ETM task handle from temperature sensor.
|
||||
:SOC_I2S_SUPPORTS_ETM: - Refer to :doc:`/api-reference/peripherals/i2s` for how to get the ETM task handle from I2S.
|
||||
:SOC_LEDC_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/ledc` for how to get the ETM task handle from LEDC.
|
||||
|
||||
.. _etm-channel-control:
|
||||
|
||||
ETM Channel Control
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Connect Event and Task
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
An ETM event has no association with an ETM task, until they are connected to the same ETM channel by calling :cpp:func:`esp_etm_channel_connect`. Especially, calling the function with a ``NULL`` task/event handle means disconnecting the channel from any task or event. Note that, this function can be called either before or after the channel is enabled. But calling this function at runtime to change the connection can be dangerous, because the channel may be in the middle of a cycle, and the new connection may not take effect immediately.
|
||||
|
||||
Enable and Disable Channel
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can call :cpp:func:`esp_etm_channel_enable` and :cpp:func:`esp_etm_channel_disable` to enable and disable the ETM channel from working.
|
||||
|
||||
ETM Channel Profiling
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To check if the ETM channels are set with proper events and tasks, you can call :cpp:func:`esp_etm_dump` to dump all working ETM channels with their associated events and tasks. The dumping format is like:
|
||||
|
||||
::
|
||||
|
||||
===========ETM Dump Start==========
|
||||
channel 0: event 48 ==> task 17
|
||||
channel 1: event 48 ==> task 90
|
||||
channel 2: event 48 ==> task 94
|
||||
===========ETM Dump End============
|
||||
|
||||
The digital ID printed in the dump information is defined in the ``soc/soc_etm_source.h`` file.
|
||||
|
||||
.. _etm-power-management:
|
||||
|
||||
Power Management
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
When power management is enabled, i.e., :ref:`CONFIG_PM_ENABLE` is on, the system may adjust or disable the clock source, and power off the ETM peripheral before going to sleep. As a result, the existing connection between events and tasks will be lost, and the ETM channels can't work correctly after wake up. So by default, the driver will acquire a power management lock internally to forbid the system from powering off the ETM peripheral.
|
||||
|
||||
.. only:: SOC_ETM_SUPPORT_SLEEP_RETENTION
|
||||
|
||||
If you want to save more power, you can set :cpp:member:`esp_etm_channel_config_t::etm_chan_flags::allow_pd` to ``true``. Then ETM registers will be backed up before sleep and restored after wake up. Please note, enabling this option will increase the memory consumption for saving the register context.
|
||||
|
||||
.. _etm-thread-safety:
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The ETM core driver is thread-safe.
|
||||
|
||||
- Factory functions (for example, :cpp:func:`esp_etm_new_channel`, :cpp:func:`gpio_new_etm_task`, and other ``*_new_etm_*`` creators) are thread-safe and can be called from different RTOS tasks concurrently.
|
||||
- Channel control APIs (for example, :cpp:func:`esp_etm_channel_connect`, :cpp:func:`esp_etm_channel_enable`, :cpp:func:`esp_etm_channel_disable`, :cpp:func:`esp_etm_del_channel`) are also thread-safe. Concurrent operations on different channels are safe. Concurrent operations on the same channel are serialized internally; if an operation is incompatible with the current channel state, it will return :c:macro:`ESP_ERR_INVALID_STATE`.
|
||||
- No functions are allowed to run within the ISR environment.
|
||||
|
||||
.. _etm-kconfig-options:
|
||||
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_ETM_ENABLE_DEBUG_LOG` is used to enable the debug log output. Enabling this option increases the firmware binary size as well.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_etm.inc
|
||||
.. include-build-file:: inc/gpio_etm.inc
|
||||
.. include-build-file:: inc/esp_systick_etm.inc
|
||||
|
||||
.. [1]
|
||||
Different ESP chip series might have different numbers of ETM channels. For more details, please refer to *{IDF_TARGET_NAME} Technical Reference Manual* > Chapter **Event Task Matrix (ETM)** [`PDF <{IDF_TARGET_TRM_EN_URL}#evntaskmatrix>`__]. The driver does not forbid you from applying for more channels, but it will return an error when all available hardware resources are used up. Please always check the return value when doing channel allocation (i.e., :cpp:func:`esp_etm_new_channel`).
|
||||
@@ -0,0 +1,169 @@
|
||||
GPIO & RTC GPIO
|
||||
===============
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
GPIO Summary
|
||||
------------
|
||||
|
||||
.. include:: gpio/{IDF_TARGET_PATH_NAME}.inc
|
||||
:start-after: gpio-summary
|
||||
:end-before: ---
|
||||
|
||||
.. only:: SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
|
||||
|
||||
.. only:: not SOC_LP_PERIPHERALS_SUPPORTED
|
||||
|
||||
There is also separate "RTC GPIO" support, which functions when GPIOs are routed to the "RTC" low-power and analog subsystem. These pin functions can be used when:
|
||||
|
||||
.. only:: SOC_LP_PERIPHERALS_SUPPORTED
|
||||
|
||||
There is also separate "RTC GPIO" support, which functions when GPIOs are routed to the "RTC" low-power, analog subsystem, and Low-Power(LP) peripherals. These pin functions can be used when:
|
||||
|
||||
.. list::
|
||||
|
||||
- In Deep-sleep mode
|
||||
:SOC_ULP_FSM_SUPPORTED: - The :doc:`Ultra Low Power FSM co-processor <../../api-reference/system/ulp>` is running
|
||||
:SOC_RISCV_COPROC_SUPPORTED: - The :doc:`Ultra Low Power RISC-V co-processor <../../api-reference/system/ulp-risc-v>` is running
|
||||
:SOC_LP_CORE_SUPPORTED: - The :doc:`Ultra Low Power LP-Core co-processor <../../api-reference/system/ulp-lp-core>` is running
|
||||
- Analog functions such as ADC/DAC/etc are in use
|
||||
:SOC_LP_PERIPHERALS_SUPPORTED: - LP peripherals, such as LP_UART, LP_I2C, are in use
|
||||
|
||||
IO Configuration
|
||||
----------------
|
||||
|
||||
An IO can be used in two ways:
|
||||
|
||||
- As a simple GPIO input to read the level on the pin, or as a simple GPIO output to output the desired level on the pin.
|
||||
- As a peripheral signal input/output.
|
||||
|
||||
IDF peripheral drivers always take care of the necessary IO configurations that need to be applied onto the pins, so that they can be used as the peripheral signal inputs or outputs. This means the users usually only need to be responsible for configuring the IOs as simple inputs or outputs. :cpp:func:`gpio_config` is an all-in-one API that can be used to configure the I/O mode, internal pull-up/pull-down resistors, etc. for pins, including the ones reused by the USB PHY.
|
||||
|
||||
In some applications, an IO pin can serve dual purposes. For example, the IO, which outputs a LEDC PWM signal, can also act as a GPIO input to generate interrupts or GPIO ETM events. Careful handling on the configuration step is necessary for such dual use of IO pins cases. :cpp:func:`gpio_config` is an API that overwrites all the current configurations, so it must be called to set the pin mode to :cpp:enumerator:`gpio_mode_t::GPIO_MODE_INPUT` before calling the LEDC driver API which connects the output signal to the pin. As an alternative, if no other configuration is needed other than making the pin input enabled, :cpp:func:`gpio_input_enable` can be the one to call at any time to achieve the same purpose.
|
||||
|
||||
Check Current Configuration of IOs
|
||||
----------------------------------
|
||||
|
||||
GPIO driver offers a dump function :cpp:func:`gpio_dump_io_configuration` to show the current configurations of IOs, such as pull-up/pull-down, input/output enable, pin mapping, etc. Below is an example of how to dump the configuration of GPIO4, GPIO18, and GPIO26:
|
||||
|
||||
::
|
||||
|
||||
gpio_dump_io_configuration(stdout, (1ULL << 4) | (1ULL << 18) | (1ULL << 26));
|
||||
|
||||
The dump will be like this:
|
||||
|
||||
::
|
||||
|
||||
================IO DUMP Start================
|
||||
IO[4] -
|
||||
Pullup: 1, Pulldown: 0, DriveCap: 2
|
||||
InputEn: 1, OutputEn: 0, OpenDrain: 0
|
||||
FuncSel: 1 (GPIO)
|
||||
GPIO Matrix SigIn ID: (simple GPIO input)
|
||||
SleepSelEn: 1
|
||||
|
||||
IO[18] -
|
||||
Pullup: 0, Pulldown: 0, DriveCap: 2
|
||||
InputEn: 0, OutputEn: 1, OpenDrain: 0
|
||||
FuncSel: 1 (GPIO)
|
||||
GPIO Matrix SigOut ID: 256 (simple GPIO output)
|
||||
SleepSelEn: 1
|
||||
|
||||
IO[26] **RESERVED** -
|
||||
Pullup: 1, Pulldown: 0, DriveCap: 2
|
||||
InputEn: 1, OutputEn: 0, OpenDrain: 0
|
||||
FuncSel: 0 (IOMUX)
|
||||
SleepSelEn: 1
|
||||
|
||||
=================IO DUMP End==================
|
||||
|
||||
In addition, if you would like to dump the configurations of all IOs, you can use:
|
||||
|
||||
::
|
||||
|
||||
gpio_dump_io_configuration(stdout, SOC_GPIO_VALID_GPIO_MASK);
|
||||
|
||||
If an IO pin is routed to a peripheral signal through the GPIO matrix, the signal ID printed in the dump information is defined in the :component_file:`soc/{IDF_TARGET_PATH_NAME}/include/soc/gpio_sig_map.h` header file. The word ``**RESERVED**`` indicates the IO is occupied by either SPI flash or PSRAM. It is strongly not recommended to reconfigure them for other application purposes.
|
||||
|
||||
Do not rely on the default configurations values in the Technical Reference Manual, because it may be changed in the bootloader or application startup code before app_main.
|
||||
|
||||
.. only:: SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER or SOC_GPIO_FLEX_GLITCH_FILTER_NUM
|
||||
|
||||
GPIO Glitch Filter
|
||||
------------------
|
||||
|
||||
The {IDF_TARGET_NAME} chip features hardware filters to remove unwanted glitch pulses from the input GPIO, which can help reduce false triggering of the interrupt and prevent a noise being routed to the peripheral side.
|
||||
|
||||
.. only:: SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER
|
||||
|
||||
Each GPIO can be configured with a glitch filter, which can be used to filter out pulses shorter than **two** sample clock cycles. The duration of the filter is not configurable. The sample clock is the clock source of the IO_MUX. In the driver, we call this kind of filter as ``pin glitch filter``. You can create the filter handle by calling :cpp:func:`gpio_new_pin_glitch_filter`. All the configurations for a pin glitch filter are listed in the :cpp:type:`gpio_pin_glitch_filter_config_t` structure.
|
||||
|
||||
- :cpp:member:`gpio_pin_glitch_filter_config_t::gpio_num` sets the GPIO number to enable the glitch filter.
|
||||
|
||||
.. only:: SOC_GPIO_FLEX_GLITCH_FILTER_NUM
|
||||
|
||||
{IDF_TARGET_FLEX_GLITCH_FILTER_NUM:default="8"}
|
||||
|
||||
{IDF_TARGET_NAME} provides {IDF_TARGET_FLEX_GLITCH_FILTER_NUM} flexible glitch filters, whose duration is configurable. We refer to this kind of filter as ``flex flitch filter``. Each of them can be applied to any input GPIO. However, applying multiple filters to the same GPIO does not make difference from one. You can create the filter handle by calling :cpp:func:`gpio_new_flex_glitch_filter`. All the configurations for a flexible glitch filter are listed in the :cpp:type:`gpio_flex_glitch_filter_config_t` structure.
|
||||
|
||||
- :cpp:member:`gpio_flex_glitch_filter_config_t::gpio_num` sets the GPIO that will be applied to the flex glitch filter.
|
||||
- :cpp:member:`gpio_flex_glitch_filter_config_t::window_width_ns` and :cpp:member:`gpio_flex_glitch_filter_config_t::window_thres_ns` are the key parameters of the glitch filter. During :cpp:member:`gpio_flex_glitch_filter_config_t::window_width_ns`, any pulse whose width is shorter than :cpp:member:`gpio_flex_glitch_filter_config_t::window_thres_ns` will be discarded. Please note that, you can not set :cpp:member:`gpio_flex_glitch_filter_config_t::window_thres_ns` bigger than :cpp:member:`gpio_flex_glitch_filter_config_t::window_width_ns`.
|
||||
|
||||
.. only:: SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER and SOC_GPIO_FLEX_GLITCH_FILTER_NUM
|
||||
|
||||
Please note, the ``pin glitch filter`` and ``flex glitch filter`` are independent. You can enable both of them for the same GPIO.
|
||||
|
||||
The glitch filter is disabled by default, and can be enabled by calling :cpp:func:`gpio_glitch_filter_enable`. To recycle the filter, you can call :cpp:func:`gpio_del_glitch_filter`. Please note, before deleting the filter, you should disable it first by calling :cpp:func:`gpio_glitch_filter_disable`.
|
||||
|
||||
|
||||
.. only:: SOC_GPIO_SUPPORT_PIN_HYS_FILTER
|
||||
|
||||
GPIO Hysteresis Filter
|
||||
----------------------
|
||||
|
||||
{IDF_TARGET_NAME} support the hardware hysteresis of the input pin, which can reduce the GPIO interrupt shoot by accident due to unstable sampling when the input voltage is near the criteria of logic 0 and 1, especially when the input logic level conversion is slow or the voltage setup time is too long.
|
||||
|
||||
.. only:: SOC_GPIO_SUPPORT_PIN_HYS_CTRL_BY_EFUSE
|
||||
|
||||
Each pin can enable hysteresis function independently. By default, it controlled by eFuse and been closed, but it can also be enabled or disabled by software manually. You can select the hysteresis control mode by configuring :cpp:member:`gpio_config_t::hys_ctrl_mode`. Hysteresis control mode is set along with all the other GPIO configurations in :cpp:func:`gpio_config`.
|
||||
|
||||
.. note::
|
||||
|
||||
When the hysteresis function is controlled by eFuse, this feature can still be controlled independently for each pin, you need to `burn the eFuse <https://docs.espressif.com/projects/esptool/en/latest/esp32/espefuse/index.html>`_ to enable the hysteresis function on specific GPIO additionally.
|
||||
|
||||
.. only:: not SOC_GPIO_SUPPORT_PIN_HYS_CTRL_BY_EFUSE
|
||||
|
||||
Each pin can enable hysteresis function independently. By default, the function is not enabled. You can select the hysteresis control mode by configuring :cpp:member:`gpio_config_t::hys_ctrl_mode`. Hysteresis control mode is set along with all the other GPIO configurations in :cpp:func:`gpio_config`.
|
||||
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
.. list::
|
||||
|
||||
* :example:`peripherals/gpio/generic_gpio` demonstrates how to configure GPIO to generate pulses and use it with interruption.
|
||||
:esp32s2: * :example:`peripherals/gpio/matrix_keyboard` demonstrates how to drive a common matrix keyboard using the dedicated GPIO APIs, including manipulating the level on a group of GPIOs, triggering edge interrupt, and reading level on a group of GPIOs.
|
||||
|
||||
|
||||
API Reference - Normal GPIO
|
||||
---------------------------
|
||||
|
||||
.. include-build-file:: inc/gpio.inc
|
||||
.. include-build-file:: inc/gpio_types.inc
|
||||
|
||||
|
||||
.. only:: SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
|
||||
|
||||
API Reference - RTC GPIO
|
||||
------------------------
|
||||
|
||||
.. include-build-file:: inc/rtc_io.inc
|
||||
.. include-build-file:: inc/lp_io.inc
|
||||
.. include-build-file:: inc/rtc_io_types.inc
|
||||
|
||||
.. only:: SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER or SOC_GPIO_FLEX_GLITCH_FILTER_NUM
|
||||
|
||||
API Reference - GPIO Glitch Filter
|
||||
----------------------------------
|
||||
|
||||
.. include-build-file:: inc/gpio_filter.inc
|
||||
@@ -0,0 +1,205 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 34 physical GPIO pins (GPIO0 ~ GPIO19, GPIO21 ~ GPIO23, GPIO25 ~ GPIO27, and GPIO32 ~ GPIO39). Each pin can be used as a general-purpose I/O, or be connected to an internal peripheral signal. Through IO MUX, RTC IO MUX and the GPIO matrix, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 8 12 12 20
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- RTC GPIO
|
||||
- Comments
|
||||
|
||||
* - GPIO0
|
||||
- ADC2_CH1
|
||||
- RTC_GPIO11
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO1
|
||||
-
|
||||
-
|
||||
- TXD
|
||||
|
||||
* - GPIO2
|
||||
- ADC2_CH2
|
||||
- RTC_GPIO12
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO3
|
||||
-
|
||||
-
|
||||
- RXD
|
||||
|
||||
* - GPIO4
|
||||
- ADC2_CH0
|
||||
- RTC_GPIO10
|
||||
-
|
||||
|
||||
* - GPIO5
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO6
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO7
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO8
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO9
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO10
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO11
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO12
|
||||
- ADC2_CH5
|
||||
- RTC_GPIO15
|
||||
- Strapping pin; JTAG
|
||||
|
||||
* - GPIO13
|
||||
- ADC2_CH4
|
||||
- RTC_GPIO14
|
||||
- JTAG
|
||||
|
||||
* - GPIO14
|
||||
- ADC2_CH6
|
||||
- RTC_GPIO16
|
||||
- JTAG
|
||||
|
||||
* - GPIO15
|
||||
- ADC2_CH3
|
||||
- RTC_GPIO13
|
||||
- Strapping pin; JTAG
|
||||
|
||||
* - GPIO16
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO17
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO18
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO19
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO21
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO22
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO23
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO25
|
||||
- ADC2_CH8
|
||||
- RTC_GPIO6
|
||||
-
|
||||
|
||||
* - GPIO26
|
||||
- ADC2_CH9
|
||||
- RTC_GPIO7
|
||||
-
|
||||
|
||||
* - GPIO27
|
||||
- ADC2_CH7
|
||||
- RTC_GPIO17
|
||||
-
|
||||
|
||||
* - GPIO32
|
||||
- ADC1_CH4
|
||||
- RTC_GPIO9
|
||||
-
|
||||
|
||||
* - GPIO33
|
||||
- ADC1_CH5
|
||||
- RTC_GPIO8
|
||||
-
|
||||
|
||||
* - GPIO34
|
||||
- ADC1_CH6
|
||||
- RTC_GPIO4
|
||||
- GPI
|
||||
|
||||
* - GPIO35
|
||||
- ADC1_CH7
|
||||
- RTC_GPIO5
|
||||
- GPI
|
||||
|
||||
* - GPIO36
|
||||
- ADC1_CH0
|
||||
- RTC_GPIO0
|
||||
- GPI
|
||||
|
||||
* - GPIO37
|
||||
- ADC1_CH1
|
||||
- RTC_GPIO1
|
||||
- GPI
|
||||
|
||||
* - GPIO38
|
||||
- ADC1_CH2
|
||||
- RTC_GPIO2
|
||||
- GPI
|
||||
|
||||
* - GPIO39
|
||||
- ADC1_CH3
|
||||
- RTC_GPIO3
|
||||
- GPI
|
||||
|
||||
.. note::
|
||||
|
||||
- Strapping pin: GPIO0, GPIO2, GPIO5, GPIO12 (MTDI), and GPIO15 (MTDO) are strapping pins. For more information, please refer to `ESP32 datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`_.
|
||||
- SPI0/1: GPIO6-11 and GPIO16-17 are usually connected to the SPI flash and PSRAM integrated on the module and therefore should not be used for other purposes.
|
||||
- JTAG: GPIO12-15 are usually used for inline debug.
|
||||
- GPI: GPIO34-39 can only be set as input mode and do not have software-enabled pullup or pulldown functions.
|
||||
- TXD & RXD are usually used for flashing and debugging.
|
||||
- ADC2: ADC2 pins cannot be used when Wi-Fi is used. So, if you are having trouble getting the value from an ADC2 GPIO while using Wi-Fi, you may consider using an ADC1 GPIO instead, which should solve your problem. For more details, please refer to :ref:`Hardware Limitations of ADC Continuous Mode <hardware_limitations_adc_continuous>` and :ref:`Hardware Limitations of ADC Oneshot Mode <hardware_limitations_adc_oneshot>`.
|
||||
- Please do not use the interrupt of GPIO36 and GPIO39 when using ADC or Wi-Fi and Bluetooth with sleep mode enabled. Please refer to `ESP32 ECO and Workarounds for Bugs <https://espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf>`_ > GPIO-3.11 for the detailed description of the issue.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,116 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 21 physical GPIO pins (GPIO0 ~ GPIO20). For chip variants with an SiP flash built in, GPIO11 ~ GPIO17 are dedicated to connecting the SiP flash; therefore, only 14 GPIO pins are available.
|
||||
|
||||
Each pin can be used as a general-purpose I/O, or to be connected to an internal peripheral signal. Through GPIO matrix and IO MUX, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 12 12 22
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- Comments
|
||||
|
||||
* - GPIO0
|
||||
- ADC1_CH0
|
||||
- RTC
|
||||
|
||||
* - GPIO1
|
||||
- ADC1_CH1
|
||||
- RTC
|
||||
|
||||
* - GPIO2
|
||||
- ADC1_CH2
|
||||
- RTC
|
||||
|
||||
* - GPIO3
|
||||
- ADC1_CH3
|
||||
- RTC
|
||||
|
||||
* - GPIO4
|
||||
- ADC1_CH4
|
||||
- RTC
|
||||
|
||||
* - GPIO5
|
||||
-
|
||||
- RTC
|
||||
|
||||
* - GPIO6
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO7
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO8
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO9
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO10
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO11
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO12
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO13
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO14
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO15
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO16
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO17
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO18
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO19
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO20
|
||||
-
|
||||
-
|
||||
|
||||
.. note::
|
||||
|
||||
- Strapping pin: GPIO8 and GPIO9 are strapping pins. For more information, please refer to `ESP8684 datasheet <https://www.espressif.com/sites/default/files/documentation/esp8684_datasheet_en.pdf>`_.
|
||||
- SPI0/1: GPIO12-17 are usually used for SPI flash and not recommended for other uses.
|
||||
- RTC: GPIO0-5 can be used to wake up the chip from Deep-sleep mode. Other GPIOs can only wake up the chip from Light-sleep mode. For more information, please refer to Section :ref:`Wakeup Sources<api-reference-wakeup-source>`.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,119 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 22 physical GPIO pins (GPIO0 ~ GPIO21). Each pin can be used as a general-purpose I/O, or be connected to an internal peripheral signal. Through GPIO matrix and IO MUX, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 12 12 22
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- Comment
|
||||
|
||||
* - GPIO0
|
||||
- ADC1_CH0
|
||||
- RTC
|
||||
|
||||
* - GPIO1
|
||||
- ADC1_CH1
|
||||
- RTC
|
||||
|
||||
* - GPIO2
|
||||
- ADC1_CH2
|
||||
- Strapping pin;RTC
|
||||
|
||||
* - GPIO3
|
||||
- ADC1_CH3
|
||||
- RTC
|
||||
|
||||
* - GPIO4
|
||||
- ADC1_CH4
|
||||
- RTC
|
||||
|
||||
* - GPIO5
|
||||
- ADC2_CH0
|
||||
- RTC
|
||||
|
||||
* - GPIO6
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO7
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO8
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO9
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO10
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO11
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO12
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO13
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO14
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO15
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO16
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO17
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO18
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO19
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO20
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO21
|
||||
-
|
||||
-
|
||||
|
||||
.. note::
|
||||
|
||||
- Strapping pin: GPIO2, GPIO8 and GPIO9 are strapping pins. For more information, please refer to `ESP32-C3 Datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`_.
|
||||
- SPI0/1: GPIO12 ~ GPIO17 are usually used for SPI flash and are not recommended for other uses.
|
||||
- USB-JTAG: GPIO18 and GPIO19 are used by USB-JTAG by default. If they are reconfigured to operate as normal GPIOs, USB-JTAG functionality will be disabled.
|
||||
- RTC: GPIO0 ~ GPIO5 can be used to wake up the chip from Deep-sleep mode. Other GPIOs can only wake up the chip from Light-sleep mode. For more information, please refer to Section :ref:`Wakeup Sources<api-reference-wakeup-source>`.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,176 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 29 physical GPIO pins (GPIO0 ~ GPIO28). Each pin can be used as a general-purpose I/O, or to be connected to an internal peripheral signal. Through GPIO matrix and IO MUX, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 8 12 12 20
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- LP GPIO
|
||||
- Comments
|
||||
|
||||
* - GPIO0
|
||||
-
|
||||
- LP_GPIO0
|
||||
-
|
||||
|
||||
* - GPIO1
|
||||
- ADC1_CH0
|
||||
- LP_GPIO1
|
||||
-
|
||||
|
||||
* - GPIO2
|
||||
- ADC1_CH1
|
||||
- LP_GPIO2
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO3
|
||||
- ADC1_CH2
|
||||
- LP_GPIO3
|
||||
-
|
||||
|
||||
* - GPIO4
|
||||
- ADC1_CH3
|
||||
- LP_GPIO4
|
||||
-
|
||||
|
||||
* - GPIO5
|
||||
- ADC1_CH4
|
||||
- LP_GPIO5
|
||||
-
|
||||
|
||||
* - GPIO6
|
||||
- ADC1_CH5
|
||||
- LP_GPIO6
|
||||
-
|
||||
|
||||
* - GPIO7
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO8
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO9
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO10
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO11
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO12
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO13
|
||||
-
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO14
|
||||
-
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO15
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO16
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO17
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO18
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO19
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO20
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO21
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO22
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO23
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO24
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO25
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO26
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO27
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO28
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
.. note::
|
||||
|
||||
- Strapping pin: GPIO2, GPIO7, GPIO25, GPIO27, and GPIO28 are strapping pins. For more information, please refer to `datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__.
|
||||
- SPI0/1: GPIO16 ~ GPIO22 are usually used for SPI flash and PSRAM, they're not recommended for other uses.
|
||||
- USB-JTAG: GPIO13 and GPIO14 are used by USB-JTAG by default. If they are reconfigured to operate as normal GPIOs, USB-JTAG functionality will be disabled.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,188 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 31 physical GPIO pins (GPIO0 ~ GPIO30). Each pin can be used as a general-purpose I/O, or to be connected to an internal peripheral signal. Through GPIO matrix and IO MUX, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 8 12 12 20
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- LP GPIO
|
||||
- Comments
|
||||
|
||||
* - GPIO0
|
||||
- ADC1_CH0
|
||||
- LP_GPIO0
|
||||
-
|
||||
|
||||
* - GPIO1
|
||||
- ADC1_CH1
|
||||
- LP_GPIO1
|
||||
-
|
||||
|
||||
* - GPIO2
|
||||
- ADC1_CH2
|
||||
- LP_GPIO2
|
||||
-
|
||||
|
||||
* - GPIO3
|
||||
- ADC1_CH3
|
||||
- LP_GPIO3
|
||||
-
|
||||
|
||||
* - GPIO4
|
||||
- ADC1_CH4
|
||||
- LP_GPIO4
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO5
|
||||
- ADC1_CH5
|
||||
- LP_GPIO5
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO6
|
||||
- ADC1_CH6
|
||||
- LP_GPIO6
|
||||
-
|
||||
|
||||
* - GPIO7
|
||||
-
|
||||
- LP_GPIO7
|
||||
-
|
||||
|
||||
* - GPIO8
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO9
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO10
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO11
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO12
|
||||
-
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO13
|
||||
-
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO14
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO15
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO16
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO17
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO18
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO19
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO20
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO21
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO22
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO23
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO24
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO25
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO26
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO27
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO28
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO29
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO30
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
.. note::
|
||||
|
||||
- Strapping pin: GPIO4, GPIO5, GPIO8, GPIO9, and GPIO15 are strapping pins. For more information, please refer to `datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__.
|
||||
- SPI0/1: GPIO24 ~ GPIO30 are usually used for SPI flash and not recommended for other uses.
|
||||
- USB-JTAG: GPIO12 and GPIO13 are used by USB-JTAG by default. If they are reconfigured to operate as normal GPIOs, USB-JTAG functionality will be disabled.
|
||||
- For chip variants with an SiP flash built in, GPIO24 ~ GPIO30 are dedicated to connecting the SiP flash; GPIO10 ~ GPIO11 are not led out to any chip pins; therefore, only the remaining 22 GPIO pins are available.
|
||||
- For chip variants without an in-package flash, GPIO14 is not led out to any chip pins.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,181 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 30 physical GPIO pins (GPIO0 ~ GPIO29). Each pin can be used as a general-purpose I/O, or to be connected to an internal peripheral signal. Through GPIO matrix and IO MUX, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 8 12 12 20
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- LP GPIO
|
||||
- Comments
|
||||
|
||||
* - GPIO0
|
||||
-
|
||||
- LP_GPIO0
|
||||
-
|
||||
|
||||
* - GPIO1
|
||||
- ADC1_CH0
|
||||
- LP_GPIO1
|
||||
-
|
||||
|
||||
* - GPIO2
|
||||
-
|
||||
- LP_GPIO2
|
||||
-
|
||||
|
||||
* - GPIO3
|
||||
- ADC1_CH1
|
||||
- LP_GPIO3
|
||||
-
|
||||
|
||||
* - GPIO4
|
||||
- ADC1_CH2
|
||||
- LP_GPIO4
|
||||
-
|
||||
|
||||
* - GPIO5
|
||||
- ADC1_CH3
|
||||
- LP_GPIO5
|
||||
-
|
||||
|
||||
* - GPIO6
|
||||
-
|
||||
- LP_GPIO6
|
||||
-
|
||||
|
||||
* - GPIO7
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO8
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO9
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO10
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO11
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO12
|
||||
-
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO13
|
||||
-
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO14
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO15
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO16
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO17
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO18
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO19
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO20
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO21
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO22
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO23
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO24
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO25
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO26
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO27
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO28
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO29
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
.. note::
|
||||
|
||||
- Some pins are used as strapping pins, which can be used to select in which boot mode to load the chip, etc.. The details can be found in `datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`_ > ``Strapping Pins``.
|
||||
- SPI0/1: GPIO14 ~ GPIO17 and GPIO19 ~ GPIO21 are usually used for SPI flash and PSRAM, they're not recommended for other uses.
|
||||
- USB-JTAG: GPIO12 and GPIO13 are used by USB-JTAG by default. If they are reconfigured to operate as normal GPIOs, USB-JTAG functionality will be disabled.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,144 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 28 physical GPIO pins (GPIO0 ~ GPIO27). Each pin can be used as a general-purpose I/O, or to be connected to an internal peripheral signal. Through GPIO matrix and IO MUX, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 8 12 20
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- Comments
|
||||
|
||||
* - GPIO0
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO1
|
||||
- ADC1_CH0
|
||||
-
|
||||
|
||||
* - GPIO2
|
||||
- ADC1_CH1
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO3
|
||||
- ADC1_CH2
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO4
|
||||
- ADC1_CH3
|
||||
-
|
||||
|
||||
* - GPIO5
|
||||
- ADC1_CH4
|
||||
-
|
||||
|
||||
* - GPIO6
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO7
|
||||
-
|
||||
- RTC
|
||||
|
||||
* - GPIO8
|
||||
-
|
||||
- Strapping pin, RTC
|
||||
|
||||
* - GPIO9
|
||||
-
|
||||
- Strapping pin, RTC
|
||||
|
||||
* - GPIO10
|
||||
- ANA_CMPR_CH0 reference voltage
|
||||
- RTC
|
||||
|
||||
* - GPIO11
|
||||
- ANA_CMPR_CH0 input (non-inverting)
|
||||
- RTC
|
||||
|
||||
* - GPIO12
|
||||
-
|
||||
- RTC
|
||||
|
||||
* - GPIO13
|
||||
-
|
||||
- RTC
|
||||
|
||||
* - GPIO14
|
||||
-
|
||||
- RTC
|
||||
|
||||
* - GPIO15
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO16
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO17
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO18
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO19
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO20
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO21
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO22
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO23
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO24
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO25
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO26
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO27
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
.. note::
|
||||
|
||||
- Strapping pin: GPIO2, GPIO3, GPIO8, GPIO9, and GPIO25 are strapping pins. For more information, please refer to `ESP32H2 datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`_.
|
||||
- SPI0/1: GPIO15-21 are usually used for SPI flash and not recommended for other uses.
|
||||
- USB-Serial-JTAG: GPIO 26 and 27 are used by USB-Serial-JTAG by default. In order to use them as GPIOs, USB-Serial-JTAG will be disabled by the drivers.
|
||||
- For chip variants with an SiP flash built in, GPIO15–GPIO21 are dedicated to connecting the SiP flash and are not fan-out to the external pins. In addition, GPIO6–GPIO7 are also not fan-out to the external pins. In conclusion, only GPIO0–GPIO5, GPIO8–GPIO14, GPIO22–GPIO27 are available to users.
|
||||
- RTC: GPIO7–GPIO14 can be used to wake up the chip from Deep-sleep mode. Note that although GPIO7 is an RTC GPIO, it cannot be used for external wake-up since it is not led out. Other GPIOs can only wake up the chip from Light-sleep mode. For more information, please refer to Section :ref:`Wakeup Sources <api-reference-wakeup-source>`.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,161 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 26 physical GPIO pins (GPIO0 ~ GPIO25). Each pin can be used as a general-purpose I/O, or to be connected to an internal peripheral signal. Through GPIO matrix and IO MUX, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 8 12 12 20
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- LP GPIO
|
||||
- Comments
|
||||
|
||||
* - GPIO0
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO1
|
||||
- ADC1_CH0
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO2
|
||||
- ADC1_CH1
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO3
|
||||
- ADC1_CH2
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO4
|
||||
- ADC1_CH3
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO5
|
||||
- ADC1_CH4
|
||||
- LP_GPIO0
|
||||
-
|
||||
|
||||
* - GPIO6
|
||||
-
|
||||
- LP_GPIO1
|
||||
-
|
||||
|
||||
* - GPIO7
|
||||
-
|
||||
- LP_GPIO2
|
||||
-
|
||||
|
||||
* - GPIO8
|
||||
-
|
||||
- LP_GPIO3
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO9
|
||||
-
|
||||
- LP_GPIO4
|
||||
-
|
||||
|
||||
* - GPIO10
|
||||
-
|
||||
- LP_GPIO5
|
||||
-
|
||||
|
||||
* - GPIO11
|
||||
-
|
||||
- LP_GPIO6
|
||||
-
|
||||
|
||||
* - GPIO12
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO13
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO14
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO15
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO16
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO17
|
||||
-
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO18
|
||||
-
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO19
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO20
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO21
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO22
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO23
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO24
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO25
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
.. note::
|
||||
|
||||
- Strapping pin: GPIO8, GPIO13, and GPIO14 are strapping pins. For more information, please refer to `datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__.
|
||||
- SPI0/1: GPIO19 ~ GPIO25 are usually used for SPI flash and not recommended for other uses.
|
||||
- USB-JTAG: GPIO17 and GPIO18 are used by USB-JTAG by default. If they are reconfigured to operate as normal GPIOs, USB-JTAG functionality will be disabled.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,231 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 40 physical GPIO pins (GPIO0 ~ GPIO39). Each pin can be used as a general-purpose I/O, or to be connected to an internal peripheral signal. Through GPIO matrix and IO MUX, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 8 12 12 20
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- LP GPIO
|
||||
- Comments
|
||||
|
||||
* - GPIO0
|
||||
-
|
||||
- LP_GPIO0
|
||||
-
|
||||
|
||||
* - GPIO1
|
||||
-
|
||||
- LP_GPIO1
|
||||
-
|
||||
|
||||
* - GPIO2
|
||||
-
|
||||
- LP_GPIO2
|
||||
-
|
||||
|
||||
* - GPIO3
|
||||
-
|
||||
- LP_GPIO3
|
||||
-
|
||||
|
||||
* - GPIO4
|
||||
-
|
||||
- LP_GPIO4
|
||||
-
|
||||
|
||||
* - GPIO5
|
||||
-
|
||||
- LP_GPIO5
|
||||
-
|
||||
|
||||
* - GPIO6
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO7
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO8
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO9
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO10
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO11
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO12
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO13
|
||||
-
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO14
|
||||
-
|
||||
-
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO15
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO16
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO17
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO18
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO19
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO20
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO21
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO22
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO23
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO24
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO25
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO26
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO27
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO28
|
||||
- ADC1_CH0
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO29
|
||||
- ADC1_CH1
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO30
|
||||
- ADC1_CH2
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO31
|
||||
- ADC1_CH3
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO32
|
||||
- ADC1_CH4
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO33
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO34
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO35
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO36
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO37
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO38
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO39
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
.. note::
|
||||
|
||||
- Strapping pin: GPIO17, GPIO36, and GPIO37 are strapping pins. For more information, please refer to `datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__.
|
||||
- SPI0/1: GPIO6 ~ GPIO12 are usually used for SPI flash and PSRAM. These pins are not recommended for other uses.
|
||||
- USB-JTAG: GPIO13 and GPIO14 are used by USB-JTAG by default. If they are reconfigured to operate as normal GPIOs, USB-JTAG functionality will be disabled.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,305 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 55 physical GPIO pins (GPIO0 ~ GPIO54). Each pin can be used as a general-purpose I/O, or to be connected to an internal peripheral signal. Through GPIO matrix and IO MUX, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 8 12 12 20
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- LP GPIO
|
||||
- Comments
|
||||
|
||||
* - GPIO0
|
||||
-
|
||||
- LP_GPIO0
|
||||
-
|
||||
|
||||
* - GPIO1
|
||||
-
|
||||
- LP_GPIO1
|
||||
-
|
||||
|
||||
* - GPIO2
|
||||
- TOUCH0
|
||||
- LP_GPIO2
|
||||
-
|
||||
|
||||
* - GPIO3
|
||||
- TOUCH1
|
||||
- LP_GPIO3
|
||||
-
|
||||
|
||||
* - GPIO4
|
||||
- TOUCH2
|
||||
- LP_GPIO4
|
||||
-
|
||||
|
||||
* - GPIO5
|
||||
- TOUCH3
|
||||
- LP_GPIO5
|
||||
-
|
||||
|
||||
* - GPIO6
|
||||
- TOUCH4
|
||||
- LP_GPIO6
|
||||
-
|
||||
|
||||
* - GPIO7
|
||||
- TOUCH5
|
||||
- LP_GPIO7
|
||||
-
|
||||
|
||||
* - GPIO8
|
||||
- TOUCH6
|
||||
- LP_GPIO8
|
||||
-
|
||||
|
||||
* - GPIO9
|
||||
- TOUCH7
|
||||
- LP_GPIO9
|
||||
-
|
||||
|
||||
* - GPIO10
|
||||
- TOUCH8
|
||||
- LP_GPIO10
|
||||
-
|
||||
|
||||
* - GPIO11
|
||||
- TOUCH9
|
||||
- LP_GPIO11
|
||||
-
|
||||
|
||||
* - GPIO12
|
||||
- TOUCH10
|
||||
- LP_GPIO12
|
||||
-
|
||||
|
||||
* - GPIO13
|
||||
- TOUCH11
|
||||
- LP_GPIO13
|
||||
-
|
||||
|
||||
* - GPIO14
|
||||
- TOUCH12
|
||||
- LP_GPIO14
|
||||
-
|
||||
|
||||
* - GPIO15
|
||||
- TOUCH13
|
||||
- LP_GPIO15
|
||||
-
|
||||
|
||||
* - GPIO16
|
||||
- ADC1_CH0
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO17
|
||||
- ADC1_CH1
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO18
|
||||
- ADC1_CH2
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO19
|
||||
- ADC1_CH3
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO20
|
||||
- ADC1_CH4
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO21
|
||||
- ADC1_CH5
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO22
|
||||
- ADC1_CH6
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO23
|
||||
- ADC1_CH7
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO24
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO25
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO26
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO27
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO28
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO29
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO30
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO31
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO32
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO33
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO34
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO35
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO36
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO37
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO38
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO39
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO40
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO41
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO42
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO43
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO44
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO45
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO46
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO47
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO48
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO49
|
||||
- ADC2_CH0
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO50
|
||||
- ADC2_CH1
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO51
|
||||
- ADC2_CH2, ANA_CMPR_CH0 reference voltage
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO52
|
||||
- ADC2_CH3, ANA_CMPR_CH0 input (non-inverting)
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO53
|
||||
- ADC2_CH4, ANA_CMPR_CH1 reference voltage
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO54
|
||||
- ADC2_CH5, ANA_CMPR_CH1 input (non-inverting)
|
||||
-
|
||||
-
|
||||
|
||||
.. note::
|
||||
|
||||
- Strapping pin: GPIO34, GPIO35, GPIO36, GPIO37, and GPIO38 are strapping pins. For more information, please refer to `datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__.
|
||||
- USB-JTAG: GPIO24 and GPIO25 are used by USB-JTAG by default. If they are reconfigured to operate as normal GPIOs, USB-JTAG functionality will be disabled.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,247 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 43 physical GPIO pins (GPIO0 ~ GPIO21 and GPIO26 ~ GPIO46). Each pin can be used as a general-purpose I/O, or be connected to an internal peripheral signal. Through IO MUX, RTC IO MUX and the GPIO matrix, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 8 12 12 20
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- RTC GPIO
|
||||
- Comment
|
||||
|
||||
* - GPIO0
|
||||
-
|
||||
- RTC_GPIO0
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO1
|
||||
- ADC1_CH0
|
||||
- RTC_GPIO1
|
||||
-
|
||||
|
||||
* - GPIO2
|
||||
- ADC1_CH1
|
||||
- RTC_GPIO2
|
||||
-
|
||||
|
||||
* - GPIO3
|
||||
- ADC1_CH2
|
||||
- RTC_GPIO3
|
||||
-
|
||||
|
||||
* - GPIO4
|
||||
- ADC1_CH3
|
||||
- RTC_GPIO4
|
||||
-
|
||||
|
||||
* - GPIO5
|
||||
- ADC1_CH4
|
||||
- RTC_GPIO5
|
||||
-
|
||||
|
||||
* - GPIO6
|
||||
- ADC1_CH5
|
||||
- RTC_GPIO6
|
||||
-
|
||||
|
||||
* - GPIO7
|
||||
- ADC1_CH6
|
||||
- RTC_GPIO7
|
||||
-
|
||||
|
||||
* - GPIO8
|
||||
- ADC1_CH7
|
||||
- RTC_GPIO8
|
||||
-
|
||||
|
||||
* - GPIO9
|
||||
- ADC1_CH8
|
||||
- RTC_GPIO9
|
||||
-
|
||||
|
||||
* - GPIO10
|
||||
- ADC1_CH9
|
||||
- RTC_GPIO10
|
||||
-
|
||||
|
||||
* - GPIO11
|
||||
- ADC2_CH0
|
||||
- RTC_GPIO11
|
||||
-
|
||||
|
||||
* - GPIO12
|
||||
- ADC2_CH1
|
||||
- RTC_GPIO12
|
||||
-
|
||||
|
||||
* - GPIO13
|
||||
- ADC2_CH2
|
||||
- RTC_GPIO13
|
||||
-
|
||||
|
||||
* - GPIO14
|
||||
- ADC2_CH3
|
||||
- RTC_GPIO14
|
||||
-
|
||||
|
||||
* - GPIO15
|
||||
- ADC2_CH4
|
||||
- RTC_GPIO15
|
||||
-
|
||||
|
||||
* - GPIO16
|
||||
- ADC2_CH5
|
||||
- RTC_GPIO16
|
||||
-
|
||||
|
||||
* - GPIO17
|
||||
- ADC2_CH6
|
||||
- RTC_GPIO17
|
||||
-
|
||||
|
||||
* - GPIO18
|
||||
- ADC2_CH7
|
||||
- RTC_GPIO18
|
||||
-
|
||||
|
||||
* - GPIO19
|
||||
- ADC2_CH8
|
||||
- RTC_GPIO19
|
||||
-
|
||||
|
||||
* - GPIO20
|
||||
- ADC2_CH9
|
||||
- RTC_GPIO20
|
||||
-
|
||||
|
||||
* - GPIO21
|
||||
-
|
||||
- RTC_GPIO21
|
||||
-
|
||||
|
||||
* - GPIO26
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO27
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO28
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO29
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO30
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO31
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO32
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO33
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO34
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO35
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO36
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO37
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO38
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO39
|
||||
-
|
||||
-
|
||||
- JTAG
|
||||
|
||||
* - GPIO40
|
||||
-
|
||||
-
|
||||
- JTAG
|
||||
|
||||
* - GPIO41
|
||||
-
|
||||
-
|
||||
- JTAG
|
||||
|
||||
* - GPIO42
|
||||
-
|
||||
-
|
||||
- JTAG
|
||||
|
||||
* - GPIO43
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO44
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO45
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO46
|
||||
-
|
||||
-
|
||||
- GPI;Strapping pin
|
||||
|
||||
.. note::
|
||||
|
||||
- Strapping pin: GPIO0, GPIO45 and GPIO46 are strapping pins. For more information, please refer to `ESP32-S2 datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`_
|
||||
- SPI0/1: GPIO26-32 are usually used for SPI flash and PSRAM and not recommended for other uses.
|
||||
- JTAG: GPIO39-42 are usually used for inline debug.
|
||||
- GPI: GPIO46 is fixed to pull-down and is input only.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,256 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 45 physical GPIO pins (GPIO0 ~ GPIO21 and GPIO26 ~ GPIO48). Each pin can be used as a general-purpose I/O, or be connected to an internal peripheral signal. Through GPIO matrix, IO MUX, and RTC IO MUX, peripheral input signals can be from any GPIO pin, and peripheral output signals can be routed to any GPIO pin. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 8 12 12 20
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- RTC GPIO
|
||||
- Comment
|
||||
|
||||
* - GPIO0
|
||||
-
|
||||
- RTC_GPIO0
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO1
|
||||
- ADC1_CH0
|
||||
- RTC_GPIO1
|
||||
-
|
||||
|
||||
* - GPIO2
|
||||
- ADC1_CH1
|
||||
- RTC_GPIO2
|
||||
-
|
||||
|
||||
* - GPIO3
|
||||
- ADC1_CH2
|
||||
- RTC_GPIO3
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO4
|
||||
- ADC1_CH3
|
||||
- RTC_GPIO4
|
||||
-
|
||||
|
||||
* - GPIO5
|
||||
- ADC1_CH4
|
||||
- RTC_GPIO5
|
||||
-
|
||||
|
||||
* - GPIO6
|
||||
- ADC1_CH5
|
||||
- RTC_GPIO6
|
||||
-
|
||||
|
||||
* - GPIO7
|
||||
- ADC1_CH6
|
||||
- RTC_GPIO7
|
||||
-
|
||||
|
||||
* - GPIO8
|
||||
- ADC1_CH7
|
||||
- RTC_GPIO8
|
||||
-
|
||||
|
||||
* - GPIO9
|
||||
- ADC1_CH8
|
||||
- RTC_GPIO9
|
||||
-
|
||||
|
||||
* - GPIO10
|
||||
- ADC1_CH9
|
||||
- RTC_GPIO10
|
||||
-
|
||||
|
||||
* - GPIO11
|
||||
- ADC2_CH0
|
||||
- RTC_GPIO11
|
||||
-
|
||||
|
||||
* - GPIO12
|
||||
- ADC2_CH1
|
||||
- RTC_GPIO12
|
||||
-
|
||||
|
||||
* - GPIO13
|
||||
- ADC2_CH2
|
||||
- RTC_GPIO13
|
||||
-
|
||||
|
||||
* - GPIO14
|
||||
- ADC2_CH3
|
||||
- RTC_GPIO14
|
||||
-
|
||||
|
||||
* - GPIO15
|
||||
- ADC2_CH4
|
||||
- RTC_GPIO15
|
||||
-
|
||||
|
||||
* - GPIO16
|
||||
- ADC2_CH5
|
||||
- RTC_GPIO16
|
||||
-
|
||||
|
||||
* - GPIO17
|
||||
- ADC2_CH6
|
||||
- RTC_GPIO17
|
||||
-
|
||||
|
||||
* - GPIO18
|
||||
- ADC2_CH7
|
||||
- RTC_GPIO18
|
||||
-
|
||||
|
||||
* - GPIO19
|
||||
- ADC2_CH8
|
||||
- RTC_GPIO19
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO20
|
||||
- ADC2_CH9
|
||||
- RTC_GPIO20
|
||||
- USB-JTAG
|
||||
|
||||
* - GPIO21
|
||||
-
|
||||
- RTC_GPIO21
|
||||
-
|
||||
|
||||
* - GPIO26
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO27
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO28
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO29
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO30
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO31
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO32
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO33
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO34
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO35
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO36
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO37
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO38
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO39
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO40
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO41
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO42
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO43
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO44
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO45
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO46
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO47
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO48
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
.. Note::
|
||||
|
||||
- Strapping pin: GPIO0, GPIO3, GPIO45 and GPIO46 are strapping pins. For more information, please refer to `ESP32-S3 datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`_.
|
||||
- SPI0/1: GPIO26 ~ GPIO32 are usually used for SPI flash and PSRAM and not recommended for other uses. When using Octal flash or Octal PSRAM or both, GPIO33 ~ GPIO37 are connected to SPIIO4 ~ SPIIO7 and SPIDQS. Therefore, on boards embedded with ESP32-S3R8 / ESP32-S3R8V chip, GPIO33 ~ GPIO37 are also not recommended for other uses.
|
||||
- USB-JTAG: GPIO19 and GPIO20 are used by USB-JTAG by default. If they are reconfigured to operate as normal GPIOs, USB-JTAG functionality will be disabled.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,330 @@
|
||||
.. This file gets included from other .rst files in this folder.
|
||||
.. It contains target-specific snippets.
|
||||
.. Comments and '---' lines act as delimiters.
|
||||
..
|
||||
.. This is necessary mainly because RST doesn't support substitutions
|
||||
.. (defined in RST, not in Python) inside code blocks. If that is ever implemented,
|
||||
.. These code blocks can be moved back to the main .rst files, with target-specific
|
||||
.. file names being replaced by substitutions.
|
||||
|
||||
.. gpio-summary
|
||||
|
||||
The {IDF_TARGET_NAME} chip features 60 physical GPIO pins (GPIO0 ~ GPIO61, except GPIO29, GPIO41). Each pin can be used as a general-purpose I/O, or to be connected to an internal peripheral signal. Through GPIO matrix and IO MUX, peripheral input signals can be from any IO pins, and peripheral output signals can be routed to any IO pins. Together these modules provide highly configurable I/O. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
|
||||
|
||||
The table below provides more information on pin usage, and please note the comments in the table for GPIOs with restrictions.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 8 12 12 20
|
||||
|
||||
* - GPIO
|
||||
- Analog Function
|
||||
- LP GPIO
|
||||
- Comment
|
||||
|
||||
* - GPIO0
|
||||
-
|
||||
- LP_GPIO0
|
||||
-
|
||||
|
||||
* - GPIO1
|
||||
-
|
||||
- LP_GPIO1
|
||||
-
|
||||
|
||||
* - GPIO2
|
||||
-
|
||||
- LP_GPIO2
|
||||
-
|
||||
|
||||
* - GPIO3
|
||||
-
|
||||
- LP_GPIO3
|
||||
-
|
||||
|
||||
* - GPIO4
|
||||
- DAC0
|
||||
- LP_GPIO4
|
||||
-
|
||||
|
||||
* - GPIO5
|
||||
- DAC1
|
||||
- LP_GPIO5
|
||||
-
|
||||
|
||||
* - GPIO6
|
||||
- TOUCH0
|
||||
- LP_GPIO6
|
||||
-
|
||||
|
||||
* - GPIO7
|
||||
- TOUCH1
|
||||
- LP_GPIO7
|
||||
-
|
||||
|
||||
* - GPIO8
|
||||
- TOUCH2
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO9
|
||||
- TOUCH3
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO10
|
||||
- TOUCH4
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO11
|
||||
- TOUCH5
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO12
|
||||
- TOUCH6
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO13
|
||||
- TOUCH7
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO14
|
||||
- TOUCH8
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO15
|
||||
- TOUCH9
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO16
|
||||
- TOUCH10
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO17
|
||||
- TOUCH11
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO18
|
||||
- TOUCH12
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO19
|
||||
- TOUCH13
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO20
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO21
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO22
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO23
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO24
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO25
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO26
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO27
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO28
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO30
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO31
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO32
|
||||
-
|
||||
-
|
||||
- SPI0/1
|
||||
|
||||
* - GPIO33
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO34
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO35
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO36
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO37
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO38
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO39
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO40
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO42
|
||||
- ADC1_CH0_N
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO43
|
||||
- ADC1_CH0_P
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO44
|
||||
- ADC1_CH1_N
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO45
|
||||
- ADC1_CH1_P
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO46
|
||||
- ADC1_CH2_N
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO47
|
||||
- ADC1_CH2_P
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO48
|
||||
- ADC1_CH3_N
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO49
|
||||
- ADC1_CH3_P
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO50
|
||||
- ADC2_CH0_N
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO51
|
||||
- ADC2_CH0_P
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO52
|
||||
- ADC2_CH1_N
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO53
|
||||
- ADC2_CH1_P
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO54
|
||||
- ADC2_CH2_N
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO55
|
||||
- ADC2_CH2_P
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO56
|
||||
- ADC2_CH3_N
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO57
|
||||
- ADC2_CH3_P
|
||||
-
|
||||
-
|
||||
|
||||
* - GPIO58
|
||||
-
|
||||
-
|
||||
- UART0
|
||||
|
||||
* - GPIO59
|
||||
-
|
||||
-
|
||||
- UART0
|
||||
|
||||
* - GPIO60
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
* - GPIO61
|
||||
-
|
||||
-
|
||||
- Strapping pin
|
||||
|
||||
.. note::
|
||||
|
||||
- Strapping pin: GPIO36 ~ GPIO40, GPIO60 ~ GPIO61 are strapping pins. For more information, please refer to `{IDF_TARGET_NAME} datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`_.
|
||||
- SPI0/1: GPIO26 ~ GPIO32 are usually used for SPI flash, they're not recommended for other uses.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,421 @@
|
||||
===============================
|
||||
General Purpose Timer (GPTimer)
|
||||
===============================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
|
||||
This document introduces the features of the General Purpose Timer (GPTimer) driver in ESP-IDF. The table of contents is as follows:
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
GPTimer is a dedicated driver for the {IDF_TARGET_NAME} [`Timer Group peripheral <{IDF_TARGET_TRM_EN_URL}#timg>`__]. This timer can select different clock sources and prescalers to meet the requirements of nanosecond-level resolution. Additionally, it has flexible timeout alarm functions and allows automatic updating of the count value at the alarm moment, achieving very precise timing cycles.
|
||||
|
||||
Based on the **high resolution, high count range, and high response** capabilities of the hardware timer, the main application scenarios of this driver include:
|
||||
|
||||
- Running freely as a calendar clock to provide timestamp services for other modules
|
||||
- Generating periodic alarms to complete periodic tasks
|
||||
- Generating one-shot alarms, which can be used to implement a monotonic software timer list with asynchronous updates of alarm values
|
||||
- Working with the GPIO module to achieve PWM signal output and input capture
|
||||
- etc.
|
||||
|
||||
Quick Start
|
||||
===========
|
||||
|
||||
This section provides a concise overview of how to use the GPTimer driver. Through practical examples, it demonstrates how to initialize and start a timer, configure alarm events, and register callback functions. The typical usage flow is as follows:
|
||||
|
||||
.. blockdiag::
|
||||
:scale: 100%
|
||||
:caption: GPTimer driver's general usage flow (click to enlarge)
|
||||
:align: center
|
||||
|
||||
blockdiag {
|
||||
default_fontsize = 14;
|
||||
node_width = 250;
|
||||
node_height = 80;
|
||||
class emphasis [color = pink, style = dashed];
|
||||
|
||||
create [label="gptimer_new_timer"];
|
||||
config [label="gptimer_set_alarm_action \n gptimer_register_event_callbacks"];
|
||||
enable [label="gptimer_enable"];
|
||||
start [label="gptimer_start"];
|
||||
running [label="Timer Running", class="emphasis"]
|
||||
stop [label="gptimer_stop"];
|
||||
disable [label="gptimer_disable"];
|
||||
cleanup [label="gptimer_delete_timer"];
|
||||
|
||||
create -> config -> enable -> start -> running -> stop -> disable -> cleanup;
|
||||
enable -> start [folded];
|
||||
stop -> disable [folded];
|
||||
}
|
||||
|
||||
Creating and Starting a Timer
|
||||
-----------------------------
|
||||
|
||||
First, we need to create a timer instance. The following code shows how to create a timer with a resolution of 1 MHz:
|
||||
|
||||
.. code:: c
|
||||
|
||||
gptimer_handle_t gptimer = NULL;
|
||||
gptimer_config_t timer_config = {
|
||||
.clk_src = GPTIMER_CLK_SRC_DEFAULT, // Select the default clock source
|
||||
.direction = GPTIMER_COUNT_UP, // Counting direction is up
|
||||
.resolution_hz = 1 * 1000 * 1000, // Resolution is 1 MHz, i.e., 1 tick equals 1 microsecond
|
||||
};
|
||||
// Create a timer instance
|
||||
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
|
||||
// Enable the timer
|
||||
ESP_ERROR_CHECK(gptimer_enable(gptimer));
|
||||
// Start the timer
|
||||
ESP_ERROR_CHECK(gptimer_start(gptimer));
|
||||
|
||||
When creating a timer instance, we need to configure parameters such as the clock source, counting direction, and resolution through :cpp:type:`gptimer_config_t`. These parameters determine how the timer works. Then, call the :cpp:func:`gptimer_new_timer` function to create a new timer instance, which returns a handle pointing to the new instance. The timer handle is essentially a pointer to the timer memory object, of type :cpp:type:`gptimer_handle_t`.
|
||||
|
||||
Here are the other configuration parameters of the :cpp:type:`gptimer_config_t` structure and their explanations:
|
||||
|
||||
- :cpp:member:`gptimer_config_t::clk_src` selects the clock source for the timer. Available clock sources are listed in :cpp:type:`gptimer_clock_source_t`, and only one can be selected. Different clock sources vary in resolution, accuracy, and power consumption.
|
||||
- :cpp:member:`gptimer_config_t::direction` sets the counting direction of the timer. Supported directions are listed in :cpp:type:`gptimer_count_direction_t`, and only one can be selected.
|
||||
- :cpp:member:`gptimer_config_t::resolution_hz` sets the resolution of the internal counter. Each tick is equivalent to **1 / resolution_hz** seconds.
|
||||
- :cpp:member:`gptimer_config_t::intr_priority` sets the interrupt priority. If set to ``0``, a default priority interrupt will be allocated; otherwise, the specified priority will be used.
|
||||
- :cpp:member:`gptimer_config_t::flags` is used to fine-tune some behaviors of the driver, including the following options:
|
||||
|
||||
- :cpp:member:`gptimer_config_t::flags::allow_pd` configures whether the driver allows the system to power down the peripheral in sleep mode. Before entering sleep, the system will back up the GPTimer register context, which will be restored when the system wakes up. Note that powering down the peripheral can save power but will consume more memory to save the register context. You need to balance power consumption and memory usage. This configuration option depends on specific hardware features. If enabled on an unsupported chip, you will see an error message like ``not able to power down in light sleep``.
|
||||
|
||||
.. note::
|
||||
|
||||
Note that if all hardware timers in the current chip have been allocated, :cpp:func:`gptimer_new_timer` will return the :c:macro:`ESP_ERR_NOT_FOUND` error.
|
||||
|
||||
Before starting the timer, it must be enabled. The enable function :cpp:func:`gptimer_enable` can switch the internal state machine of the driver to the active state, which includes some system service requests/registrations, such as applying for a power management lock. The corresponding disable function is :cpp:func:`gptimer_disable`, which releases all system services.
|
||||
|
||||
.. note::
|
||||
|
||||
When calling the :cpp:func:`gptimer_enable` and :cpp:func:`gptimer_disable` functions, they need to be used in pairs. This means you cannot call :cpp:func:`gptimer_enable` or :cpp:func:`gptimer_disable` twice in a row. This pairing principle ensures the correct management and release of resources.
|
||||
|
||||
The :cpp:func:`gptimer_start` function is used to start the timer. After starting, the timer will begin counting and will automatically overflow and restart from 0 when it reaches the maximum or minimum value (depending on the counting direction).
|
||||
The :cpp:func:`gptimer_stop` function is used to stop the timer. Note that stopping a timer does not clear the current value of the counter. To clear the counter, use the :cpp:func:`gptimer_set_raw_count` function introduced later.
|
||||
The :cpp:func:`gptimer_start` and :cpp:func:`gptimer_stop` functions follow the idempotent principle. This means that if the timer is already started, calling the :cpp:func:`gptimer_start` function again will have no effect. Similarly, if the timer is already stopped, calling the :cpp:func:`gptimer_stop` function again will have no effect.
|
||||
|
||||
.. note::
|
||||
|
||||
However, note that when the timer is in the **intermediate state** of starting (the start has begun but not yet completed), if another thread calls the :cpp:func:`gptimer_start` or :cpp:func:`gptimer_stop` function, it will return the :c:macro:`ESP_ERR_INVALID_STATE` error to avoid triggering uncertain behavior.
|
||||
|
||||
Setting and Getting the Count Value
|
||||
-----------------------------------
|
||||
|
||||
When a timer is newly created, its internal counter value defaults to zero. You can set other count values using the :cpp:func:`gptimer_set_raw_count` function. The maximum count value depends on the bit width of the hardware timer (usually no less than ``54 bits``).
|
||||
|
||||
.. note::
|
||||
|
||||
If the timer is already running, :cpp:func:`gptimer_set_raw_count` will make the timer immediately jump to the new value and start counting from the newly set value.
|
||||
|
||||
The :cpp:func:`gptimer_get_raw_count` function is used to get the current count value of the timer. This count value is the accumulated count since the timer started (assuming it started from 0). Note that the returned value has not been converted to any unit; it is a pure count value. You need to convert the count value to time units based on the actual resolution of the timer. The timer's resolution can be obtained using the :cpp:func:`gptimer_get_resolution` function.
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Check the timer's resolution
|
||||
uint32_t resolution_hz;
|
||||
ESP_ERROR_CHECK(gptimer_get_resolution(gptimer, &resolution_hz));
|
||||
// Read the current count value
|
||||
uint64_t count;
|
||||
ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &count));
|
||||
// (Optional) Convert the count value to time units (seconds)
|
||||
double time = (double)count / resolution_hz;
|
||||
|
||||
Triggering Periodic Alarm Events
|
||||
--------------------------------
|
||||
|
||||
In addition to the timestamp function, the general-purpose timer also supports alarm functions. The following code shows how to set a periodic alarm that triggers once per second:
|
||||
|
||||
.. code-block:: c
|
||||
:emphasize-lines: 10-32
|
||||
|
||||
gptimer_handle_t gptimer = NULL;
|
||||
gptimer_config_t timer_config = {
|
||||
.clk_src = GPTIMER_CLK_SRC_DEFAULT, // Select the default clock source
|
||||
.direction = GPTIMER_COUNT_UP, // Counting direction is up
|
||||
.resolution_hz = 1 * 1000 * 1000, // Resolution is 1 MHz, i.e., 1 tick equals 1 microsecond
|
||||
};
|
||||
// Create a timer instance
|
||||
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
|
||||
|
||||
static bool example_timer_on_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
// General process for handling event callbacks:
|
||||
// 1. Retrieve user context data from user_ctx (passed in from gptimer_register_event_callbacks)
|
||||
// 2. Get alarm event data from edata, such as edata->count_value
|
||||
// 3. Perform user-defined operations
|
||||
// 4. Return whether a high-priority task was awakened during the above operations to notify the scheduler to switch tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.reload_count = 0, // When the alarm event occurs, the timer will automatically reload to 0
|
||||
.alarm_count = 1000000, // Set the actual alarm period, since the resolution is 1us, 1000000 represents 1s
|
||||
.flags.auto_reload_on_alarm = true, // Enable auto-reload function
|
||||
};
|
||||
// Set the timer's alarm action
|
||||
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
|
||||
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = example_timer_on_alarm_cb, // Call the user callback function when the alarm event occurs
|
||||
};
|
||||
// Register timer event callback functions, allowing user context to be carried
|
||||
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));
|
||||
// Enable the timer
|
||||
ESP_ERROR_CHECK(gptimer_enable(gptimer));
|
||||
// Start the timer
|
||||
ESP_ERROR_CHECK(gptimer_start(gptimer));
|
||||
|
||||
The :cpp:func:`gptimer_set_alarm_action` function is used to configure the timer's alarm action. When the timer count value reaches the specified alarm value, an alarm event will be triggered. Users can choose to automatically reload the preset count value when the alarm event occurs, thereby achieving periodic alarms.
|
||||
|
||||
Here are the necessary members of the :cpp:type:`gptimer_alarm_config_t` structure and their functions. By configuring these parameters, users can flexibly control the timer's alarm behavior to meet different application needs.
|
||||
|
||||
- :cpp:member:`gptimer_alarm_config_t::alarm_count` sets the target count value that triggers the alarm event. When the timer count value reaches this value, an alarm event will be triggered. When setting the alarm value, consider the counting direction of the timer. If the current count value has **exceeded** the alarm value, the alarm event will be triggered immediately.
|
||||
- :cpp:member:`gptimer_alarm_config_t::reload_count` sets the count value to be reloaded when the alarm event occurs. This configuration only takes effect when the :cpp:member:`gptimer_alarm_config_t::flags::auto_reload_on_alarm` flag is ``true``. The actual alarm period will be determined by ``|alarm_count - reload_count|``. From a practical application perspective, it is not recommended to set the alarm period to less than 5us.
|
||||
|
||||
.. note::
|
||||
|
||||
Specifically, ``gptimer_set_alarm_action(gptimer, NULL);`` means disabling the timer's alarm function.
|
||||
|
||||
The :cpp:func:`gptimer_register_event_callbacks` function is used to register the timer event callback functions. When the timer triggers a specific event (such as an alarm event), the user-defined callback function will be called. Users can perform custom operations in the callback function, such as sending signals, to achieve more flexible event handling mechanisms. Since the callback function is executed in the interrupt context, avoid performing complex operations (including any operations that may cause blocking) in the callback function to avoid affecting the system's real-time performance. The :cpp:func:`gptimer_register_event_callbacks` function also allows users to pass a context pointer to access user-defined data in the callback function.
|
||||
|
||||
The supported event callback functions for GPTimer are as follows:
|
||||
|
||||
- :cpp:type:`gptimer_alarm_cb_t` alarm event callback function, which has a corresponding data structure :cpp:type:`gptimer_alarm_event_data_t` for passing alarm event-related data:
|
||||
- :cpp:member:`gptimer_alarm_event_data_t::alarm_value` stores the alarm value, which is the target count value that triggers the alarm event.
|
||||
- :cpp:member:`gptimer_alarm_event_data_t::count_value` stores the count value when entering the interrupt handler after the alarm occurs. This value may differ from the alarm value due to interrupt handler delays, and the count value may have been automatically reloaded when the alarm occurred.
|
||||
|
||||
.. note::
|
||||
|
||||
Be sure to register the callback function before calling :cpp:func:`gptimer_enable`, otherwise the timer event will not correctly trigger the interrupt service.
|
||||
|
||||
Triggering One-Shot Alarm Events
|
||||
--------------------------------
|
||||
|
||||
Some application scenarios only require triggering a one-shot alarm interrupt. The following code shows how to set a one-shot alarm that triggers after 1 second:
|
||||
|
||||
.. code-block:: c
|
||||
:emphasize-lines: 12-13,24
|
||||
|
||||
gptimer_handle_t gptimer = NULL;
|
||||
gptimer_config_t timer_config = {
|
||||
.clk_src = GPTIMER_CLK_SRC_DEFAULT, // Select the default clock source
|
||||
.direction = GPTIMER_COUNT_UP, // Counting direction is up
|
||||
.resolution_hz = 1 * 1000 * 1000, // Resolution is 1 MHz, i.e., 1 tick equals 1 microsecond
|
||||
};
|
||||
// Create a timer instance
|
||||
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
|
||||
|
||||
static bool example_timer_on_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
// This is just a demonstration of how to stop the timer when the alarm occurs for the first time
|
||||
gptimer_stop(timer);
|
||||
// General process for handling event callbacks:
|
||||
// 1. Retrieve user context data from user_ctx (passed in from gptimer_register_event_callbacks)
|
||||
// 2. Get alarm event data from edata, such as edata->count_value
|
||||
// 3. Perform user-defined operations
|
||||
// 4. Return whether a high-priority task was awakened during the above operations to notify the scheduler to switch tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.alarm_count = 1000000, // Set the actual alarm period, since the resolution is 1us, 1000000 represents 1s
|
||||
.flags.auto_reload_on_alarm = false; // Disable auto-reload function
|
||||
};
|
||||
// Set the timer's alarm action
|
||||
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
|
||||
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = example_timer_on_alarm_cb, // Call the user callback function when the alarm event occurs
|
||||
};
|
||||
// Register timer event callback functions, allowing user context to be carried
|
||||
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));
|
||||
// Enable the timer
|
||||
ESP_ERROR_CHECK(gptimer_enable(gptimer));
|
||||
// Start the timer
|
||||
ESP_ERROR_CHECK(gptimer_start(gptimer));
|
||||
|
||||
Unlike periodic alarms, the above code disables the auto-reload function when configuring the alarm behavior. This means that after the alarm event occurs, the timer will not automatically reload to the preset count value but will continue counting until it overflows. If you want the timer to stop immediately after the alarm, you can call :cpp:func:`gptimer_stop` in the callback function.
|
||||
|
||||
Resource Recycling
|
||||
------------------
|
||||
|
||||
When the timer is no longer needed, you should call the :cpp:func:`gptimer_delete_timer` function to release software and hardware resources. Before deleting, ensure that the timer is already stopped.
|
||||
|
||||
Advanced Features
|
||||
=================
|
||||
|
||||
After understanding the basic usage, we can further explore more features of the GPTimer driver.
|
||||
|
||||
Dynamic Alarm Value Update
|
||||
--------------------------
|
||||
|
||||
The GPTimer driver supports dynamically updating the alarm value in the interrupt callback function by calling the :cpp:func:`gptimer_set_alarm_action` function, thereby implementing a monotonic software timer list. The following code shows how to reset the next alarm trigger time when the alarm event occurs:
|
||||
|
||||
.. code-block:: c
|
||||
:emphasize-lines: 12-16
|
||||
|
||||
gptimer_handle_t gptimer = NULL;
|
||||
gptimer_config_t timer_config = {
|
||||
.clk_src = GPTIMER_CLK_SRC_DEFAULT, // Select the default clock source
|
||||
.direction = GPTIMER_COUNT_UP, // Counting direction is up
|
||||
.resolution_hz = 1 * 1000 * 1000, // Resolution is 1 MHz, i.e., 1 tick equals 1 microsecond
|
||||
};
|
||||
// Create a timer instance
|
||||
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
|
||||
|
||||
static bool example_timer_on_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.alarm_count = edata->alarm_value + 1000000, // Next alarm in 1s from the current alarm
|
||||
};
|
||||
// Update the alarm value
|
||||
gptimer_set_alarm_action(timer, &alarm_config);
|
||||
return false;
|
||||
}
|
||||
|
||||
gptimer_alarm_config_t alarm_config = {
|
||||
.alarm_count = 1000000, // Set the actual alarm period, since the resolution is 1us, 1000000 represents 1s
|
||||
.flags.auto_reload_on_alarm = false, // Disable auto-reload function
|
||||
};
|
||||
// Set the timer's alarm action
|
||||
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
|
||||
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = example_timer_on_alarm_cb, // Call the user callback function when the alarm event occurs
|
||||
};
|
||||
// Register timer event callback functions, allowing user context to be carried
|
||||
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));
|
||||
// Enable the timer
|
||||
ESP_ERROR_CHECK(gptimer_enable(gptimer));
|
||||
// Start the timer
|
||||
ESP_ERROR_CHECK(gptimer_start(gptimer));
|
||||
|
||||
.. only:: SOC_TIMER_SUPPORT_ETM and SOC_ETM_SUPPORTED
|
||||
|
||||
.. _gptimer-etm-event-and-task:
|
||||
|
||||
GPTimer's ETM Events and Tasks
|
||||
------------------------------
|
||||
|
||||
GPTimer can generate various events that can be connected to the :doc:`ETM </api-reference/peripherals/etm>` module. The event types are listed in :cpp:type:`gptimer_etm_event_type_t`. Users can create an ``ETM event`` handle by calling :cpp:func:`gptimer_new_etm_event`.
|
||||
GPTimer also supports some tasks that can be triggered by other events and executed automatically. The task types are listed in :cpp:type:`gptimer_etm_task_type_t`. Users can create an ``ETM task`` handle by calling :cpp:func:`gptimer_new_etm_task`.
|
||||
|
||||
For how to connect the timer events and tasks to the ETM channel, please refer to the :doc:`ETM </api-reference/peripherals/etm>` documentation.
|
||||
|
||||
Power Management
|
||||
----------------
|
||||
|
||||
When power management :ref:`CONFIG_PM_ENABLE` is enabled, the system may adjust or disable the clock source before entering sleep mode, causing the GPTimer to lose accuracy.
|
||||
|
||||
To prevent this, the GPTimer driver creates a power management lock internally. When the :cpp:func:`gptimer_enable` function is called, the lock is activated to ensure the system does not enter sleep mode, thus maintaining the timer's accuracy. To reduce power consumption, you can call the :cpp:func:`gptimer_disable` function to release the power management lock, allowing the system to enter sleep mode. However, this will stop the timer, so you need to restart the timer after waking up.
|
||||
|
||||
.. only:: SOC_TIMER_SUPPORT_SLEEP_RETENTION
|
||||
|
||||
Besides disabling the clock source, the system can also power down the GPTimer before entering sleep mode to further reduce power consumption. To achieve this, set :cpp:member:`gptimer_config_t::allow_pd` to ``true``. Before the system enters sleep mode, the GPTimer register context will be backed up to memory and restored after the system wakes up. Note that enabling this option reduces power consumption but increases memory usage. Therefore, you need to balance power consumption and memory usage when using this feature.
|
||||
|
||||
Thread Safety
|
||||
-------------
|
||||
|
||||
The driver uses critical sections to ensure atomic operations on registers. Key members in the driver handle are also protected by critical sections. The driver's internal state machine uses atomic instructions to ensure thread safety, with state checks preventing certain invalid concurrent operations (e.g., conflicts between `start` and `stop`). Therefore, GPTimer driver APIs can be used in a multi-threaded environment without extra locking.
|
||||
|
||||
The following functions can also be used in an interrupt context:
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:func:`gptimer_start`
|
||||
- :cpp:func:`gptimer_stop`
|
||||
- :cpp:func:`gptimer_get_raw_count`
|
||||
- :cpp:func:`gptimer_set_raw_count`
|
||||
- :cpp:func:`gptimer_get_captured_count`
|
||||
- :cpp:func:`gptimer_set_alarm_action`
|
||||
|
||||
Cache Safety
|
||||
------------
|
||||
|
||||
When the file system performs Flash read/write operations, the system temporarily disables the Cache function to avoid errors when loading instructions and data from Flash. This causes the GPTimer interrupt handler to be unresponsive during this period, preventing the user callback function from executing in time. If you want the interrupt handler to run normally when the Cache is disabled, you can enable the :ref:`CONFIG_GPTIMER_ISR_CACHE_SAFE` option.
|
||||
|
||||
.. note::
|
||||
|
||||
Note that when this option is enabled, all interrupt callback functions and their context data **must be placed in internal storage**. This is because the system cannot load data and instructions from Flash when the Cache is disabled.
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
||||
To improve the real-time responsiveness of interrupt handling, the GPTimer driver provides the :ref:`CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM` option. Once enabled, the interrupt handler is placed in internal RAM, reducing delays caused by potential cache misses when loading instructions from Flash.
|
||||
|
||||
.. note::
|
||||
|
||||
However, the user callback function and its context data called by the interrupt handler may still reside in Flash. Cache misses are still possible, so users must manually place the callback function and data in internal RAM, for example by using :c:macro:`IRAM_ATTR` and :c:macro:`DRAM_ATTR`.
|
||||
|
||||
As mentioned above, the GPTimer driver allows some functions to be called in an interrupt context. By enabling the :ref:`CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM` option, these functions can also be placed in IRAM, which helps avoid performance loss caused by cache misses and allows them to be used when the Cache is disabled.
|
||||
|
||||
Other Kconfig Options
|
||||
---------------------
|
||||
|
||||
- The :ref:`CONFIG_GPTIMER_ENABLE_DEBUG_LOG` option forces the GPTimer driver to enable all debug logs, regardless of the global log level settings. Enabling this option helps developers obtain more detailed log information during debugging, making it easier to locate and solve problems.
|
||||
|
||||
Resource Consumption
|
||||
--------------------
|
||||
|
||||
Use the :doc:`/api-guides/tools/idf-size` tool to check the code and data consumption of the GPTimer driver. The following are the test conditions (using ESP32-C2 as an example):
|
||||
|
||||
- Compiler optimization level set to ``-Os`` to ensure minimal code size.
|
||||
- Default log level set to ``ESP_LOG_INFO`` to balance debug information and performance.
|
||||
- Disable the following driver optimization options:
|
||||
- :ref:`CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM` - Do not place the interrupt handler in IRAM.
|
||||
- :ref:`CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM` - Do not place control functions in IRAM.
|
||||
- :ref:`CONFIG_GPTIMER_ISR_CACHE_SAFE` - Do not enable Cache safety options.
|
||||
|
||||
**Note that the following data are not exact values and are for reference only; they may differ on different chip models.**
|
||||
|
||||
+------------------+------------+-------+------+-------+-------+------------+-------+------------+---------+
|
||||
| Component Layer | Total Size | DIRAM | .bss | .data | .text | Flash Code | .text | Flash Data | .rodata |
|
||||
+==================+============+=======+======+=======+=======+============+=======+============+=========+
|
||||
| soc | 8 | 0 | 0 | 0 | 0 | 0 | 0 | 8 | 8 |
|
||||
+------------------+------------+-------+------+-------+-------+------------+-------+------------+---------+
|
||||
| hal | 206 | 0 | 0 | 0 | 0 | 206 | 206 | 0 | 0 |
|
||||
+------------------+------------+-------+------+-------+-------+------------+-------+------------+---------+
|
||||
| driver | 4251 | 12 | 12 | 0 | 0 | 4046 | 4046 | 193 | 193 |
|
||||
+------------------+------------+-------+------+-------+-------+------------+-------+------------+---------+
|
||||
|
||||
Additionally, each GPTimer handle dynamically allocates about ``100`` bytes of memory from the heap. If the :cpp:member:`gptimer_config_t::flags::allow_pd` option is enabled, each timer will also consume approximately ``30`` extra bytes of memory during sleep to store the register context.
|
||||
|
||||
Application Examples
|
||||
====================
|
||||
|
||||
.. list::
|
||||
|
||||
- :example:`peripherals/timer_group/gptimer` demonstrates how to use the general-purpose timer APIs on ESP SOC chips to generate periodic alarm events and trigger different alarm actions.
|
||||
- :example:`peripherals/timer_group/wiegand_interface` uses two timers (one in one-shot alarm mode and the other in periodic alarm mode) to trigger interrupts and change the GPIO output state in the alarm event callback function, simulating the output waveform of the Wiegand protocol.
|
||||
:SOC_TIMER_SUPPORT_ETM and SOC_ETM_SUPPORTED: - :example:`peripherals/timer_group/gptimer_capture_hc_sr04` demonstrates how to use the general-purpose timer and Event Task Matrix (ETM) to accurately capture timestamps of ultrasonic sensor events and convert them into distance information.
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
GPTimer Driver APIs
|
||||
-------------------
|
||||
|
||||
.. include-build-file:: inc/gptimer.inc
|
||||
|
||||
GPTimer Driver Types
|
||||
--------------------
|
||||
|
||||
.. include-build-file:: inc/gptimer_types.inc
|
||||
|
||||
GPTimer HAL Types
|
||||
-----------------
|
||||
|
||||
.. include-build-file:: inc/timer_types.inc
|
||||
|
||||
GPTimer ETM APIs
|
||||
----------------
|
||||
|
||||
.. only:: SOC_TIMER_SUPPORT_ETM and SOC_ETM_SUPPORTED
|
||||
|
||||
.. include-build-file:: inc/gptimer_etm.inc
|
||||
@@ -0,0 +1,259 @@
|
||||
Hash-Based Message Authentication Code (HMAC)
|
||||
=============================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Hash-based Message Authentication Code (HMAC) is a secure authentication technique that verifies the authenticity and integrity of a message with a pre-shared key. This module provides hardware acceleration for SHA256-HMAC generation using a key burned into an eFuse block.
|
||||
|
||||
For more detailed information on the application workflow and the HMAC calculation process, see **{IDF_TARGET_NAME} Technical Reference Manual** > **HMAC Accelerator (HMAC)** [`PDF <{IDF_TARGET_TRM_EN_URL}#hmac>`__].
|
||||
|
||||
Generalized Application Scheme
|
||||
------------------------------
|
||||
|
||||
Let there be two parties, A and B. They want to verify the authenticity and integrity of messages sent between each other. Before they can start sending messages, they need to exchange the secret key via a secure channel.
|
||||
|
||||
To verify A's messages, B can do the following:
|
||||
|
||||
- A calculates the HMAC of the message it wants to send.
|
||||
- A sends the message and the HMAC to B.
|
||||
- B calculates the HMAC of the received message itself.
|
||||
- B checks whether the received and calculated HMACs match.
|
||||
|
||||
If they do match, the message is authentic.
|
||||
|
||||
However, the HMAC itself is not bound to this use case. It can also be used for challenge-response protocols supporting HMAC or as a key input for further security modules (see below), etc.
|
||||
|
||||
HMAC on {IDF_TARGET_NAME}
|
||||
-----------------------------
|
||||
|
||||
On {IDF_TARGET_NAME}, the HMAC module works with a secret key burnt into the eFuses.
|
||||
|
||||
.. only:: SOC_KEY_MANAGER_SUPPORTED
|
||||
|
||||
On {IDF_TARGET_NAME}, the HMAC module also supports storing a secret key in the Key Manager. Refer to :ref:`key-manager` for more details.
|
||||
|
||||
This key can be made completely inaccessible for any resources outside the cryptographic modules, thus avoiding key leakage.
|
||||
|
||||
.. only:: SOC_DIG_SIGN_SUPPORTED
|
||||
|
||||
Furthermore, {IDF_TARGET_NAME} has three different application scenarios for its HMAC module:
|
||||
|
||||
#. HMAC is generated for software use
|
||||
#. HMAC is used as a key for the RSA Digital Signature Peripheral (RSA_DS)
|
||||
#. HMAC is used for enabling the soft-disabled JTAG interface
|
||||
|
||||
The first mode is called **Upstream** mode, while the last two modes are called **Downstream** modes.
|
||||
|
||||
.. only:: not SOC_DIG_SIGN_SUPPORTED
|
||||
|
||||
Furthermore, {IDF_TARGET_NAME} has two different application scenarios for its HMAC module:
|
||||
|
||||
#. HMAC is generated for software use
|
||||
#. HMAC is used for enabling the soft-disabled JTAG interface
|
||||
|
||||
The first mode is called **Upstream** mode, while the second mode is called **Downstream** mode.
|
||||
|
||||
eFuse Keys for HMAC
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Six physical eFuse blocks can be used as keys for the HMAC module: block 4 ~ block 9. The enum :cpp:enum:`hmac_key_id_t` in the API maps them to ``HMAC_KEY0`` ~ ``HMAC_KEY5``.
|
||||
|
||||
Each key has a corresponding eFuse parameter **key purpose** determining for which of the HMAC application scenarios (see below) the key may be used:
|
||||
|
||||
.. only:: SOC_DIG_SIGN_SUPPORTED
|
||||
|
||||
.. list-table::
|
||||
:widths: 15 70
|
||||
:header-rows: 1
|
||||
|
||||
* - Key Purpose
|
||||
- Application Scenario
|
||||
* - 8
|
||||
- HMAC generated for software use
|
||||
* - 7
|
||||
- HMAC used as a key for the RSA Digital Signature Peripheral (RSA_DS)
|
||||
* - 6
|
||||
- HMAC used for enabling the soft-disabled JTAG interface
|
||||
* - 5
|
||||
- HMAC both as a key for the RSA_DS module and for enabling JTAG
|
||||
|
||||
.. only:: not SOC_DIG_SIGN_SUPPORTED
|
||||
|
||||
.. list-table::
|
||||
:widths: 15 70
|
||||
:header-rows: 1
|
||||
|
||||
* - Key Purpose
|
||||
- Application Scenario
|
||||
* - 8
|
||||
- HMAC generated for software use
|
||||
* - 5, 6
|
||||
- HMAC used for enabling the soft-disabled JTAG interface (HMAC Downstream mode)
|
||||
|
||||
This is to prevent the usage of a key for a different function than originally intended.
|
||||
|
||||
To calculate an HMAC, the software has to provide the ID of the key block containing the secret key as well as the **key purpose** (see **{IDF_TARGET_NAME} Technical Reference Manual** > **eFuse Controller (eFuse)** [`PDF <{IDF_TARGET_TRM_EN_URL}#efuse>`__]).
|
||||
|
||||
Before the HMAC key calculation, the HMAC module looks up the purpose of the provided key block. The calculation only proceeds if the purpose of the provided key block matches the purpose stored in the eFuses of the key block provided by the ID.
|
||||
|
||||
HMAC Generation for Software
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Key purpose value: 8
|
||||
|
||||
In this case, the HMAC is given out to the software, e.g., to authenticate a message.
|
||||
|
||||
The API to calculate the HMAC is :cpp:func:`psa_mac_compute`, which takes an opaque PSA key referencing an eFuse key block that contains the secret and has its purpose set to Upstream mode.
|
||||
|
||||
.. only:: SOC_DIG_SIGN_SUPPORTED
|
||||
|
||||
HMAC for RSA Digital Signature
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Key purpose values: 7, 5
|
||||
|
||||
The HMAC can be used as a key derivation function to decrypt private key parameters which are used by the RSA Digital Signature module. A standard message is used by the hardware in that case. You only need to provide the eFuse key block and purpose on the HMAC side, additional parameters are required for the RSA Digital Signature component in that case.
|
||||
|
||||
Neither the key nor the actual HMAC is ever exposed outside the HMAC module and RSA_DS component. The calculation of the HMAC and its handover to the RSA_DS component happen internally.
|
||||
|
||||
For more details, see **{IDF_TARGET_NAME} Technical Reference Manual** > **RSA Digital Signature Peripheral (RSA_DS)** [`PDF <{IDF_TARGET_TRM_EN_URL}#digsig>`__].
|
||||
|
||||
.. _hmac_for_enabling_jtag:
|
||||
|
||||
HMAC for Enabling JTAG
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Key purpose values: 6, 5
|
||||
|
||||
The third application is using the HMAC as a key to enable JTAG if it was soft-disabled before.
|
||||
|
||||
Following is the procedure to re-enable the JTAG:
|
||||
|
||||
**Stage 1: Setup**
|
||||
|
||||
1. Generate a 256-bit HMAC secret key to use for JTAG re-enable.
|
||||
2. Write the key to an eFuse block with key purpose HMAC_DOWN_ALL (5) or HMAC_DOWN_JTAG (6). This can be done using the ``esp_efuse_write_key()`` function in the firmware or using ``idf.py efuse-burn-key`` from the host.
|
||||
3. Configure the eFuse key block to be read-protected using the ``esp_efuse_set_read_protect()``, so that software cannot read back the value.
|
||||
4. Burn the ``soft JTAG disable`` bit/bits on {IDF_TARGET_NAME}. This will permanently disable JTAG unless the correct key value is provided by the software.
|
||||
|
||||
.. only:: esp32s2
|
||||
|
||||
.. note::
|
||||
|
||||
The API **esp_efuse_write_field_bit(ESP_EFUSE_SOFT_DIS_JTAG)** can be used to burn ``soft JTAG disable`` bit on {IDF_TARGET_NAME}.
|
||||
|
||||
.. only:: not esp32s2
|
||||
|
||||
.. note::
|
||||
|
||||
The API **esp_efuse_write_field_cnt(ESP_EFUSE_SOFT_DIS_JTAG, ESP_EFUSE_SOFT_DIS_JTAG[0]->bit_count)** can be used to burn ``soft JTAG disable`` bits on {IDF_TARGET_NAME}.
|
||||
|
||||
.. only:: esp32s2 or esp32s3
|
||||
|
||||
.. note::
|
||||
|
||||
If ``HARD_DIS_JTAG`` eFuse is set, then ``SOFT_DIS_JTAG`` functionality does not work because JTAG is permanently disabled.
|
||||
|
||||
.. only:: not esp32s2 and not esp32s3
|
||||
|
||||
.. note::
|
||||
|
||||
If ``DIS_PAD_JTAG`` eFuse is set, then ``SOFT_DIS_JTAG`` functionality does not work because JTAG is permanently disabled.
|
||||
|
||||
JTAG enables
|
||||
|
||||
1. The key to re-enable JTAG is the output of the HMAC-SHA256 function using the secret key in eFuse and 32 ``0x00`` bytes as the message.
|
||||
2. Pass this key value when calling the :cpp:func:`esp_hmac_jtag_enable` function from the firmware.
|
||||
3. To re-disable JTAG in the firmware, reset the system or call :cpp:func:`esp_hmac_jtag_disable`.
|
||||
|
||||
For a complete workflow of soft-disabling and re-enabling JTAG, refer to :example:`security/hmac_soft_jtag`. This example demonstrates how to use HMAC to re-enable a soft-disabled JTAG interface, covering steps like generating an HMAC key, burning it to eFuse, and creating token data from the key.
|
||||
|
||||
For more details, see **{IDF_TARGET_NAME} Technical Reference Manual** > **HMAC Accelerator (HMAC)** [`PDF <{IDF_TARGET_TRM_EN_URL}#hmac>`__].
|
||||
|
||||
|
||||
Application Outline
|
||||
-------------------
|
||||
|
||||
The following code is an outline of how to set an eFuse key and then use it to calculate an HMAC for software usage.
|
||||
|
||||
Using eFuses to store the HMAC key:
|
||||
|
||||
We use ``esp_efuse_write_key`` to set physical key block 4 in the eFuse for the HMAC module together with its purpose. ``ESP_EFUSE_KEY_PURPOSE_HMAC_UP`` (8) means that this key can only be used for HMAC generation for software usage:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "esp_efuse.h"
|
||||
|
||||
const uint8_t key_data[32] = { ... };
|
||||
|
||||
esp_err_t status = esp_efuse_write_key(EFUSE_BLK_KEY4,
|
||||
ESP_EFUSE_KEY_PURPOSE_HMAC_UP,
|
||||
key_data, sizeof(key_data));
|
||||
|
||||
if (status == ESP_OK) {
|
||||
// written key
|
||||
} else {
|
||||
// writing key failed, maybe written already
|
||||
}
|
||||
|
||||
Now we can calculate an HMAC for software usage with the saved key through the PSA Crypto API.
|
||||
|
||||
Using an eFuse-based HMAC key:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "psa/crypto.h"
|
||||
#include "psa_crypto_driver_esp_hmac_opaque.h"
|
||||
|
||||
uint8_t hmac[32];
|
||||
size_t hmac_length = 0;
|
||||
|
||||
const char *message = "Hello, HMAC!";
|
||||
const size_t msg_len = 12;
|
||||
|
||||
// Setup key attributes for ESP-HMAC opaque driver
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
|
||||
psa_set_key_algorithm(&attributes, PSA_ALG_HMAC(PSA_ALG_SHA_256));
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC);
|
||||
psa_set_key_bits(&attributes, 256);
|
||||
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_ESP_HMAC_VOLATILE);
|
||||
|
||||
// Create opaque key reference for eFuse-based key
|
||||
esp_hmac_opaque_key_t opaque_key = {
|
||||
.efuse_key_id = HMAC_KEY4,
|
||||
};
|
||||
|
||||
// Import the opaque key
|
||||
psa_key_id_t key_id = 0;
|
||||
psa_status_t status = psa_import_key(&attributes, (uint8_t *)&opaque_key,
|
||||
sizeof(opaque_key), &key_id);
|
||||
if (status != PSA_SUCCESS) {
|
||||
// Failed to import key
|
||||
psa_reset_key_attributes(&attributes);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute HMAC
|
||||
status = psa_mac_compute(key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256),
|
||||
(uint8_t *)message, msg_len,
|
||||
hmac, sizeof(hmac), &hmac_length);
|
||||
|
||||
// Clean up
|
||||
psa_destroy_key(key_id);
|
||||
psa_reset_key_attributes(&attributes);
|
||||
|
||||
if (status == PSA_SUCCESS) {
|
||||
// HMAC written to hmac now
|
||||
} else {
|
||||
// failure calculating HMAC
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
The ESP-HMAC opaque PSA driver is backed by the one-shot hardware HMAC peripheral, which computes the MAC over the whole message in a single operation and cannot save or restore intermediate state between calls. Multipart streaming is therefore not supported: supply the entire message in a single :cpp:func:`psa_mac_compute` call (as shown above) or in a single multipart update. A second non-empty update on the same operation returns ``PSA_ERROR_BAD_STATE``; the operation fails closed and never produces a MAC computed over only part of the message.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/psa_crypto_driver_esp_hmac_opaque_contexts.inc
|
||||
@@ -0,0 +1,707 @@
|
||||
Inter-Integrated Circuit (I2C)
|
||||
==============================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
I2C is a serial, synchronous, multi-device, half-duplex communication protocol that allows co-existence of multiple masters and slaves on the same bus. I2C uses two bidirectional open-drain lines: serial data line (SDA) and serial clock line (SCL), pulled up by resistors.
|
||||
|
||||
{IDF_TARGET_NAME} has {IDF_TARGET_SOC_HP_I2C_NUM} I2C controller(s) (also called port), responsible for handling communication on the I2C bus.
|
||||
|
||||
.. only:: not esp32c2
|
||||
|
||||
A single I2C controller can be a master or a slave.
|
||||
|
||||
.. only:: esp32c2
|
||||
|
||||
The I2C controller can only be a master.
|
||||
|
||||
.. only:: SOC_LP_I2C_SUPPORTED
|
||||
|
||||
Additionally, the {IDF_TARGET_NAME} chip has 1 low-power (LP) I2C controller. It is the cut-down version of regular I2C. Usually, the LP I2C controller only support basic I2C functionality with a much smaller RAM size, and does not support slave mode. For a full list of difference between HP I2C and LP I2C, please refer to the *{IDF_TARGET_NAME} Technical Reference Manual* > *I2C Controller (I2C)* > *Features* [`PDF <{IDF_TARGET_TRM_EN_URL}#i2c>`__].
|
||||
|
||||
You can use LP I2C peripheral when HP I2C is not sufficient for users' usage. But please note again the LP I2C does not support all HP I2C functions. Please read documentation before you use it.
|
||||
|
||||
Typically, an I2C slave device has a 7-bit address or 10-bit address. {IDF_TARGET_NAME} supports both I2C Standard-mode (Sm) and Fast-mode (Fm) which can go up to 100 kHz and 400 kHz respectively.
|
||||
|
||||
.. warning::
|
||||
|
||||
The clock frequency of SCL in master mode should not be larger than 400 kHz.
|
||||
|
||||
.. note::
|
||||
|
||||
The frequency of SCL is influenced by both the pull-up resistor and the wire capacitance. Therefore, it is strongly recommended to choose appropriate pull-up resistors to make the frequency accurate. The recommended value for pull-up resistors usually ranges from 1 kΩ to 10 kΩ.
|
||||
|
||||
Keep in mind that the higher the frequency, the smaller the pull-up resistor should be (but not less than 1 kΩ). Indeed, large resistors will decline the current, which will increase the clock switching time and reduce the frequency. A range of 2 kΩ to 5 kΩ is recommended, but adjustments may also be necessary depending on their current draw requirements.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. note::
|
||||
|
||||
The ESP32 I2C controller does not support clock stretching when operating as a slave. Therefore, in addition to driver configuration, the application layer should pay attention to speed matching and synchronization between master and slave devices: if the master processes too fast while the slave responds slowly, communication errors or data loss may occur. It is recommended to use application-layer data verification, GPIO signal synchronization, or other methods to achieve data synchronization between master and slave. Please confirm whether the ESP32 slave mode meets your project requirements according to your use case before use.
|
||||
|
||||
I2C Clock Configuration
|
||||
-----------------------
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_DEFAULT`: Default I2C source clock.
|
||||
:SOC_I2C_SUPPORT_XTAL: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_XTAL`: External crystal for I2C clock source.
|
||||
:SOC_I2C_SUPPORT_RTC: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_RC_FAST`: Internal 20 MHz RC oscillator for I2C clock source.
|
||||
:SOC_I2C_SUPPORT_APB: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_APB`: APB clock as I2C clock source.
|
||||
:SOC_I2C_SUPPORT_REF_TICK: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_REF_TICK`: 1 MHZ clock.
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
The I2C driver offers following services:
|
||||
|
||||
- :ref:`i2c-resource-allocation` - covers how to allocate I2C bus with properly set of configurations. It also covers how to recycle the resources when they finished working.
|
||||
- :ref:`i2c-master-controller` - covers behavior of I2C master controller. Introduce data transmit, data receive, and data transmit and receive.
|
||||
- :ref:`i2c-slave-controller` - covers behavior of I2C slave controller. Involve data transmit and data receive.
|
||||
- :ref:`i2c-power-management` - describes how different source clock will affect power consumption.
|
||||
- :ref:`i2c-iram-safe` - describes tips on how to make the I2C interrupt work better along with a disabled cache.
|
||||
- :ref:`i2c-thread-safety` - lists which APIs are guaranteed to be thread safe by the driver.
|
||||
- :ref:`i2c-kconfig-options` - lists the supported Kconfig options that can bring different effects to the driver.
|
||||
|
||||
.. _i2c-resource-allocation:
|
||||
|
||||
Resource Allocation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The I2C master bus is represented by :cpp:type:`i2c_master_bus_handle_t` in the driver. The available ports are managed in a resource pool that allocates a free port on request.
|
||||
|
||||
Install I2C master bus and device
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The I2C master bus is designed based on bus-device model. So :cpp:type:`i2c_master_bus_config_t` and :cpp:type:`i2c_device_config_t` are required separately to allocate the I2C master bus instance and I2C device instance.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i2c/i2c_master_module.png
|
||||
:align: center
|
||||
:alt: I2C master bus-device module
|
||||
|
||||
I2C master bus-device module
|
||||
|
||||
I2C master bus requires the configuration that specified by :cpp:type:`i2c_master_bus_config_t`:
|
||||
|
||||
- :cpp:member:`i2c_master_bus_config_t::i2c_port` sets the I2C port used by the controller.
|
||||
- :cpp:member:`i2c_master_bus_config_t::sda_io_num` sets the GPIO number for the serial data bus (SDA).
|
||||
- :cpp:member:`i2c_master_bus_config_t::scl_io_num` sets the GPIO number for the serial clock bus (SCL).
|
||||
- :cpp:member:`i2c_master_bus_config_t::clk_source` selects the source clock for I2C bus. The available clocks are listed in :cpp:type:`i2c_clock_source_t`. For the effect on power consumption of different clock source, please refer to :ref:`i2c-power-management` section.
|
||||
- :cpp:member:`i2c_master_bus_config_t::glitch_ignore_cnt` sets the glitch period of master bus, if the glitch period on the line is less than this value, it can be filtered out, typically value is 7.
|
||||
- :cpp:member:`i2c_master_bus_config_t::intr_priority` sets the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1, 2 or 3), otherwise use the priority indicated by :cpp:member:`i2c_master_bus_config_t::intr_priority`. Please use the number form (1, 2, 3) , not the bitmask form ((1<<1), (1<<2), (1<<3)).
|
||||
- :cpp:member:`i2c_master_bus_config_t::trans_queue_depth` sets the depth of internal transfer queue. Only valid in asynchronous transaction.
|
||||
- :cpp:member:`i2c_master_bus_config_t::enable_internal_pullup` enables internal pullups. Note: This is not strong enough to pullup buses under high-speed frequency. A suitable external pullup is recommended.
|
||||
- :cpp:member:`i2c_master_bus_config_t::allow_pd` configures if the driver allows the system to power down the peripheral in light sleep mode. Before entering sleep, the system will backup the I2C register context, which will be restored later when the system exit the sleep mode. Powering down the peripheral can save more power, but at the cost of more memory consumed to save the register context. It's a tradeoff between power consumption and memory consumption. This configuration option relies on specific hardware feature, if you enable it on an unsupported chip, you will see error message like ``not able to power down in light sleep``.
|
||||
|
||||
If the configurations in :cpp:type:`i2c_master_bus_config_t` is specified, then :cpp:func:`i2c_new_master_bus` can be called to allocate and initialize an I2C master bus. This function will return an I2C bus handle if it runs correctly. Specifically, when there are no more I2C port available, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error.
|
||||
|
||||
I2C master device requires the configuration that specified by :cpp:type:`i2c_device_config_t`:
|
||||
|
||||
- :cpp:member:`i2c_device_config_t::dev_addr_length` configure the address bit length of the slave device. It can be chosen from enumerator :cpp:enumerator:`I2C_ADDR_BIT_LEN_7` or :cpp:enumerator:`I2C_ADDR_BIT_LEN_10` (if supported).
|
||||
- :cpp:member:`i2c_device_config_t::device_address` sets the I2C device raw address. Please parse the device address to this member directly. For example, the device address is 0x28, then parse 0x28 to :cpp:member:`i2c_device_config_t::device_address`, don't carry a write or read bit.
|
||||
- :cpp:member:`i2c_device_config_t::scl_speed_hz` sets the SCL line frequency of this device.
|
||||
- :cpp:member:`i2c_device_config_t::scl_wait_us` sets the SCL await time (in μs). Usually this value should not be very small because slave stretch will happen in pretty long time (It's possible even stretch for 12 ms). Set ``0`` means use default register value.
|
||||
|
||||
|
||||
Once the :cpp:type:`i2c_device_config_t` structure is populated with mandatory parameters, :cpp:func:`i2c_master_bus_add_device` can be called to allocate an I2C device instance and mounted to the master bus then. This function will return an I2C device handle if it runs correctly. Specifically, when the I2C bus is not initialized properly, calling this function will result in a :c:macro:`ESP_ERR_INVALID_ARG` error.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include "driver/i2c_master.h"
|
||||
|
||||
i2c_master_bus_config_t i2c_mst_config = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.i2c_port = TEST_I2C_PORT,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.flags.enable_internal_pullup = true,
|
||||
};
|
||||
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
|
||||
|
||||
i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = 0x58,
|
||||
.scl_speed_hz = 100000,
|
||||
};
|
||||
|
||||
i2c_master_dev_handle_t dev_handle;
|
||||
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));
|
||||
|
||||
Get I2C master handle via port
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When the I2C master handle has been initialized in one module (e.g. the audio module), but it is not convenient to acquire this handle in another module (e.g. the video module). You can use the helper function, :cpp:func:`i2c_master_get_bus_handle` to retrieve the initialized handle via port. Ensure that the handle has already been initialized beforehand to avoid potential errors.
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Source File 1
|
||||
#include "driver/i2c_master.h"
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
i2c_master_bus_config_t i2c_mst_config = {
|
||||
... // same as others
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
|
||||
|
||||
// Source File 2
|
||||
#include "driver/i2c_master.h"
|
||||
i2c_master_bus_handle_t handle;
|
||||
ESP_ERROR_CHECK(i2c_master_get_bus_handle(0, &handle));
|
||||
|
||||
.. only:: SOC_LP_I2C_SUPPORTED
|
||||
|
||||
Install I2C master bus with LP I2C Peripheral
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Install I2C master bus with LP I2C peripheral is almost as same as how HP I2C peripheral is installed. However, there are still some difference should be taken focus on, including IOs, clock sources, I2C port number, etc. Following code will show how to install I2C master bus with LP_I2C.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include "driver/i2c_master.h"
|
||||
|
||||
i2c_master_bus_config_t i2c_mst_config = {
|
||||
.clk_source = LP_I2C_SCLK_DEFAULT, // clock source for LP I2C, might different from HP I2C
|
||||
.i2c_port = LP_I2C_NUM_0, // Assign to LP I2C port
|
||||
.scl_io_num = 7, // SCL IO number. Please refer to technical reference manual
|
||||
.sda_io_num = 6, // SDA IO number. Please refer to technical reference manual
|
||||
.glitch_ignore_cnt = 7,
|
||||
.flags.enable_internal_pullup = true,
|
||||
};
|
||||
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
|
||||
|
||||
i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = 0x58,
|
||||
.scl_speed_hz = 100000,
|
||||
};
|
||||
|
||||
i2c_master_dev_handle_t dev_handle;
|
||||
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));
|
||||
|
||||
Uninstall I2C master bus and device
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed I2C bus or device is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`i2c_master_bus_rm_device` or :cpp:func:`i2c_del_master_bus`, so as to release the underlying hardware.
|
||||
|
||||
Please note that removing all devices attached to bus before delete the master bus.
|
||||
|
||||
Install I2C slave device
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
I2C slave requires the configuration specified by :cpp:type:`i2c_slave_config_t`:
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:member:`i2c_slave_config_t::i2c_port` sets the I2C port used by the controller.
|
||||
- :cpp:member:`i2c_slave_config_t::sda_io_num` sets the GPIO number for serial data bus (SDA).
|
||||
- :cpp:member:`i2c_slave_config_t::scl_io_num` sets the GPIO number for serial clock bus (SCL).
|
||||
- :cpp:member:`i2c_slave_config_t::clk_source` selects the source clock for I2C bus. The available clocks are listed in :cpp:type:`i2c_clock_source_t`. For the effect on power consumption of different clock source, please refer to :ref:`i2c-power-management` section.
|
||||
- :cpp:member:`i2c_slave_config_t::send_buf_depth` sets the sending software buffer length.
|
||||
- :cpp:member:`i2c_slave_config_t::receive_buf_depth` sets the receiving software buffer length.
|
||||
- :cpp:member:`i2c_slave_config_t::intr_priority` sets the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1, 2 or 3), otherwise use the priority indicated by :cpp:member:`i2c_slave_config_t::intr_priority`. Please use the number form (1, 2, 3), instead of the bitmask form ((1<<1), (1<<2), (1<<3)). Please pay attention that once the interrupt priority is set, it cannot be changed until :cpp:func:`i2c_del_slave_device` is called.
|
||||
- :cpp:member:`i2c_slave_config_t::addr_bit_len` Set this variable to ``I2C_ADDR_BIT_LEN_10`` if the slave should have a 10-bit address.
|
||||
- :cpp:member:`i2c_slave_config_t::allow_pd` If set, the driver will backup/restore the I2C registers before/after entering/exist sleep mode. By this approach, the system can power off I2C's power domain. This can save power, but at the expense of more RAM being consumed.
|
||||
:SOC_I2C_SLAVE_SUPPORT_BROADCAST: - :cpp:member:`i2c_slave_config_t::broadcast_en` Set this to true to enable the slave broadcast. When the slave receives the general call address 0x00 from the master and the R/W bit followed is 0, it responds to the master regardless of its own address.
|
||||
- :cpp:member:`i2c_slave_config_t::enable_internal_pullup` Set this to enable internal pull-up. Even though, an output pull-up resistance is strongly recommended.
|
||||
|
||||
Once the :cpp:type:`i2c_slave_config_t` structure is populated with mandatory parameters, :cpp:func:`i2c_new_slave_device` can be called to allocate and initialize an I2C master bus. This function will return an I2C bus handle if it runs correctly. Specifically, when there are no more I2C port available, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error.
|
||||
|
||||
.. code:: c
|
||||
|
||||
i2c_slave_config_t i2c_slv_config = {
|
||||
.i2c_port = I2C_SLAVE_NUM,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.scl_io_num = I2C_SLAVE_SCL_IO,
|
||||
.sda_io_num = I2C_SLAVE_SDA_IO,
|
||||
.slave_addr = ESP_SLAVE_ADDR,
|
||||
.send_buf_depth = 100,
|
||||
.receive_buf_depth = 100,
|
||||
};
|
||||
|
||||
i2c_slave_dev_handle_t slave_handle;
|
||||
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
|
||||
|
||||
Uninstall I2C slave device
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed I2C bus is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`i2c_del_slave_device`, so that to release the underlying hardware.
|
||||
|
||||
.. _i2c-master-controller:
|
||||
|
||||
I2C Master Controller
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After installing the I2C master driver by :cpp:func:`i2c_new_master_bus`, {IDF_TARGET_NAME} is ready to communicate with other I2C devices. I2C APIs allow the standard transactions. Like the wave as follows:
|
||||
|
||||
.. wavedrom:: /../_static/diagrams/i2c/i2c_trans_wave.json
|
||||
|
||||
I2C Master Write
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
After installing I2C master bus successfully, you can simply call :cpp:func:`i2c_master_transmit` to write data to the slave device. The principle of this function can be explained by following chart.
|
||||
|
||||
In order to organize the process, the driver uses a command link, that should be populated with a sequence of commands and then passed to I2C controller for execution.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i2c/i2c_master_write_slave.png
|
||||
:align: center
|
||||
:alt: I2C master write to slave
|
||||
|
||||
I2C master write to slave
|
||||
|
||||
Simple example for writing data to slave:
|
||||
|
||||
.. code:: c
|
||||
|
||||
#define DATA_LENGTH 100
|
||||
i2c_master_bus_config_t i2c_mst_config = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.i2c_port = I2C_PORT_NUM_0,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.glitch_ignore_cnt = 7,
|
||||
};
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
|
||||
|
||||
i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = 0x58,
|
||||
.scl_speed_hz = 100000,
|
||||
};
|
||||
|
||||
i2c_master_dev_handle_t dev_handle;
|
||||
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));
|
||||
|
||||
ESP_ERROR_CHECK(i2c_master_transmit(dev_handle, data_wr, DATA_LENGTH, -1));
|
||||
|
||||
|
||||
I2C master write also supports transmit multi-buffer in one transaction. Take following transaction as a simple example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
uint8_t control_phase_byte = 0;
|
||||
size_t control_phase_size = 0;
|
||||
if (/*condition*/) {
|
||||
control_phase_byte = 1;
|
||||
control_phase_size = 1;
|
||||
}
|
||||
|
||||
uint8_t *cmd_buffer = NULL;
|
||||
size_t cmd_buffer_size = 0;
|
||||
if (/*condition*/) {
|
||||
uint8_t cmds[4] = {BYTESHIFT(lcd_cmd, 3), BYTESHIFT(lcd_cmd, 2), BYTESHIFT(lcd_cmd, 1), BYTESHIFT(lcd_cmd, 0)};
|
||||
cmd_buffer = cmds;
|
||||
cmd_buffer_size = 4;
|
||||
}
|
||||
|
||||
uint8_t *lcd_buffer = NULL;
|
||||
size_t lcd_buffer_size = 0;
|
||||
if (buffer) {
|
||||
lcd_buffer = (uint8_t*)buffer;
|
||||
lcd_buffer_size = buffer_size;
|
||||
}
|
||||
|
||||
i2c_master_transmit_multi_buffer_info_t lcd_i2c_buffer[3] = {
|
||||
{.write_buffer = &control_phase_byte, .buffer_size = control_phase_size},
|
||||
{.write_buffer = cmd_buffer, .buffer_size = cmd_buffer_size},
|
||||
{.write_buffer = lcd_buffer, .buffer_size = lcd_buffer_size},
|
||||
};
|
||||
|
||||
i2c_master_multi_buffer_transmit(handle, lcd_i2c_buffer, sizeof(lcd_i2c_buffer) / sizeof(i2c_master_transmit_multi_buffer_info_t), -1);
|
||||
|
||||
|
||||
I2C Master Read
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
After installing I2C master bus successfully, you can simply call :cpp:func:`i2c_master_receive` to read data from the slave device. The principle of this function can be explained by following chart.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i2c/i2c_master_read_slave.png
|
||||
:align: center
|
||||
:alt: I2C master read from slave
|
||||
|
||||
I2C master read from slave
|
||||
|
||||
Simple example for reading data from slave:
|
||||
|
||||
.. code:: c
|
||||
|
||||
#define DATA_LENGTH 100
|
||||
i2c_master_bus_config_t i2c_mst_config = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.i2c_port = I2C_PORT_NUM_0,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.glitch_ignore_cnt = 7,
|
||||
};
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
|
||||
|
||||
i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = 0x58,
|
||||
.scl_speed_hz = 100000,
|
||||
};
|
||||
|
||||
i2c_master_dev_handle_t dev_handle;
|
||||
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));
|
||||
|
||||
i2c_master_receive(dev_handle, data_rd, DATA_LENGTH, -1);
|
||||
|
||||
I2C Master Write and Read
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Some I2C device needs write configurations before reading data from it. Therefore, an interface called :cpp:func:`i2c_master_transmit_receive` can help. The principle of this function can be explained by following chart.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i2c/i2c_master_write_read_slave.png
|
||||
:align: center
|
||||
:alt: I2C master write to slave and read from slave
|
||||
|
||||
I2C master write to slave and read from slave
|
||||
|
||||
Please note that no STOP condition bit is inserted between the write and read operations; therefore, this function is suited to read a register from an I2C device. A simple example for writing and reading from a slave device:
|
||||
|
||||
.. code:: c
|
||||
|
||||
i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = 0x58,
|
||||
.scl_speed_hz = 100000,
|
||||
};
|
||||
|
||||
i2c_master_dev_handle_t dev_handle;
|
||||
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));
|
||||
uint8_t buf[20] = {0x20};
|
||||
uint8_t buffer[2];
|
||||
ESP_ERROR_CHECK(i2c_master_transmit_receive(dev_handle, buf, sizeof(buf), buffer, 2, -1));
|
||||
|
||||
I2C Master Probe
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
I2C driver can use :cpp:func:`i2c_master_probe` to detect whether the specific device has been connected on I2C bus. If this function return ``ESP_OK``, that means the device has been detected.
|
||||
|
||||
.. important::
|
||||
|
||||
Pull-ups must be connected to the SCL and SDA pins when this function is called. If you get `ESP_ERR_TIMEOUT` while `xfer_timeout_ms` was parsed correctly, you should check the pull-up resistors. If you do not have proper resistors nearby, setting `flags.enable_internal_pullup` as true is also acceptable.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i2c/i2c_master_probe.png
|
||||
:align: center
|
||||
:alt: I2C master probe
|
||||
|
||||
I2C master probe
|
||||
|
||||
Simple example for probing an I2C device:
|
||||
|
||||
.. code:: c
|
||||
|
||||
i2c_master_bus_config_t i2c_mst_config_1 = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.i2c_port = TEST_I2C_PORT,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.flags.enable_internal_pullup = true,
|
||||
};
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config_1, &bus_handle));
|
||||
ESP_ERROR_CHECK(i2c_master_probe(bus_handle, 0x22, -1));
|
||||
ESP_ERROR_CHECK(i2c_del_master_bus(bus_handle));
|
||||
|
||||
|
||||
I2C Master Execute Customized Transactions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Not all I2C devices strictly adhere to the standard I2C protocol, as different manufacturers may implement custom variations. For example, some devices require the address to be shifted, while others do not. Similarly, certain devices mandate acknowledgment (ACK) checks for specific operations, whereas others might not. To accommodate these variations, :cpp:func:`i2c_master_execute_defined_operations` function allow developers to define and execute fully customized I2C transactions. This flexibility ensures seamless communication with non-standard devices by tailoring the transaction sequence, addressing, and acknowledgment behavior to the device's specific requirements.
|
||||
|
||||
.. note::
|
||||
|
||||
If you want to define your address in :cpp:type:`i2c_operation_job_t`, please set :cpp:member:`i2c_device_config_t::device_address` as ``I2C_DEVICE_ADDRESS_NOT_USED`` to skip internal address configuration in driver.
|
||||
|
||||
For address configuration of user defined transactions, given that the device address is ``0x20``, there are two situations. See following example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
i2c_device_config_t i2c_device = {
|
||||
.device_address = I2C_DEVICE_ADDRESS_NOT_USED,
|
||||
.scl_speed_hz = 100 * 1000,
|
||||
.scl_wait_us = 20000,
|
||||
};
|
||||
|
||||
i2c_master_dev_handle_t dev_handle;
|
||||
|
||||
i2c_master_bus_add_device(bus_handle, &i2c_device, &dev_handle);
|
||||
|
||||
// Situation one: The device does not allow device address shift
|
||||
uint8_t address1 = 0x20;
|
||||
i2c_operation_job_t i2c_ops1[] = {
|
||||
{ .command = I2C_MASTER_CMD_START },
|
||||
{ .command = I2C_MASTER_CMD_WRITE, .write = { .ack_check = false, .data = (uint8_t *) &address1, .total_bytes = 1 } },
|
||||
{ .command = I2C_MASTER_CMD_STOP },
|
||||
};
|
||||
|
||||
// Situation one: The device address should be left shifted by one byte to include a write bit or a read bit (official protocol)
|
||||
uint8_t address2 = (0x20 << 1 | 0); // (0x20 << 1 | 1)
|
||||
i2c_operation_job_t i2c_ops2[] = {
|
||||
{ .command = I2C_MASTER_CMD_START },
|
||||
{ .command = I2C_MASTER_CMD_WRITE, .write = { .ack_check = false, .data = (uint8_t *) &address2, .total_bytes = 1 } },
|
||||
{ .command = I2C_MASTER_CMD_STOP },
|
||||
};
|
||||
|
||||
Some devices do not require an address, and allow direct transaction with data:
|
||||
|
||||
.. code:: c
|
||||
|
||||
uint8_t data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
|
||||
|
||||
i2c_operation_job_t i2c_ops[] = {
|
||||
{ .command = I2C_MASTER_CMD_START },
|
||||
{ .command = I2C_MASTER_CMD_WRITE, .write = { .ack_check = false, .data = (uint8_t *)data, .total_bytes = 8 } },
|
||||
{ .command = I2C_MASTER_CMD_STOP },
|
||||
};
|
||||
|
||||
i2c_master_execute_defined_operations(dev_handle, i2c_ops, sizeof(i2c_ops) / sizeof(i2c_operation_job_t), -1);
|
||||
|
||||
The principle of read operations is the same as that of write operations. Note to always ensure the last byte read before the stop condition is a ``NACK``. An example is as follows:
|
||||
|
||||
.. code:: c
|
||||
|
||||
uint8_t address = (0x20 << 1 | 1);
|
||||
uint8_t rcv_data[10] = {};
|
||||
|
||||
i2c_operation_job_t i2c_ops[] = {
|
||||
{ .command = I2C_MASTER_CMD_START },
|
||||
{ .command = I2C_MASTER_CMD_WRITE, .write = { .ack_check = false, .data = (uint8_t *) &address, .total_bytes = 1 } },
|
||||
{ .command = I2C_MASTER_CMD_READ, .read = { .ack_value = I2C_ACK_VAL, .data = (uint8_t *)rcv_data, .total_bytes = 9 } },
|
||||
{ .command = I2C_MASTER_CMD_READ, .read = { .ack_value = I2C_NACK_VAL, .data = (uint8_t *)(rcv_data + 9), .total_bytes = 1 } }, // This must be NACK
|
||||
{ .command = I2C_MASTER_CMD_STOP },
|
||||
};
|
||||
|
||||
i2c_master_execute_defined_operations(dev_handle, i2c_ops, sizeof(i2c_ops) / sizeof(i2c_operation_job_t), -1);
|
||||
|
||||
.. _i2c-slave-controller:
|
||||
|
||||
I2C Slave Controller
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After installing the I2C slave driver by :cpp:func:`i2c_new_slave_device`, {IDF_TARGET_NAME} is ready to communicate with other I2C masters as a slave.
|
||||
|
||||
The I2C slave is not as active as the I2C master, which knows when to send data and when to receive it. The I2C slave is very passive in most cases, meaning the I2C slave's ability to send and receive data is largely dependent on the master's actions. Therefore, we implement two callback functions in the driver to handle read and write requests from the I2C master.
|
||||
|
||||
I2C Slave Write
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
You can get I2C slave write event by registering :cpp:member:`i2c_slave_event_callbacks_t::on_request` callback. Then, in a task where the request event is triggered, you can call ``i2c_slave_write`` to send data.
|
||||
|
||||
A simple example for transmitting data:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Prepare a callback function
|
||||
static bool i2c_slave_request_cb(i2c_slave_dev_handle_t i2c_slave, const i2c_slave_request_event_data_t *evt_data, void *arg)
|
||||
{
|
||||
i2c_slave_event_t evt = I2C_SLAVE_EVT_TX;
|
||||
BaseType_t xTaskWoken = 0;
|
||||
xQueueSendFromISR(context->event_queue, &evt, &xTaskWoken);
|
||||
return xTaskWoken;
|
||||
}
|
||||
|
||||
// Register callback in a task
|
||||
i2c_slave_event_callbacks_t cbs = {
|
||||
.on_request = i2c_slave_request_cb,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(context.handle, &cbs, &context));
|
||||
|
||||
// Wait for request event and send data in a task
|
||||
static void i2c_slave_task(void *arg)
|
||||
{
|
||||
uint8_t buffer_size = 64;
|
||||
uint32_t write_len;
|
||||
uint8_t *data_buffer;
|
||||
|
||||
while (true) {
|
||||
i2c_slave_event_t evt;
|
||||
if (xQueueReceive(context->event_queue, &evt, 10) == pdTRUE) {
|
||||
ESP_ERROR_CHECK(i2c_slave_write(handle, data_buffer, buffer_size, &write_len, 1000));
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
I2C Slave Reset TX FIFO
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In some scenarios, the slave may prepare more data than the master actually reads. For example, if the slave prepares 16 bytes of data but the master only reads 8 bytes, the remaining 8 bytes will stay in the TX FIFO. To prepare fresh data for the next transaction, you can use :cpp:func:`i2c_slave_reset_tx_fifo` to clear the TX FIFO.
|
||||
|
||||
.. note::
|
||||
|
||||
It is recommended to call this function after the master has completed the read transaction to ensure data integrity.
|
||||
|
||||
Simple example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// First write, data may not be completely read by master
|
||||
ESP_ERROR_CHECK(i2c_slave_write(handle, data_buffer_1, buffer_size_1, &write_len_1, 1000));
|
||||
|
||||
// Wait for the next master read transaction, here we simply use a delay for demonstration
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
// Clear remaining data in TX FIFO
|
||||
ESP_ERROR_CHECK(i2c_slave_reset_tx_fifo(handle));
|
||||
|
||||
// Second write, new data will be sent normally
|
||||
ESP_ERROR_CHECK(i2c_slave_write(handle, data_buffer_2, buffer_size_2, &write_len_2, 1000));
|
||||
|
||||
I2C Slave Read
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Same as write event, you can get I2C slave read event by registering :cpp:member:`i2c_slave_event_callbacks_t::on_receive` callback. Then, in a task where the request event is triggered, you can save the data and do what you want.
|
||||
|
||||
A simple example for receiving data:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Prepare a callback function
|
||||
static bool i2c_slave_receive_cb(i2c_slave_dev_handle_t i2c_slave, const i2c_slave_rx_done_event_data_t *evt_data, void *arg)
|
||||
{
|
||||
i2c_slave_event_t evt = I2C_SLAVE_EVT_RX;
|
||||
BaseType_t xTaskWoken = 0;
|
||||
// You can get data and length via i2c_slave_rx_done_event_data_t
|
||||
xQueueSendFromISR(context->event_queue, &evt, &xTaskWoken);
|
||||
return xTaskWoken;
|
||||
}
|
||||
|
||||
// Register callback in a task
|
||||
i2c_slave_event_callbacks_t cbs = {
|
||||
.on_receive = i2c_slave_receive_cb,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(context.handle, &cbs, &context));
|
||||
|
||||
Register Event Callbacks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
I2C master callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When an I2C master bus triggers an interrupt, a specific event will be generated and notify the CPU. If you have some functions that need to be called when those events occurred, you can hook your functions to the ISR (Interrupt Service Routine) by calling :cpp:func:`i2c_master_register_event_callbacks`. Since the registered callback functions are called in the interrupt context, users should ensure the callback function doesn't attempt to block (e.g. by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from the function). The callback functions are required to return a boolean value, to tell the ISR whether a high priority task is woken up by it.
|
||||
|
||||
I2C master event callbacks are listed in the :cpp:type:`i2c_master_event_callbacks_t`.
|
||||
|
||||
Although I2C is a synchronous communication protocol, asynchronous behavior is supported by registering above callbacks. In this way, I2C APIs will be non-blocking interface. But note that on the same bus, only one device can adopt asynchronous operation.
|
||||
|
||||
.. important::
|
||||
|
||||
I2C master asynchronous transaction is still an experimental feature (The issue is that when asynchronous transaction is very large, it will cause memory problem).
|
||||
|
||||
- :cpp:member:`i2c_master_event_callbacks_t::on_recv_done` sets a callback function for master "transaction-done" event. The function prototype is declared in :cpp:type:`i2c_master_callback_t`.
|
||||
|
||||
I2C slave callbacks
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When an I2C slave bus triggers an interrupt, a specific event will be generated and notify the CPU. If you have some function that needs to be called when those events occurred, you can hook your function to the ISR (Interrupt Service Routine) by calling :cpp:func:`i2c_slave_register_event_callbacks`. Since the registered callback functions are called in the interrupt context, users should ensure the callback function doesn't attempt to block (e.g. by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from the function). The callback function has a boolean return value, to tell the caller whether a high priority task is woken up by it.
|
||||
|
||||
I2C slave event callbacks are listed in the :cpp:type:`i2c_slave_event_callbacks_t`.
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:member:`i2c_slave_event_callbacks_t::on_request` sets a callback function for request event.
|
||||
- :cpp:member:`i2c_slave_event_callbacks_t::on_receive` sets a callback function for receive event. The function prototype is declared in :cpp:type:`i2c_slave_received_callback_t`.
|
||||
|
||||
.. _i2c-power-management:
|
||||
|
||||
Power Management
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
.. only:: SOC_I2C_SUPPORT_APB
|
||||
|
||||
When the power management is enabled (i.e. :ref:`CONFIG_PM_ENABLE` is on), the system will adjust or stop the source clock of I2C FIFO before going into Light-sleep mode, thus potentially changing the I2C signals and leading to transmitting or receiving invalid data.
|
||||
|
||||
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever user creates an I2C bus that has selected :cpp:enumerator:`I2C_CLK_SRC_APB` as the clock source, the driver will guarantee that the power management lock is acquired when I2C operations begin and the lock will be released automatically when I2C operations finish.
|
||||
|
||||
.. only:: SOC_I2C_SUPPORT_REF_TICK
|
||||
|
||||
If the controller clock source is selected to :cpp:enumerator:`I2C_CLK_SRC_REF_TICK`, then the driver won't install power management lock for it, which is more suitable for a low power application as long as the source clock can still provide sufficient resolution.
|
||||
|
||||
.. only:: SOC_I2C_SUPPORT_XTAL
|
||||
|
||||
If the controller clock source is selected to :cpp:enumerator:`I2C_CLK_SRC_XTAL`, then the driver won't install power management lock for it, which is more suitable for a low power application as long as the source clock can still provide sufficient resolution.
|
||||
|
||||
.. _i2c-iram-safe:
|
||||
|
||||
IRAM Safe
|
||||
^^^^^^^^^
|
||||
|
||||
By default, the I2C interrupt will be deferred when the cache is disabled for reasons like writing or erasing flash. Thus the event callback functions will not get executed in time, which is not expected in a real-time application.
|
||||
|
||||
There's a Kconfig option :ref:`CONFIG_I2C_ISR_IRAM_SAFE` that will:
|
||||
|
||||
1. Enable the interrupt being serviced even when cache is disabled.
|
||||
2. Place all functions that used by the ISR into IRAM.
|
||||
3. Place driver object into DRAM (in case it's mapped to PSRAM by accident).
|
||||
|
||||
This will allow the interrupt to run while the cache is disabled but will come at the cost of increased IRAM consumption.
|
||||
|
||||
.. _i2c-thread-safety:
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The factory function :cpp:func:`i2c_new_master_bus` and :cpp:func:`i2c_new_slave_device` are guaranteed to be thread safe by the driver, which means that the functions can be called from different RTOS tasks without protection by extra locks.
|
||||
|
||||
I2C master operation functions are also guaranteed to be thread safe by bus operation semaphore.
|
||||
|
||||
- :cpp:func:`i2c_master_transmit`
|
||||
- :cpp:func:`i2c_master_multi_buffer_transmit`
|
||||
- :cpp:func:`i2c_master_transmit_receive`
|
||||
- :cpp:func:`i2c_master_receive`
|
||||
- :cpp:func:`i2c_master_probe`
|
||||
|
||||
I2C slave operation functions are also guaranteed to be thread safe by bus operation semaphore.
|
||||
|
||||
- :cpp:func:`i2c_slave_write`
|
||||
|
||||
Other functions are not guaranteed to be thread-safe. Thus, you should avoid calling them in different tasks without mutex protection.
|
||||
|
||||
.. _i2c-kconfig-options:
|
||||
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_I2C_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled, see also :ref:`i2c-iram-safe` for more information.
|
||||
- :ref:`CONFIG_I2C_ENABLE_DEBUG_LOG` is used to enable the debug log at the cost of increased firmware binary size.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`peripherals/i2c/i2c_basic` demonstrates the basic steps to initialize the I2C master driver and read data from a MPU9250 sensor.
|
||||
|
||||
- :example:`peripherals/i2c/i2c_eeprom` demonstrates how to use the I2C master mode to read and write data from a connected EEPROM.
|
||||
|
||||
- :example:`peripherals/i2c/i2c_tools` demonstrates how to use the I2C Tools for developing I2C related applications, providing command-line tools for configuring the I2C bus, scanning for devices, reading and setting registers, and examining registers.
|
||||
|
||||
- :example:`peripherals/i2c/i2c_slave_network_sensor` demonstrates how to use the I2C slave for developing I2C related applications, providing how I2C slave can behave as a network sensor, and use event callbacks to receive and send data.
|
||||
|
||||
- :example:`peripherals/i2c/i2c_u8g2` demonstrates how to use the I2C master mode to interface with U8G2 library for controlling OLED displays.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/i2c_master.inc
|
||||
|
||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
||||
|
||||
.. include-build-file:: inc/i2c_slave.inc
|
||||
|
||||
.. include-build-file:: inc/components/esp_driver_i2c/include/driver/i2c_types.inc
|
||||
.. include-build-file:: inc/components/esp_hal_i2c/include/hal/i2c_types.inc
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,603 @@
|
||||
====================
|
||||
I3C master interface
|
||||
====================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
{IDF_TARGET_I3C_INTERNAL_PULLUP_PIN:default="Not updated!", esp32p4="GPIO32/GPIO33"}
|
||||
|
||||
This document introduces the I3C master driver functionality of ESP-IDF. The table of contents is as follows:
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
I3C is a serial synchronous half-duplex communication protocol and an enhanced version of the I2C protocol. While maintaining most compatibility with I2C, I3C provides higher speed, lower power consumption, and richer features.
|
||||
|
||||
For hardware-related information about I3C, please refer to the I3C Technical Reference Manual.
|
||||
|
||||
The main features of the I3C protocol include:
|
||||
|
||||
- **Backward compatibility with I2C**: I3C bus can support both I2C and I3C devices simultaneously
|
||||
- **Higher speed**: I3C can reach up to 12.5 MHz, while I2C can reach up to 1 MHz
|
||||
- **Static address assignment**: Manually assign dynamic addresses based on static addresses through the SETDASA procedure
|
||||
- **Dynamic address assignment**: Automatically assign dynamic addresses through the ENTDAA procedure to avoid address conflicts
|
||||
- **In-band interrupt (IBI)**: Supports slave devices sending interrupt requests through the I3C bus without additional interrupt lines
|
||||
- **Common Command Code (CCC)**: Supports broadcast and direct CCC commands for bus management and device configuration
|
||||
|
||||
.. important::
|
||||
|
||||
1. When using with I2C devices, ensure that I2C devices mounted on the I3C bus **must not** support or enable clock stretching, otherwise when I2C slaves stretch the clock, it will cause the hardware state machine to hang.
|
||||
2. The I3C frequency depends on circuit design and timing adjustment. Please refer to the I3C device manual you are using.
|
||||
3. Some I3C slave devices have strict timing requirements for their acknowledgment mechanism (ACK/NACK). Please refer to the I3C slave device manual you are using.
|
||||
|
||||
Quick Start
|
||||
===========
|
||||
|
||||
This section will quickly guide you through using the I3C master driver. It demonstrates how to create a bus, add devices, and perform data transfers. The general usage flow is as follows:
|
||||
|
||||
.. blockdiag::
|
||||
:scale: 100%
|
||||
:caption: General usage flow of I3C driver (click image to view full size)
|
||||
:align: center
|
||||
|
||||
blockdiag {
|
||||
default_fontsize = 14;
|
||||
node_width = 250;
|
||||
node_height = 80;
|
||||
class emphasis [color = pink, style = dashed];
|
||||
|
||||
create_bus [label="Create I3C Bus\n(i3c_new_master_bus)"];
|
||||
add_device [label="Add Device\n(add_i2c_device / \nadd_i3c_static_device / \nscan_devices_by_entdaa)"];
|
||||
transfer [label="Data Transfer\n(transmit / receive / \ntransmit_receive)", class="emphasis"];
|
||||
cleanup [label="Remove Device and Bus\n(rm_device / del_master_bus)"];
|
||||
|
||||
create_bus -> add_device -> transfer -> cleanup;
|
||||
}
|
||||
|
||||
Create I3C Bus
|
||||
--------------
|
||||
|
||||
The I3C master bus is represented by :cpp:type:`i3c_master_bus_handle_t` in the driver. The driver internally maintains a resource pool that can manage multiple buses and allocates free bus ports when requested.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i3c/i3c_bus_structure.svg
|
||||
:align: center
|
||||
:alt: I3C Bus Structure
|
||||
|
||||
I3C Bus Structure
|
||||
|
||||
When creating an I3C bus instance, we need to configure GPIO pins, clock source, frequency, and other parameters through :cpp:type:`i3c_master_bus_config_t`. These parameters will determine how the bus operates. The following code shows how to create a basic I3C bus:
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include "driver/i3c_master.h"
|
||||
|
||||
i3c_master_bus_config_t i3c_mst_config = {
|
||||
.sda_io_num = I3C_MASTER_SDA_IO, // GPIO number for SDA signal line
|
||||
.scl_io_num = I3C_MASTER_SCL_IO, // GPIO number for SCL signal line
|
||||
.i3c_scl_freq_hz_od = 600 * 1000, // SCL clock frequency in Open-Drain mode, please refer to device manual for appropriate values
|
||||
.i3c_scl_freq_hz_pp = 2 * 1000 * 1000, // SCL clock frequency in Push-Pull mode, please refer to device manual for appropriate values
|
||||
.i3c_sda_od_hold_time_ns = 25, // Hold time of SDA after SCL falling edge in Open-Drain mode (nanoseconds), recommended to set to 25, please refer to device manual for appropriate values
|
||||
.i3c_sda_pp_hold_time_ns = 0, // Hold time of SDA after SCL falling edge in Push-Pull mode (nanoseconds), default is 0, please refer to device manual for appropriate values
|
||||
.entdaa_device_num = 0, // Maximum number of devices allowed to be dynamically discovered through ENTDAA, range from [0x0, 0x7F], 0x0 means dynamic device discovery is not used
|
||||
};
|
||||
|
||||
i3c_master_bus_handle_t bus_handle;
|
||||
ESP_ERROR_CHECK(i3c_new_master_bus(&i3c_mst_config, &bus_handle));
|
||||
|
||||
.. note::
|
||||
|
||||
The I3C protocol requires automatic switching between open-drain and push-pull modes during each transfer between the addressing phase and data transfer phase. On ESP32-P4, only {IDF_TARGET_I3C_INTERNAL_PULLUP_PIN} support automatic opening/closing of internal pull-up switches and support user adjustment of internal pull-up resistance values. When using other GPIOs, the internal pull-up may be insufficient, and it is recommended to add external pull-up resistors. However, in push-pull mode, this pull-up cannot be canceled, which may increase additional power consumption.
|
||||
|
||||
Add and Drive Legacy I2C Devices
|
||||
---------------------------------
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i3c/i3c_i2c_write.svg
|
||||
:align: center
|
||||
:alt: Write to legacy I2C device
|
||||
|
||||
Write to legacy I2C device
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i3c/i3c_i2c_read.svg
|
||||
:align: center
|
||||
:alt: Read from legacy I2C device
|
||||
|
||||
Read from legacy I2C device
|
||||
|
||||
The I3C bus supports compatibility with legacy I2C devices. If you need to connect a legacy I2C device (such as EEPROM, sensors, etc.) to the I3C bus, please note that I2C slaves **must not** perform clock stretching during I3C communication. The specific process can be done as follows:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// 1. Create I3C bus (refer to the code above)
|
||||
i3c_master_bus_handle_t bus_handle;
|
||||
ESP_ERROR_CHECK(i3c_new_master_bus(&i3c_mst_config, &bus_handle));
|
||||
|
||||
// 2. Add I2C device
|
||||
i3c_device_i2c_config_t i2c_dev_cfg = {
|
||||
.device_address = 0x50, // 7-bit address of I2C device
|
||||
.scl_freq_hz = 100 * 1000, // Clock frequency of I2C device (100 kHz)
|
||||
};
|
||||
i3c_master_i2c_device_handle_t i2c_dev_handle;
|
||||
ESP_ERROR_CHECK(i3c_master_bus_add_i2c_device(bus_handle, &i2c_dev_cfg, &i2c_dev_handle));
|
||||
|
||||
// 3. Write data to I2C device
|
||||
uint8_t write_data[10] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};
|
||||
ESP_ERROR_CHECK(i3c_master_i2c_device_transmit(i2c_dev_handle, write_data, sizeof(write_data), -1)); // -1 means infinite timeout
|
||||
|
||||
// 4. Read data from I2C device
|
||||
uint8_t read_data[10] = {0};
|
||||
ESP_ERROR_CHECK(i3c_master_i2c_device_receive(i2c_dev_handle, read_data, sizeof(read_data), -1));
|
||||
|
||||
// 5. Write-read combined transaction (write register address first, then read data, no STOP in between)
|
||||
uint8_t reg_addr = 0x00;
|
||||
uint8_t read_buffer[5] = {0};
|
||||
ESP_ERROR_CHECK(i3c_master_i2c_device_transmit_receive(i2c_dev_handle, ®_addr, 1, read_buffer, sizeof(read_buffer), -1));
|
||||
|
||||
// 6. Clean up resources
|
||||
ESP_ERROR_CHECK(i3c_master_bus_rm_i2c_device(i2c_dev_handle));
|
||||
ESP_ERROR_CHECK(i3c_del_master_bus(bus_handle));
|
||||
|
||||
In this scenario, we:
|
||||
|
||||
1. Created an I3C bus instance through :cpp:func:`i3c_new_master_bus`
|
||||
2. Added an I2C device through :cpp:func:`i3c_master_bus_add_i2c_device`, which requires specifying the device's static address and clock frequency
|
||||
3. Used :cpp:func:`i3c_master_i2c_device_transmit` to write data. By default, it works in blocking mode. For non-blocking mode, please refer to :ref:`dma-support`. The same applies to other transfer functions.
|
||||
4. Used :cpp:func:`i3c_master_i2c_device_receive` to read data
|
||||
5. Used :cpp:func:`i3c_master_i2c_device_transmit_receive` to execute write-read combined transactions (commonly used to write register address first, then read data, with no STOP bit in between)
|
||||
6. Finally cleaned up resources
|
||||
|
||||
Add and Drive I3C Devices via SETDASA
|
||||
--------------------------------------
|
||||
|
||||
For specific behaviors that may occur during I3C transfers, please refer to the standard I3C protocol. The following diagram is used to briefly explain the behavior in I3C transfers to understand the I3C transfer diagrams in this document:
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i3c/i3c_icon.svg
|
||||
:align: center
|
||||
:alt: I3C Transfer Legend
|
||||
|
||||
I3C Transfer Legend
|
||||
|
||||
If you know the static address of an I3C device, you can add the device using the SETDASA method:
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i3c/i3c_setdasa.svg
|
||||
:align: center
|
||||
:alt: I3C Directed Dynamic Address Assignment
|
||||
|
||||
I3C Directed Dynamic Address Assignment
|
||||
|
||||
.. code:: c
|
||||
|
||||
// 1. Create I3C bus
|
||||
i3c_master_bus_handle_t bus_handle;
|
||||
ESP_ERROR_CHECK(i3c_new_master_bus(&i3c_mst_config, &bus_handle));
|
||||
|
||||
// 2. Add I3C device (using SETDASA)
|
||||
i3c_device_i3c_config_t i3c_dev_cfg = {
|
||||
.dynamic_addr = 0x08, // Dynamic address assigned to the device, can be any value except reserved addresses in the I3C protocol, or can be obtained through `i3c_master_get_valid_address_slot` to get an available dynamic address
|
||||
.static_addr = 0x74, // Static address of the device (obtained from device manual)
|
||||
};
|
||||
i3c_master_i3c_device_handle_t i3c_dev_handle;
|
||||
ESP_ERROR_CHECK(i3c_master_bus_add_i3c_static_device(bus_handle, &i3c_dev_cfg, &i3c_dev_handle));
|
||||
|
||||
// 3. Write data to I3C device
|
||||
uint8_t write_data[100] = {0};
|
||||
ESP_ERROR_CHECK(i3c_master_i3c_device_transmit(i3c_dev_handle, write_data, sizeof(write_data), -1));
|
||||
|
||||
// 4. Read data from I3C device
|
||||
uint8_t read_data[100] = {0};
|
||||
ESP_ERROR_CHECK(i3c_master_i3c_device_receive(i3c_dev_handle, read_data, sizeof(read_data), -1));
|
||||
|
||||
// 5. Write-read combined transaction
|
||||
uint8_t reg_addr = 0x12;
|
||||
uint8_t read_buffer[10] = {0};
|
||||
ESP_ERROR_CHECK(i3c_master_i3c_device_transmit_receive(i3c_dev_handle, ®_addr, 1, read_buffer, sizeof(read_buffer), -1));
|
||||
|
||||
// 6. Clean up resources
|
||||
ESP_ERROR_CHECK(i3c_master_bus_rm_i3c_device(i3c_dev_handle));
|
||||
ESP_ERROR_CHECK(i3c_del_master_bus(bus_handle));
|
||||
|
||||
In this scenario:
|
||||
|
||||
1. We use :cpp:func:`i3c_master_bus_add_i3c_static_device` to add an I3C device
|
||||
2. We need to provide the device's static address (obtained from device manual) and the dynamic address to be assigned
|
||||
3. The driver automatically executes the SETDASA procedure to assign the dynamic address to the device. If there is an address conflict, it will return ``ESP_ERR_INVALID_STATE``.
|
||||
4. After that, we can use the dynamic address for data transfers through :cpp:func:`i3c_master_i3c_device_transmit` or :cpp:func:`i3c_master_i3c_device_receive` or :cpp:func:`i3c_master_i3c_device_transmit_receive`. By default, it works in blocking mode. For non-blocking mode, please refer to :ref:`dma-support`. The same applies to other transfer functions.
|
||||
5. Finally clean up resources
|
||||
|
||||
Add and Drive I3C Devices via ENTDAA
|
||||
-------------------------------------
|
||||
|
||||
If you don't know which I3C devices are on the bus, or want the system to automatically discover and assign addresses, you can use the ENTDAA method:
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i3c/i3c_entdaa.svg
|
||||
:align: center
|
||||
:alt: I3C Automatic Dynamic Address Assignment
|
||||
|
||||
I3C Automatic Dynamic Address Assignment
|
||||
|
||||
.. code:: c
|
||||
|
||||
// 1. Create I3C bus (need to set entdaa_device_num)
|
||||
i3c_master_bus_config_t i3c_mst_config = {
|
||||
// ... other configurations ...
|
||||
.entdaa_device_num = 5, // Maximum number of devices that can be dynamically discovered by the driver
|
||||
};
|
||||
i3c_master_bus_handle_t bus_handle;
|
||||
ESP_ERROR_CHECK(i3c_new_master_bus(&i3c_mst_config, &bus_handle));
|
||||
|
||||
// 2. Scan I3C devices on the bus
|
||||
i3c_master_i3c_device_table_handle_t table_handle = NULL;
|
||||
ESP_ERROR_CHECK(i3c_master_scan_devices_by_entdaa(bus_handle, &table_handle));
|
||||
|
||||
// 3. Get the number of discovered devices
|
||||
size_t device_count = 0;
|
||||
ESP_ERROR_CHECK(i3c_master_get_device_count(table_handle, &device_count));
|
||||
printf("Found %zu I3C devices\n", device_count);
|
||||
|
||||
// 4. Iterate through all devices and get device information
|
||||
i3c_master_i3c_device_handle_t dev = NULL;
|
||||
for (size_t i = 0; i < device_count; i++) {
|
||||
i3c_master_i3c_device_handle_t dev_handle = NULL;
|
||||
ESP_ERROR_CHECK(i3c_master_get_device_handle(table_handle, i, &dev_handle));
|
||||
|
||||
// Get device information
|
||||
i3c_device_information_t info;
|
||||
ESP_ERROR_CHECK(i3c_master_i3c_device_get_info(dev_handle, &info));
|
||||
printf("Device %d: Dynamic Addr=0x%02X, BCR=0x%02X, DCR=0x%02X, PID=0x%016llX\n",
|
||||
i, info.dynamic_addr, info.bcr, info.dcr, info.pid);
|
||||
|
||||
if (info.pid == /* Device PID, obtained from device manual */) {
|
||||
dev = dev_handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Release device handle table, call when no longer needed
|
||||
ESP_ERROR_CHECK(i3c_master_free_device_handle_table(table_handle));
|
||||
|
||||
// 5. Perform data transfer through transmit or receive
|
||||
ESP_ERROR_CHECK(i3c_master_i3c_device_transmit(dev, data, sizeof(data), -1));
|
||||
ESP_ERROR_CHECK(i3c_master_i3c_device_receive(dev, data, sizeof(data), -1));
|
||||
|
||||
In this scenario:
|
||||
|
||||
1. When creating the bus, we need to set `entdaa_device_num`, which represents the expected number of devices to be discovered
|
||||
2. Use :cpp:func:`i3c_master_scan_devices_by_entdaa` to scan all I3C devices on the bus
|
||||
3. The system automatically assigns dynamic addresses to each device
|
||||
4. We can get the device count through :cpp:func:`i3c_master_get_device_count`
|
||||
5. Get each device's handle through :cpp:func:`i3c_master_get_device_handle`
|
||||
6. Use :cpp:func:`i3c_master_i3c_device_get_info` to get detailed device information (dynamic address, BCR, DCR, PID)
|
||||
7. Perform data transfers through :cpp:func:`i3c_master_i3c_device_transmit` or :cpp:func:`i3c_master_i3c_device_receive` based on the obtained device information
|
||||
|
||||
.. note::
|
||||
|
||||
:cpp:func:`i3c_master_scan_devices_by_entdaa` is thread-safe, and there will not be two threads addressing simultaneously. According to the protocol, when a slave is addressed and discovered by :cpp:func:`i3c_master_scan_devices_by_entdaa`, it no longer has the ability to respond to a second addressing. Therefore, there will be no address changes due to addressing in different threads. This interface supports adding new devices after initialization. To rescan, use the CCC mechanism to reset addresses on the I3C bus, or clear address information on the bus by power cycling.
|
||||
|
||||
Common Command Code (CCC) Transfer
|
||||
-----------------------------------
|
||||
|
||||
The I3C protocol uses Common Command Code (CCC) for bus management and device configuration. You can use the :cpp:func:`i3c_master_transfer_ccc` function to send CCC commands.
|
||||
|
||||
CCC transfers can be broadcast (sent to all devices) or direct (sent to a specific device):
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i3c/i3c_broadcast_ccc.svg
|
||||
:align: center
|
||||
:alt: I3C Broadcast Command
|
||||
|
||||
I3C Broadcast Command
|
||||
|
||||
.. figure:: ../../../_static/diagrams/i3c/i3c_direct_ccc.svg
|
||||
:align: center
|
||||
:alt: I3C Direct Command
|
||||
|
||||
I3C Direct Command
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Broadcast CCC command example: Send RSTDAA (Reset All Dynamic Addresses)
|
||||
i3c_master_ccc_transfer_config_t ccc_trans = {
|
||||
.ccc_command = I3C_CCC_RSTDAA,
|
||||
.direction = I3C_MASTER_TRANSFER_DIRECTION_WRITE,
|
||||
.device_address = 0, // Broadcast command, this field is ignored
|
||||
.data = NULL,
|
||||
.data_size = 0,
|
||||
};
|
||||
ESP_ERROR_CHECK(i3c_master_transfer_ccc(bus_handle, &ccc_trans));
|
||||
|
||||
// Direct CCC command example: Read device's GETPID (Get Device ID)
|
||||
uint8_t pid_data[6] = {0};
|
||||
ccc_trans = (i3c_master_ccc_transfer_config_t) {
|
||||
.ccc_command = I3C_CCC_GETPID,
|
||||
.direction = I3C_MASTER_TRANSFER_DIRECTION_READ,
|
||||
.device_address = 0x08, // Target device address, which is the dynamic address
|
||||
.data = pid_data,
|
||||
.data_size = sizeof(pid_data),
|
||||
};
|
||||
ESP_ERROR_CHECK(i3c_master_transfer_ccc(bus_handle, &ccc_trans));
|
||||
|
||||
.. note::
|
||||
|
||||
:cpp:func:`i3c_master_transfer_ccc` is always blocking and is not affected by DMA and asynchronous configuration. Users need to query the I3C protocol to know the specific format of CCC commands, and fill :cpp:member:`i3c_master_ccc_transfer_config_t::direction` as ``I3C_MASTER_TRANSFER_DIRECTION_READ`` or ``I3C_MASTER_TRANSFER_DIRECTION_WRITE`` and fill :cpp:member:`i3c_master_ccc_transfer_config_t::data` and :cpp:member:`i3c_master_ccc_transfer_config_t::data_size` according to the format of sending commands or obtaining values.
|
||||
|
||||
Resource Cleanup
|
||||
----------------
|
||||
|
||||
When the previously installed I3C bus or device is no longer needed, call :cpp:func:`i3c_master_bus_rm_i3c_device` or :cpp:func:`i3c_master_bus_rm_i2c_device` to remove the device, then call :cpp:func:`i3c_del_master_bus` to reclaim resources and release the underlying hardware.
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_ERROR_CHECK(i3c_master_bus_rm_i3c_device(i3c_dev_handle));
|
||||
ESP_ERROR_CHECK(i3c_del_master_bus(bus_handle));
|
||||
|
||||
Advanced Features
|
||||
=================
|
||||
|
||||
Clock Source and Timing Parameter Fine-tuning
|
||||
---------------------------------------------
|
||||
|
||||
Clock Source Selection
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The clock source of the I3C bus can be selected through :cpp:member:`i3c_master_bus_config_t::clock_source`.
|
||||
|
||||
.. code:: c
|
||||
|
||||
i3c_master_bus_config_t i3c_mst_config = {
|
||||
// ... other configurations ...
|
||||
.clock_source = I3C_MASTER_CLK_SRC_DEFAULT, // Default clock source
|
||||
};
|
||||
|
||||
.. note::
|
||||
|
||||
When the I3C push-pull output frequency is greater than 3 MHz, please set the clock source to :cpp:enumerator:`i3c_master_clock_source_t::I3C_MASTER_CLK_SRC_PLL_F120M` or :cpp:enumerator:`i3c_master_clock_source_t::I3C_MASTER_CLK_SRC_PLL_F160M`.
|
||||
|
||||
The I3C driver provides rich timing parameter configuration options. You can adjust these parameters according to the actual hardware situation to optimize performance or solve timing issues.
|
||||
|
||||
Duty Cycle and Hold Time
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some I3C slave devices have strict timing requirements for their acknowledgment mechanism (ACK/NACK), such as requirements for SCL waveform duty cycle and SDA hold time. These parameters can be configured through the following configuration items.
|
||||
|
||||
.. code:: c
|
||||
|
||||
i3c_master_bus_config_t i3c_mst_config = {
|
||||
// ... other configurations ...
|
||||
.i3c_scl_pp_duty_cycle = 0.5, // Push-Pull mode duty cycle, usually 0.5 (default value 0 also means 0.5)
|
||||
.i3c_scl_od_duty_cycle = 0.5, // Open-Drain mode duty cycle, usually 0.5 (default value 0 also means 0.5)
|
||||
.i3c_sda_od_hold_time_ns = 25, // Open-Drain mode hold time, default 25 ns
|
||||
.i3c_sda_pp_hold_time_ns = 0, // Push-Pull mode hold time, default 0 ns
|
||||
};
|
||||
|
||||
The specific values of these parameters need to be determined according to the device manual and actual testing.
|
||||
|
||||
Event Callbacks
|
||||
---------------
|
||||
|
||||
The I3C driver supports an event callback mechanism that can notify the application when a transfer is complete or when an IBI interrupt is received.
|
||||
|
||||
When the I3C controller generates events such as send or receive completion, it notifies the CPU through interrupts. If you need to call a function when a specific event occurs, you can register event callbacks with the I3C driver's interrupt service routine (ISR) by calling :cpp:func:`i3c_master_i3c_device_register_event_callbacks` and :cpp:func:`i3c_master_i2c_device_register_event_callbacks` for I3C and I2C slaves respectively. Since these callback functions are called in the ISR, they should not involve blocking operations. You can check the suffix of the called API to ensure that only FreeRTOS APIs with the ISR suffix are called in the function. The callback function has a boolean return value indicating whether the callback unblocked a higher priority task.
|
||||
|
||||
For event callbacks of I2C slaves, please refer to i2c_master_i2c_event_callbacks_t.
|
||||
|
||||
* :cpp:member:`i3c_master_i2c_event_callbacks_t::on_trans_done` can be set to a callback function for the master "transfer done" event. The function prototype is declared in :cpp:type:`i3c_master_i2c_callback_t`. Note that this callback function can only be used when the I2C slave device has DMA enabled and uses asynchronous transfer. For details, please refer to :ref:`dma-support`.
|
||||
|
||||
For event callbacks of I3C slaves, please refer to i3c_master_i3c_event_callbacks_t.
|
||||
|
||||
* :cpp:member:`i3c_master_i3c_event_callbacks_t::on_trans_done` can be set to a callback function for the master "transfer done" event. The function prototype is declared in :cpp:type:`i3c_master_i3c_callback_t`. Note that this callback function can only be used when the I3C slave device has DMA enabled and uses asynchronous transfer. For details, please refer to :ref:`dma-support`.
|
||||
|
||||
* :cpp:member:`i3c_master_i3c_event_callbacks_t::on_ibi` can be set to a callback function for IBI events. The function prototype is declared in :cpp:type:`i3c_master_ibi_callback_t`. For detailed information about IBI events, please refer to :ref:`in-band-interrupt`
|
||||
|
||||
.. note::
|
||||
|
||||
Callback functions are executed in the ISR context, therefore:
|
||||
|
||||
- Cannot perform blocking operations
|
||||
- Can only call FreeRTOS APIs with the ISR suffix
|
||||
- If ``CONFIG_I3C_MASTER_ISR_CACHE_SAFE`` is enabled, callback functions must be placed in IRAM
|
||||
|
||||
.. _in-band-interrupt:
|
||||
|
||||
In-Band Interrupt (IBI)
|
||||
-----------------------
|
||||
|
||||
The I3C protocol supports In-Band Interrupt (IBI), allowing slave devices to send interrupt requests through the I3C bus without additional interrupt lines.
|
||||
|
||||
Configure IBI
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The I3C bus configuration structure :cpp:type:`i3c_master_bus_config_t` contains IBI-related global configuration items:
|
||||
|
||||
- :cpp:member:`i3c_master_bus_config_t::ibi_rstart_trans_en` enables restart transaction on IBI. The I3C controller continues to execute the command that was interrupted by IBI after IBI completion. If IBI occurs during bus idle and the I3C transfer task is not empty, the I3C controller will continue to execute that task. If IBI conflicts with I3C controller transfer and wins arbitration, the interrupted task will continue to execute after IBI processing is complete.
|
||||
- :cpp:member:`i3c_master_bus_config_t::ibi_silent_sir_rejected` when written as 0, does not notify the application layer when a slave interrupt request (SIR) is rejected. When written as 1, the IBI status is still written to the IBI FIFO and the application layer is notified.
|
||||
- :cpp:member:`i3c_master_bus_config_t::ibi_no_auto_disable` if set, does not automatically disable IBI after the controller NACKs an In-Band interrupt, keeping in-band interrupt enabled.
|
||||
|
||||
You can use the :cpp:func:`i3c_master_i3c_device_ibi_config` function to configure IBI for a specific device:
|
||||
|
||||
.. code:: c
|
||||
|
||||
i3c_ibi_config_t ibi_cfg = {
|
||||
.enable_ibi = true,
|
||||
.enable_ibi_payload = true, // Allow IBI to carry payload
|
||||
};
|
||||
ESP_ERROR_CHECK(i3c_master_i3c_device_ibi_config(dev_handle, &ibi_cfg));
|
||||
|
||||
Handle IBI Events
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Detailed information about IBI events will be provided from the callback through :cpp:type:`i3c_master_ibi_info_t`:
|
||||
|
||||
:cpp:member:`i3c_master_ibi_info_t::ibi_id` is the raw identifier of the IBI, usually encoded from the slave device's dynamic address; it is the raw value, i.e., dynamic address + read/write bit. :cpp:member:`i3c_master_ibi_info_t::ibi_sts` is the IBI status field reported by the controller. :cpp:member:`i3c_master_ibi_info_t::data_length` is the number of valid bytes in the payload buffer :cpp:member:`i3c_master_ibi_info_t::ibi_data`. :cpp:member:`i3c_master_ibi_info_t::ibi_data` is the optional payload bytes associated with the IBI. Only the first :cpp:member:`i3c_master_ibi_info_t::data_length` bytes are valid.
|
||||
|
||||
.. code:: c
|
||||
|
||||
static bool i3c_ibi_callback(i3c_master_i3c_device_handle_t dev_handle, const i3c_master_ibi_info_t *ibi_info, void *user_ctx)
|
||||
{
|
||||
// Can copy IBI event data to user-provided context and do further processing in task
|
||||
// i3c_master_ibi_info_t is a user-defined structure, here including ibi_id and ibi_data_len, members can be added or removed according to actual needs
|
||||
i3c_master_ibi_info_t evt = {
|
||||
.ibi_id = ibi_info->ibi_id,
|
||||
.ibi_data_len = ibi_info->data_length,
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
i3c_master_i3c_event_callbacks_t cbs = {
|
||||
.on_ibi = i3c_ibi_callback,
|
||||
};
|
||||
ESP_ERROR_CHECK(i3c_master_i3c_device_register_event_callbacks(dev_handle, &cbs, NULL));
|
||||
|
||||
.. _dma-support:
|
||||
|
||||
DMA and Asynchronous Transfer
|
||||
-----------------------------
|
||||
|
||||
The I3C driver supports DMA for large-capacity data transfers and asynchronous transfers, which can improve transfer efficiency and reduce CPU usage.
|
||||
|
||||
Enable DMA
|
||||
^^^^^^^^^^
|
||||
|
||||
You can configure DMA for the bus through the :cpp:func:`i3c_master_bus_decorate_dma` function:
|
||||
|
||||
.. code:: c
|
||||
|
||||
i3c_master_dma_config_t dma_config = {
|
||||
.max_transfer_size = 4096, // Maximum transfer size (bytes)
|
||||
.dma_burst_size = 16, // DMA burst size (bytes)
|
||||
};
|
||||
ESP_ERROR_CHECK(i3c_master_bus_decorate_dma(bus_handle, &dma_config));
|
||||
|
||||
Enable Asynchronous Transfer
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When DMA is enabled, you can further enable asynchronous transfer to improve performance:
|
||||
|
||||
.. code:: c
|
||||
|
||||
i3c_master_bus_config_t i3c_mst_config = {
|
||||
// ... other configurations ...
|
||||
.trans_queue_depth = 5, // Set the depth of internal transfer queue
|
||||
.flags = {
|
||||
.enable_async_trans = 1, // Enable asynchronous transfer
|
||||
}
|
||||
};
|
||||
|
||||
At this point, the I3C master transfer functions will return immediately after being called. When each transfer is complete, the :cpp:member:`i3c_master_i3c_event_callbacks_t::on_trans_done` callback function will be called to indicate the completion of a transfer. If you need to wait for the transfer to complete, you can call the :cpp:func:`i3c_master_bus_wait_all_done` function to wait for all transfers to complete:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Start multiple asynchronous transfers
|
||||
ESP_ERROR_CHECK(i3c_master_i3c_device_transmit(dev_handle1, data1, size1, -1));
|
||||
ESP_ERROR_CHECK(i3c_master_i3c_device_transmit(dev_handle2, data2, size2, -1));
|
||||
ESP_ERROR_CHECK(i3c_master_i3c_device_transmit(dev_handle3, data3, size3, -1));
|
||||
|
||||
// Wait for all transfers to complete
|
||||
ESP_ERROR_CHECK(i3c_master_bus_wait_all_done(bus_handle, -1));
|
||||
|
||||
Thread Safety
|
||||
-------------
|
||||
|
||||
The following functions of the I3C driver are thread-safe and can be called from different RTOS tasks without additional lock protection:
|
||||
|
||||
Factory functions:
|
||||
|
||||
- :cpp:func:`i3c_new_master_bus`
|
||||
- :cpp:func:`i3c_del_master_bus`
|
||||
|
||||
I3C master operation functions (thread safety guaranteed through bus operation signals):
|
||||
|
||||
- :cpp:func:`i3c_master_bus_add_i3c_static_device`
|
||||
- :cpp:func:`i3c_master_bus_rm_i3c_device`
|
||||
- :cpp:func:`i3c_master_i3c_device_transmit`
|
||||
- :cpp:func:`i3c_master_i3c_device_receive`
|
||||
- :cpp:func:`i3c_master_i3c_device_transmit_receive`
|
||||
- :cpp:func:`i3c_master_i2c_device_transmit`
|
||||
- :cpp:func:`i3c_master_i2c_device_receive`
|
||||
- :cpp:func:`i3c_master_i2c_device_transmit_receive`
|
||||
- :cpp:func:`i3c_master_transfer_ccc`
|
||||
|
||||
Cache Safety
|
||||
------------
|
||||
|
||||
By default, when the cache is disabled (e.g., during SPI Flash write), I3C interrupts will be delayed, and event callback functions will not be able to execute on time, which will affect the system response of real-time applications.
|
||||
|
||||
This can be avoided by enabling the Kconfig option ``CONFIG_I3C_MASTER_ISR_CACHE_SAFE``. After enabling:
|
||||
|
||||
1. Interrupts can continue to run even when the cache is disabled
|
||||
2. All functions used by the ISR are placed in IRAM
|
||||
3. Driver objects are placed in DRAM (to prevent them from being accidentally mapped to PSRAM)
|
||||
|
||||
Enabling this option ensures interrupt operation when the cache is disabled, but will consume more IRAM.
|
||||
|
||||
.. note::
|
||||
|
||||
After enabling this option, when the cache is disabled, ISR callback functions will continue to run. Therefore, you must ensure that the callback functions and their context are also IRAM-safe. At the same time, data transfer buffers must also be placed in DRAM.
|
||||
|
||||
About Low Power Consumption
|
||||
----------------------------
|
||||
|
||||
When power management :ref:`CONFIG_PM_ENABLE` is enabled, the system may adjust or disable clock sources before entering sleep mode, which can cause I3C transfer errors.
|
||||
|
||||
To prevent this from happening, the I3C driver internally creates a power management lock. After calling a transfer function, this lock will be activated to ensure the system does not enter sleep mode, thus maintaining the correct operation of the timer. After the transfer is complete, the driver automatically releases the lock, allowing the system to enter sleep mode.
|
||||
|
||||
Kconfig Options
|
||||
---------------
|
||||
|
||||
The following Kconfig options can be used to configure the I3C driver:
|
||||
|
||||
- :ref:`CONFIG_I3C_MASTER_ISR_CACHE_SAFE`: Ensure I3C interrupts work properly when cache is disabled (e.g., during SPI Flash write)
|
||||
- :ref:`CONFIG_I3C_MASTER_ISR_HANDLER_IN_IRAM`: Place I3C master ISR handler in IRAM to improve performance and reduce cache misses
|
||||
- :ref:`CONFIG_I3C_MASTER_ENABLE_DEBUG_LOG`: Enable I3C debug logging
|
||||
|
||||
About Resource Consumption
|
||||
---------------------------
|
||||
|
||||
You can use the :doc:`/api-guides/tools/idf-size` tool to view the code and data consumption of the I3C driver. The following are the test prerequisites (using ESP32-P4 as an example):
|
||||
|
||||
- Compiler optimization level is set to ``-Os`` to ensure minimal code size.
|
||||
- Default log level is set to ``ESP_LOG_INFO`` to balance debug information and performance.
|
||||
- The following driver optimization options are disabled:
|
||||
- :ref:`CONFIG_I3C_MASTER_ISR_HANDLER_IN_IRAM` - ISR handler is not placed in IRAM.
|
||||
- :ref:`CONFIG_I3C_MASTER_ISR_CACHE_SAFE` - Cache safety option is not enabled.
|
||||
|
||||
**Note: The following data is not precise and is for reference only. Data may vary on different chip models.**
|
||||
|
||||
+------------------+------------+-------+------+-------+-------+------------+-------+------------+---------+
|
||||
| Component Layer | Total Size | DIRAM | .bss | .data | .text | Flash Code | .text | Flash Data | .rodata |
|
||||
+==================+============+=======+======+=======+=======+============+=======+============+=========+
|
||||
| hal | 30 | 0 | 0 | 0 | 0 | 30 | 18 | 0 | 12 |
|
||||
+------------------+------------+-------+------+-------+-------+------------+-------+------------+---------+
|
||||
| driver | 9249 | 12 | 12 | 0 | 0 | 9237 | 8666 | 571 | 571 |
|
||||
+------------------+------------+-------+------+-------+-------+------------+-------+------------+---------+
|
||||
|
||||
Application Examples
|
||||
====================
|
||||
|
||||
- :example:`peripherals/i3c/i3c_i2c_basic` demonstrates the basic steps of initializing the I3C master driver and reading data from the ICM42688 sensor using the I2C interface.
|
||||
|
||||
- :example:`peripherals/i3c/i3c_lsm6dscx` demonstrates how to read and write data from a connected LSM6DSOX sensor using I3C master mode, and supports in-band interrupt (IBI) event handling.
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
I3C Driver API
|
||||
--------------
|
||||
|
||||
.. include-build-file:: inc/i3c_master.inc
|
||||
|
||||
I3C Driver I2C Slave API
|
||||
------------------------
|
||||
|
||||
.. include-build-file:: inc/i3c_master_i2c.inc
|
||||
|
||||
I3C Driver Types
|
||||
----------------
|
||||
|
||||
.. include-build-file:: inc/components/esp_hal_i3c/include/hal/i3c_master_types.inc
|
||||
|
||||
I3C HAL Types
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/components/esp_driver_i3c/include/driver/i3c_master_types.inc
|
||||
@@ -0,0 +1,58 @@
|
||||
Peripherals API
|
||||
****************
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
:SOC_ADC_SUPPORTED: adc/index
|
||||
:SOC_ANA_CMPR_SUPPORTED: ana_cmpr
|
||||
:SOC_GDMA_SUPPORT_CRC: async_crc
|
||||
:SOC_DMA2D_SUPPORTED: async_color_convert
|
||||
:SOC_ASYNC_MEMCPY_SUPPORTED: async_memcpy
|
||||
:SOC_BITSCRAMBLER_SUPPORTED: bitscrambler
|
||||
:SOC_MIPI_CSI_SUPPORTED: camera_driver
|
||||
:SOC_CLK_TREE_SUPPORTED: clk_tree
|
||||
:SOC_CORDIC_SUPPORTED: cordic
|
||||
:SOC_DAC_SUPPORTED: dac
|
||||
:SOC_ECDSA_SUPPORTED: ecdsa
|
||||
:SOC_ETM_SUPPORTED: etm
|
||||
gpio
|
||||
:SOC_GPTIMER_SUPPORTED: gptimer
|
||||
:SOC_DEDICATED_GPIO_SUPPORTED: dedic_gpio
|
||||
:SOC_HMAC_SUPPORTED: hmac
|
||||
:SOC_DIG_SIGN_SUPPORTED: ds
|
||||
:SOC_I2C_SUPPORTED: i2c
|
||||
:SOC_I2S_SUPPORTED: i2s
|
||||
:SOC_I3C_MASTER_SUPPORTED: i3c_master
|
||||
:SOC_ISP_SUPPORTED: isp
|
||||
:SOC_JPEG_CODEC_SUPPORTED: jpeg
|
||||
:SOC_KEY_MANAGER_SUPPORTED: key_manager
|
||||
lcd/index
|
||||
:SOC_GP_LDO_SUPPORTED: ldo_regulator
|
||||
:SOC_LEDC_SUPPORTED: ledc
|
||||
:SOC_MCPWM_SUPPORTED: mcpwm
|
||||
:SOC_PARLIO_SUPPORTED: parlio/index
|
||||
:SOC_PCNT_SUPPORTED: pcnt
|
||||
:SOC_PPA_SUPPORTED: ppa
|
||||
:SOC_RMT_SUPPORTED: rmt
|
||||
:SOC_SDMMC_HOST_SUPPORTED or SOC_SDIO_SLAVE_SUPPORTED: sd_pullup_requirements
|
||||
:SOC_SDMMC_HOST_SUPPORTED: sdmmc_host
|
||||
:SOC_GPSPI_SUPPORTED: sdspi_host
|
||||
:SOC_SDIO_SLAVE_SUPPORTED: sdio_slave
|
||||
:SOC_SDM_SUPPORTED: sdm
|
||||
:SOC_SPI_FLASH_SUPPORTED: spi_flash/index
|
||||
:SOC_GPSPI_SUPPORTED: spi_master
|
||||
:SOC_GPSPI_SUPPORTED: spi_slave
|
||||
:SOC_SPI_SUPPORT_SLAVE_HD_VER2: spi_slave_hd
|
||||
:SOC_LP_I2S_SUPPORTED: lp_i2s
|
||||
:SOC_LP_VAD_SUPPORTED: vad
|
||||
:SOC_TEMP_SENSOR_SUPPORTED: temp_sensor
|
||||
:SOC_TOUCH_SENSOR_SUPPORTED: cap_touch_sens
|
||||
:SOC_TWAI_SUPPORTED: twai
|
||||
:SOC_UART_SUPPORTED: uart
|
||||
:SOC_USB_OTG_SUPPORTED: USB Device Stack <https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_device.html>
|
||||
:SOC_USB_OTG_SUPPORTED: USB Host <https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_host.html>
|
||||
|
||||
Code examples for this API section are provided in the :example:`peripherals` directory of ESP-IDF examples.
|
||||
@@ -0,0 +1,982 @@
|
||||
Image Signal Processor (ISP)
|
||||
============================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
{IDF_TARGET_NAME} includes an Image Signal Processor (ISP), which is a feature pipeline that consists of many image processing algorithms. ISP receives image data from the DVP camera or MIPI-CSI camera, or system memory, and writes the processed image data to the system memory through DMA. The ISP is designed to work with other camera controller modules and can not operate independently.
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
|
||||
.. list::
|
||||
|
||||
- MIPI-CSI: Camera serial interface, a high-speed serial interface for cameras compliant with MIPI specifications
|
||||
- DVP: Digital video parallel interface, generally composed of vsync, hsync, de, and data signals
|
||||
- RAW: Unprocessed data directly output from an image sensor, typically divided into R, Gr, Gb, and B four channels classified into RAW8, RAW10, RAW12, etc., based on bit width
|
||||
- RGB: Colored image format composed of red, green, and blue colors classified into RGB888, RGB565, etc., based on the bit width of each color
|
||||
- YUV: Colored image format composed of luminance and chrominance classified into YUV444, YUV422, YUV420, etc., based on the data arrangement
|
||||
- AF: Auto focus
|
||||
- AWB: Auto white balance
|
||||
- AE: Auto exposure
|
||||
- HIST: Histogram
|
||||
- BF: Bayer noise filter
|
||||
- BLC: Black Level Correction
|
||||
- LSC: Lens Shading Correction
|
||||
- CCM: Color correction matrix
|
||||
|
||||
ISP Pipeline
|
||||
------------
|
||||
|
||||
.. blockdiag::
|
||||
:scale: 100%
|
||||
:caption: ISP Pipeline
|
||||
:align: center
|
||||
|
||||
blockdiag isp_pipeline {
|
||||
orientation = portrait;
|
||||
node_height = 30;
|
||||
node_width = 120;
|
||||
span_width = 100;
|
||||
default_fontsize = 16;
|
||||
|
||||
isp_header [label = "ISP Header"];
|
||||
isp_tail [label = "ISP Tail"];
|
||||
isp_chs [label = "Contrast &\n Hue & Saturation", width = 150, height = 70];
|
||||
isp_yuv [label = "YUV Limit\n YUB2RGB", width = 120, height = 70];
|
||||
|
||||
isp_header -> BLC -> BF -> LSC -> Demosaic -> WBG -> CCM -> Gamma -> RGB2YUV -> SHARP -> isp_chs -> isp_yuv -> CROP -> isp_tail;
|
||||
|
||||
LSC -> HIST
|
||||
Demosaic -> WBG
|
||||
Demosaic -> AWB
|
||||
Demosaic -> AE
|
||||
Demosaic -> HIST
|
||||
WBG -> AWB
|
||||
Gamma -> AE
|
||||
RGB2YUV -> HIST
|
||||
RGB2YUV -> AF
|
||||
}
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
The ISP driver offers following services:
|
||||
|
||||
- :ref:`isp-resource-allocation` - covers how to allocate ISP resources with properly set of configurations. It also covers how to recycle the resources when they finished working.
|
||||
- :ref:`isp-enable-disable` - covers how to enable and disable an ISP processor.
|
||||
- :ref:`isp-af-statistics` - covers how to get AF statistics one-shot or continuously.
|
||||
- :ref:`isp-awb-statistics` - covers how to get AWB white patches statistics one-shot or continuously.
|
||||
- :ref:`isp-ae-statistics` - covers how to get AE statistics one-shot or continuously.
|
||||
- :ref:`isp-hist-statistics` - covers how to get histogram statistics one-shot or continuously.
|
||||
- :ref:`isp-bf` - covers how to enable and configure BF function.
|
||||
- :ref:`isp-blc` - covers how to enable and configure BLC function.
|
||||
- :ref:`isp-lsc` - covers how to enable and configure LSC function.
|
||||
- :ref:`isp-ccm-config` - covers how to configure the CCM.
|
||||
- :ref:`isp-demosaic` - covers how to configure the Demosaic function.
|
||||
- :ref:`isp-gamma-correction` - covers how to enable and configure gamma correction.
|
||||
- :ref:`isp-sharpen` - covers how to configure the sharpening function.
|
||||
- :ref:`isp-crop` - covers how to enable and configure image cropping function.
|
||||
- :ref:`isp-callback` - covers how to hook user specific code to ISP driver event callback function.
|
||||
- :ref:`isp-thread-safety` - lists which APIs are guaranteed to be thread safe by the driver.
|
||||
- :ref:`isp-kconfig-options` - lists the supported Kconfig options that can bring different effects to the driver.
|
||||
- :ref:`isp-iram-safe` - describes tips on how to make the ISP interrupt and control functions work better along with a disabled cache.
|
||||
|
||||
.. _isp-resource-allocation:
|
||||
|
||||
Resource Allocation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Install ISP Driver
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ISP driver requires the configuration that specified by :cpp:type:`esp_isp_processor_cfg_t`.
|
||||
|
||||
If the configurations in :cpp:type:`esp_isp_processor_cfg_t` is specified, users can call :cpp:func:`esp_isp_new_processor` to allocate and initialize an ISP processor. This function will return an ISP processor handle if it runs correctly. You can take following code as reference:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_isp_processor_cfg_t isp_config = {
|
||||
.clk_src = ISP_CLK_SRC_DEFAULT,
|
||||
...
|
||||
};
|
||||
|
||||
isp_proc_handle_t isp_proc = NULL;
|
||||
ESP_ERROR_CHECK(esp_isp_new_processor(&isp_config, &isp_proc));
|
||||
|
||||
You can use the created handle to enable/disable the ISP driver and do other ISP module installation.
|
||||
|
||||
.. note::
|
||||
|
||||
ISP peripheral is necessary if MIPI CSI or ISP_DVP is used as camera controller. This means that even if ISP functions are not needed, you still need to install the ISP driver by calling :cpp:func:`esp_isp_new_processor`.
|
||||
|
||||
If ISP functions are not needed, ISP driver supports bypassing ISP pipelines and enabling only the necessary functions. This can be achieved by setting :cpp:member:`esp_isp_processor_cfg_t::bypass_isp`.
|
||||
|
||||
Install ISP Auto Focus (AF) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ISP auto focus (AF) driver requires the configuration that specified by :cpp:type:`esp_isp_af_config_t`.
|
||||
|
||||
If the configurations in :cpp:type:`esp_isp_af_config_t` is specified, users can call :cpp:func:`esp_isp_new_af_controller` to allocate and initialize an ISP AF controller. This function will return an ISP AF controller handle if it runs correctly. You can take following code as reference:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_isp_af_config_t af_config = {
|
||||
.edge_thresh = 128,
|
||||
};
|
||||
isp_af_ctlr_t af_ctrlr = NULL;
|
||||
ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr));
|
||||
|
||||
You can use the created handle to enable/disable the ISP AF driver and install ISP AF environment detector module.
|
||||
|
||||
Install ISP Auto White Balance (AWB) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ISP auto white balance (AWB) driver requires the configuration specified by :cpp:type:`esp_isp_awb_config_t`.
|
||||
|
||||
If an :cpp:type:`esp_isp_awb_config_t` configuration is specified, you can call :cpp:func:`esp_isp_new_awb_controller` to allocate and initialize an ISP AWB controller. This function will return an ISP AWB controller handle on success. You can take following code as reference:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
isp_awb_ctlr_t awb_ctlr = NULL;
|
||||
uint32_t image_width = 800;
|
||||
uint32_t image_height = 600;
|
||||
/* The AWB configuration, please refer to the API comment for how to tune these parameters */
|
||||
esp_isp_awb_config_t awb_config = {
|
||||
.sample_point = ISP_AWB_SAMPLE_POINT_1,
|
||||
...
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_new_awb_controller(isp_proc, &awb_config, &awb_ctlr));
|
||||
|
||||
The AWB handle created in this step is required by other AWB APIs and AWB scheme.
|
||||
|
||||
Install ISP Auto Exposure (AE) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ISP auto exposure (AE) driver requires the configuration that specified by :cpp:type:`esp_isp_ae_config_t`.
|
||||
|
||||
If the configurations in :cpp:type:`esp_isp_ae_config_t` is specified, call :cpp:func:`esp_isp_new_ae_controller` to allocate and initialize an ISP AE controller. This function will return an ISP AE controller handle if it runs correctly. You can take following code as reference.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_isp_ae_config_t ae_config = {
|
||||
.sample_point = ISP_AE_SAMPLE_POINT_0,
|
||||
...
|
||||
};
|
||||
isp_ae_ctlr_t ae_ctlr = NULL;
|
||||
ESP_ERROR_CHECK(esp_isp_new_ae_controller(isp_proc, &ae_config, &ae_ctlr));
|
||||
|
||||
You can use the created handle to enable/disable the ISP AE driver and do ISP AE environment detector setup.
|
||||
|
||||
Install ISP Histogram (HIST) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ISP histogram (HIST) driver requires the configuration that specified by :cpp:type:`esp_isp_hist_config_t`.
|
||||
|
||||
If the configurations in :cpp:type:`esp_isp_hist_config_t` is specified, users can call :cpp:func:`esp_isp_new_hist_controller` to allocate and initialize an ISP Histogram controller. This function will return an ISP HIST controller handle if it runs correctly. You can take following code as reference.
|
||||
|
||||
.. list::
|
||||
|
||||
- The sum of all subwindow weights' decimal values should be 256; otherwise, the statistics will be small. The integer value should be 0.
|
||||
- The sum of all RGB coefficients' decimal values should be 256; otherwise, the statistics will be small. The integer value should be 0.
|
||||
- The segment_threshold must be 0–255 and in order.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_hist_config_t hist_cfg = {
|
||||
.segment_threshold = {16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240},
|
||||
.hist_mode = ISP_HIST_SAMPLING_RGB,
|
||||
.rgb_coefficient.coeff_r = {
|
||||
.integer = 0,
|
||||
.decimal = 86,
|
||||
},
|
||||
.rgb_coefficient.coeff_g = {
|
||||
.integer = 0,
|
||||
.decimal = 85,
|
||||
},
|
||||
.rgb_coefficient.coeff_b = {
|
||||
.integer = 0,
|
||||
.decimal = 85,
|
||||
},
|
||||
.window_weight = {
|
||||
{{16, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}},
|
||||
{{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}},
|
||||
{{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}},
|
||||
{{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}},
|
||||
{{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}},
|
||||
},
|
||||
};
|
||||
isp_hist_ctlr_t hist_ctlr_ctlr = NULL;
|
||||
ESP_ERROR_CHECK(esp_isp_new_hist_controller(isp_proc, &hist_config, &hist_ctlr));
|
||||
|
||||
You can use the created handle to enable/disable the ISP HIST driver setup.
|
||||
|
||||
Uninstall ISP Drivers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If previously installed ISP drivers are no longer needed, it's recommended to recycle the resource by following APIs to release the underlying hardware:
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:func:`esp_isp_del_processor`, for ISP processor.
|
||||
- :cpp:func:`esp_isp_del_af_controller`, for ISP AF controller.
|
||||
- :cpp:func:`esp_isp_del_awb_controller`, for ISP AWB controller.
|
||||
- :cpp:func:`esp_isp_del_ae_controller`, for ISP AE controller.
|
||||
- :cpp:func:`esp_isp_del_hist_controller`, for ISP Histogram controller.
|
||||
|
||||
.. _isp-enable-disable:
|
||||
|
||||
Enable and Disable ISP
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
ISP
|
||||
~~~
|
||||
|
||||
Before doing ISP pipeline, you need to enable the ISP processor first, by calling :cpp:func:`esp_isp_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
ISP AF Controller
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Before doing ISP AF, you need to enable the ISP AF controller first, by calling :cpp:func:`esp_isp_af_controller_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_controller_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
.. _isp-af-statistics:
|
||||
|
||||
AF One-shot and Continuous Statistics
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_controller_get_oneshot_statistics` to get one-shot AF statistics result. You can take following code as reference.
|
||||
|
||||
Aside from the above one-shot API, the ISP AF driver also provides a way to start AF statistics continuously. Calling :cpp:func:`esp_isp_af_controller_start_continuous_statistics` to start the continuous statistics and :cpp:func:`esp_isp_af_controller_stop_continuous_statistics` to stop it.
|
||||
|
||||
Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_statistics_done` or :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_change` callbacks to get the statistics result. See how to register in :ref:`isp-callback`.
|
||||
|
||||
.. note::
|
||||
|
||||
When you use the continuous statistics, AF Environment Detector will be invalid.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_af_config_t af_config = {
|
||||
.edge_thresh = 128,
|
||||
};
|
||||
isp_af_ctlr_t af_ctrlr = NULL;
|
||||
ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr));
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_enable(af_ctrlr));
|
||||
isp_af_result_t result = {};
|
||||
/* Trigger the AF statistics and get its result for one time with timeout value 2000 ms */
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_get_oneshot_statistics(af_ctrlr, 2000, &result));
|
||||
|
||||
/* Start continuous AF statistics */
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_start_continuous_statistics(af_ctrlr));
|
||||
// You can do other stuffs here, the statistics result can be obtained in the callback
|
||||
// ......
|
||||
// vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
/* Stop continuous AF statistics */
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_stop_continuous_statistics(af_ctrlr));
|
||||
|
||||
/* Disable the AF controller */
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_disable(af_ctrlr));
|
||||
/* Delete the AF controller and free the resources */
|
||||
ESP_ERROR_CHECK(esp_isp_del_af_controller(af_ctrlr));
|
||||
|
||||
Set AF Environment Detector
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_controller_set_env_detector` to set an ISP AF environment detector. You can take following code as reference:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_isp_af_env_config_t env_config = {
|
||||
.interval = 10,
|
||||
};
|
||||
isp_af_ctlr_t af_ctrlr = NULL;
|
||||
ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr));
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_set_env_detector(af_ctrlr, &env_config));
|
||||
|
||||
Set AF Environment Detector Threshold
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_controller_set_env_detector_threshold` to set the threshold of an ISP AF environment detector.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int definition_thresh = 0;
|
||||
int luminance_thresh = 0;
|
||||
ESP_ERROR_CHECK(esp_isp_af_env_detector_set_threshold(env_detector, definition_thresh, luminance_thresh));
|
||||
|
||||
ISP AWB Controller
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Before doing ISP AWB, you need to enable the ISP AWB controller first, by calling :cpp:func:`esp_isp_awb_controller_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_awb_controller_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
.. _isp-awb-statistics:
|
||||
|
||||
AWB One-shot and Continuous Statistics
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_awb_controller_get_oneshot_statistics` to get oneshot AWB statistics result of white patches. You can take following code as reference.
|
||||
|
||||
Aside from the above one-shot API, the ISP AWB driver also provides a way to start AWB statistics continuously. Calling :cpp:func:`esp_isp_awb_controller_start_continuous_statistics` starts the continuous statistics and :cpp:func:`esp_isp_awb_controller_stop_continuous_statistics` stops it.
|
||||
|
||||
Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_awb_cbs_t::on_statistics_done` callback to get the statistics result. See how to register it in :ref:`isp-callback`.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
bool example_isp_awb_on_statistics_done_cb(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_evt_data_t *edata, void *user_data);
|
||||
// ...
|
||||
isp_awb_ctlr_t awb_ctlr = NULL;
|
||||
uint32_t image_width = 800;
|
||||
uint32_t image_height = 600;
|
||||
/* The AWB configuration, please refer to the API comment for how to tune these parameters */
|
||||
esp_isp_awb_config_t awb_config = {
|
||||
.sample_point = ISP_AWB_SAMPLE_POINT_1,
|
||||
...
|
||||
};
|
||||
isp_awb_stat_result_t stat_res = {};
|
||||
/* Create the AWB controller */
|
||||
ESP_ERROR_CHECK(esp_isp_new_awb_controller(isp_proc, &awb_config, &awb_ctlr));
|
||||
/* Register the AWB callback */
|
||||
esp_isp_awb_cbs_t awb_cb = {
|
||||
.on_statistics_done = example_isp_awb_on_statistics_done_cb,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_awb_register_event_callbacks(awb_ctlr, &awb_cb, NULL));
|
||||
/* Enable the AWB controller */
|
||||
ESP_ERROR_CHECK(esp_isp_awb_controller_enable(awb_ctlr));
|
||||
|
||||
/* Get one-shot AWB statistics result */
|
||||
ESP_ERROR_CHECK(esp_isp_awb_controller_get_oneshot_statistics(awb_ctlr, -1, &stat_res));
|
||||
|
||||
/* Start continuous AWB statistics, note that continuous statistics requires `on_statistics_done` callback */
|
||||
ESP_ERROR_CHECK(esp_isp_awb_controller_start_continuous_statistics(awb_ctlr));
|
||||
// You can do other stuffs here, the statistics result can be obtained in the callback
|
||||
// ......
|
||||
// vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
/* Stop continuous AWB statistics */
|
||||
ESP_ERROR_CHECK(esp_isp_awb_controller_stop_continuous_statistics(awb_ctlr));
|
||||
|
||||
/* Disable the AWB controller */
|
||||
ESP_ERROR_CHECK(esp_isp_awb_controller_disable(awb_ctlr));
|
||||
/* Delete the AWB controller and free the resources */
|
||||
ESP_ERROR_CHECK(esp_isp_del_awb_controller(awb_ctlr));
|
||||
|
||||
ISP AE Controller
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Before doing ISP AE, you need to enable the ISP AE controller first, by calling :cpp:func:`esp_isp_ae_controller_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_ae_controller_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
.. _isp-ae-statistics:
|
||||
|
||||
AE One-shot and Continuous Statistics
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_ae_controller_get_oneshot_statistics` to get oneshot AE statistics result. You can take following code as reference.
|
||||
|
||||
When using AE oneshot statistics, the AE continuous mode need to be disabled otherwise the result may be overwritten by the environment detector. After oneshot operation finishes, you need to restart continuous mode again.
|
||||
|
||||
Aside from the above oneshot API, the ISP AE driver also provides a way to start AE statistics continuously. Calling :cpp:func:`esp_isp_ae_controller_start_continuous_statistics` to start the continuous statistics and :cpp:func:`esp_isp_ae_controller_stop_continuous_statistics` to stop it.
|
||||
|
||||
Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_statistics_done` or :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_change` callback to get the statistics result. See how to register in :ref:`isp-callback`.
|
||||
|
||||
.. note::
|
||||
|
||||
When using oneshot statistics, the AE environment detector will be temporarily disabled and will automatically recover once the oneshot is completed.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_isp_ae_config_t ae_config = {
|
||||
.sample_point = ISP_AE_SAMPLE_POINT_0,
|
||||
};
|
||||
isp_ae_ctlr_t ae_ctlr = NULL;
|
||||
ESP_ERROR_CHECK(esp_isp_new_ae_controller(isp_proc, &ae_config, &ae_ctlr));
|
||||
ESP_ERROR_CHECK(esp_isp_ae_controller_enable(ae_ctlr));
|
||||
isp_ae_result_t result = {};
|
||||
/* Trigger the AE statistics and get its result for one time with timeout value 2000 ms. */
|
||||
ESP_ERROR_CHECK(esp_isp_ae_controller_get_oneshot_statistics(ae_ctlr, 2000, &result));
|
||||
|
||||
/* Start continuous AE statistics */
|
||||
ESP_ERROR_CHECK(esp_isp_ae_controller_start_continuous_statistics(ae_ctlr));
|
||||
// You can do other stuffs here, the statistics result can be obtained in the callback
|
||||
// ......
|
||||
// vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
/* Stop continuous AE statistics */
|
||||
ESP_ERROR_CHECK(esp_isp_ae_controller_stop_continuous_statistics(ae_ctlr));
|
||||
|
||||
/* Disable the AE controller */
|
||||
ESP_ERROR_CHECK(esp_isp_ae_controller_disable(ae_ctlr));
|
||||
/* Delete the AE controller and free the resources */
|
||||
ESP_ERROR_CHECK(esp_isp_del_ae_controller(ae_ctlr));
|
||||
|
||||
Set AE Environment Detector
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_ae_controller_set_env_detector` to set an ISP AE environment detector. You can take following code as reference.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_ae_env_config_t env_config = {
|
||||
.interval = 10,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_ae_controller_set_env_detector(ae_ctlr, &env_config));
|
||||
|
||||
Set AE Environment Detector Threshold
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_ae_controller_set_env_detector_threshold` to set the thresholds (1-255) of an ISP AE environment detector.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_ae_env_thresh_t env_thresh = {
|
||||
.low_thresh = 110,
|
||||
.high_thresh = 130,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_ae_controller_set_env_detector_threshold(ae_ctlr, env_thresh));
|
||||
|
||||
.. _isp-hist:
|
||||
|
||||
ISP Histogram Controller
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Before doing ISP histogram statistics, you need to enable the ISP histogram controller first, by calling :cpp:func:`esp_isp_hist_controller_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_hist_controller_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
.. _isp-hist-statistics:
|
||||
|
||||
Histogram One-shot and Continuous Statistics
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_hist_controller_get_oneshot_statistics` to get oneshot histogram statistics result. You can take following code as reference.
|
||||
|
||||
Aside from the above oneshot API, the ISP histogram driver also provides a way to start histogram statistics continuously. Calling :cpp:func:`esp_isp_hist_controller_start_continuous_statistics` starts the continuous statistics and :cpp:func:`esp_isp_hist_controller_stop_continuous_statistics` stops it.
|
||||
|
||||
Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_hist_cbs_t::on_statistics_done` callback to get the statistics result. See how to register it in :ref:`isp-callback`.
|
||||
|
||||
.. code:: c
|
||||
|
||||
static bool s_hist_scheme_on_statistics_done_callback(isp_hist_ctlr_t awb_ctrlr, const esp_isp_hist_evt_data_t *edata, void *user_data)
|
||||
{
|
||||
for(int i = 0; i < 16; i++) {
|
||||
esp_rom_printf(DRAM_STR("val %d is %x\n"), i, edata->hist_result.hist_value[i]); // get the histogram statistic value
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_isp_hist_cbs_t hist_cbs = {
|
||||
.on_statistics_done = s_hist_scheme_on_statistics_done_callback,
|
||||
};
|
||||
|
||||
esp_isp_hist_register_event_callbacks(hist_ctlr, &hist_cbs, hist_ctlr);
|
||||
esp_isp_hist_controller_enable(hist_ctlr);
|
||||
|
||||
|
||||
.. _isp-bf:
|
||||
|
||||
ISP BF Controller
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
This pipeline is used for doing image input denoising under bayer mode.
|
||||
|
||||
Calling :cpp:func:`esp_isp_bf_configure` to configure BF function, you can take following code as reference.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_isp_bf_config_t bf_config = {
|
||||
.denoising_level = 5,
|
||||
.bf_template = {
|
||||
{1, 2, 1},
|
||||
{2, 4, 2},
|
||||
{1, 2, 1},
|
||||
},
|
||||
...
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_bf_configure(isp_proc, &bf_config));
|
||||
ESP_ERROR_CHECK(esp_isp_bf_enable(isp_proc));
|
||||
|
||||
:cpp:member:`esp_isp_bf_config_t::bf_template` is used for bayer denoise. You can set the :cpp:member:`esp_isp_bf_config_t::bf_template` with a Gaussian filter template or an average filter template.
|
||||
|
||||
After calling :cpp:func:`esp_isp_bf_configure`, you need to enable the ISP BF controller, by calling :cpp:func:`esp_isp_bf_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_bf_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
|
||||
.. _isp-blc:
|
||||
|
||||
ISP BLC Controller
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Black Level Correction (BLC) aims for the issues caused by the uneven black level of the image.
|
||||
|
||||
Calling :cpp:func:`esp_isp_blc_configure` to configure the BLC module to do the correction.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_isp_blc_config_t blc_config = {
|
||||
.window = {
|
||||
.top_left = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
},
|
||||
.btm_right = {
|
||||
.x = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES,
|
||||
.y = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES,
|
||||
},
|
||||
},
|
||||
.filter_enable = true,
|
||||
.filter_threshold = {
|
||||
.top_left_chan_thresh = 128,
|
||||
.top_right_chan_thresh = 128,
|
||||
.bottom_left_chan_thresh = 128,
|
||||
.bottom_right_chan_thresh = 128,
|
||||
},
|
||||
.stretch = {
|
||||
.top_left_chan_stretch_en = true,
|
||||
.top_right_chan_stretch_en = true,
|
||||
.bottom_left_chan_stretch_en = true,
|
||||
.bottom_right_chan_stretch_en = true,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_blc_configure(isp_proc, &blc_config));
|
||||
ESP_ERROR_CHECK(esp_isp_blc_enable(isp_proc));
|
||||
|
||||
After calling :cpp:func:`esp_isp_blc_configure`, you need to enable the ISP BLC controller by calling :cpp:func:`esp_isp_blc_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_blc_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
Calling :cpp:func:`esp_isp_blc_set_correction_offset` to set the BLC correction offset.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_isp_blc_offset_t blc_offset = {
|
||||
.top_left_chan_offset = 20,
|
||||
.top_right_chan_offset = 20,
|
||||
.bottom_left_chan_offset = 20,
|
||||
.bottom_right_chan_offset = 20,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_blc_set_correction_offset(isp_proc, &blc_offset));
|
||||
|
||||
|
||||
.. _isp-lsc:
|
||||
|
||||
ISP LSC Controller
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Lens Shading Correction (LSC) aims for the issues caused by the uneven refraction of light through the camera lens.
|
||||
|
||||
Calling :cpp:func:`esp_isp_lsc_configure` to configure the LSC module to do the correction. The :cpp:type:`esp_isp_lsc_gain_array_t` is necessary for the hardware to do the correction related calculation. :cpp:func:`esp_isp_lsc_allocate_gain_array` is a helper function to help allocate proper size of memory for the gains.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_isp_lsc_gain_array_t gain_array = {};
|
||||
size_t gain_size = 0;
|
||||
ESP_ERROR_CHECK(esp_isp_lsc_allocate_gain_array(isp_proc, &gain_array, &gain_size));
|
||||
|
||||
esp_isp_lsc_config_t lsc_config = {
|
||||
.gain_array = &gain_array,
|
||||
};
|
||||
isp_lsc_gain_t gain_val = {
|
||||
.decimal = 204,
|
||||
.integer = 0,
|
||||
};
|
||||
for (int i = 0; i < gain_size; i++) {
|
||||
gain_array.gain_r[i].val = gain_val.val;
|
||||
gain_array.gain_gr[i].val = gain_val.val;
|
||||
gain_array.gain_gb[i].val = gain_val.val;
|
||||
gain_array.gain_b[i].val = gain_val.val;
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_isp_lsc_configure(isp_proc, &lsc_config));
|
||||
|
||||
After calling :cpp:func:`esp_isp_lsc_configure`, you need to enable the ISP LSC controller by calling :cpp:func:`esp_isp_lsc_enable`. The LSC can be disabled by calling :cpp:func:`esp_isp_lsc_disable`. It is allowed to call :cpp:func:`esp_isp_lsc_configure` when the LSC is not enabled, but the LSC function will only take effect when it is enabled.
|
||||
|
||||
|
||||
.. _isp-color:
|
||||
|
||||
ISP Color Controller
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This pipeline is used to adjust the image contrast, saturation, hue and brightness.
|
||||
|
||||
Calling :cpp:func:`esp_isp_color_configure` to configure color function, you can take following code as reference.
|
||||
|
||||
{IDF_TARGET_SOC_ISP_COLOR_CONTRAST_MAX:default="1.0", esp32p4="1.0"}
|
||||
{IDF_TARGET_SOC_ISP_COLOR_CONTRAST_DEFAULT:default="1.0", esp32p4="1.0"}
|
||||
|
||||
{IDF_TARGET_SOC_ISP_COLOR_SATURATION_MAX:default="1.0", esp32p4="1.0"}
|
||||
{IDF_TARGET_SOC_ISP_COLOR_SATURATION_DEFAULT:default="1.0", esp32p4="1.0"}
|
||||
|
||||
{IDF_TARGET_SOC_ISP_COLOR_HUE_MAX:default="359", esp32p4="359"}
|
||||
{IDF_TARGET_SOC_ISP_COLOR_HUE_DEFAULT:default="0", esp32p4="0"}
|
||||
|
||||
{IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MIN:default="-127", esp32p4="-127"}
|
||||
{IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MAX:default="128", esp32p4="128"}
|
||||
{IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_DEFAULT:default="0", esp32p4="0"}
|
||||
|
||||
.. list::
|
||||
|
||||
- Contrast value should be 0 ~ {IDF_TARGET_SOC_ISP_COLOR_CONTRAST_MAX}, default {IDF_TARGET_SOC_ISP_COLOR_CONTRAST_DEFAULT}
|
||||
- Saturation value should be 0 ~ {IDF_TARGET_SOC_ISP_COLOR_SATURATION_MAX}, default {IDF_TARGET_SOC_ISP_COLOR_SATURATION_DEFAULT}
|
||||
- Hue value should be 0 ~ {IDF_TARGET_SOC_ISP_COLOR_HUE_MAX}, default {IDF_TARGET_SOC_ISP_COLOR_HUE_DEFAULT}
|
||||
- Brightness value should be {IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MIN} ~ {IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MAX}, default {IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_DEFAULT}
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_color_config_t color_config = {
|
||||
.color_contrast = {
|
||||
.integer = 1,
|
||||
.decimal = 0,
|
||||
},
|
||||
.color_saturation = {
|
||||
.integer = 1,
|
||||
.decimal = 0,
|
||||
},
|
||||
.color_hue = 0,
|
||||
.color_brightness = 0,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_color_configure(isp_proc, &color_config));
|
||||
ESP_ERROR_CHECK(esp_isp_color_enable(isp_proc));
|
||||
|
||||
After calling :cpp:func:`esp_isp_color_configure`, you need to enable the ISP color controller, by calling :cpp:func:`esp_isp_color_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_color_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
.. note::
|
||||
|
||||
When the ISP DVP peripheral is used with the output color format set to the RGB color space, :ref:`isp-color` is automatically enabled in the camera driver to ensure correct data output. The function :cpp:func:`esp_isp_color_disable` should never be called in this case, otherwise it may result in disarrayed camera data.
|
||||
|
||||
.. _isp-ccm-config:
|
||||
|
||||
Configure CCM
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Color correction matrix can scale the color ratio of RGB888 pixels. It can be used for adjusting the image color via some algorithms, for example, used for white balance by inputting the AWB computed result, or used as a filter with some filter algorithms.
|
||||
|
||||
To adjust the color correction matrix, here is the formula:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[ R' ] [ RR RG RB ] [ R ]
|
||||
[ G' ] = [ GR GG GB ] * [ G ]
|
||||
[ B' ] [ BR BG BB ] [ B ]
|
||||
|
||||
, and you can refer to the following code:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// ...
|
||||
// Configure CCM
|
||||
esp_isp_ccm_config_t ccm_cfg = {
|
||||
.matrix = {
|
||||
1.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 1.0
|
||||
},
|
||||
.saturation = false,
|
||||
...
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_ccm_configure(isp_proc, &ccm_cfg));
|
||||
// The configured CCM will be applied to the image once the CCM module is enabled
|
||||
ESP_ERROR_CHECK(esp_isp_ccm_enable(isp_proc));
|
||||
// CCM can also be configured after it is enabled
|
||||
ccm_cfg.matrix[0][0] = 2.0;
|
||||
ESP_ERROR_CHECK(esp_isp_ccm_configure(isp_proc, &ccm_cfg));
|
||||
// Disable CCM if no longer needed
|
||||
ESP_ERROR_CHECK(esp_isp_ccm_disable(isp_proc));
|
||||
|
||||
.. _isp-demosaic:
|
||||
|
||||
ISP Demosaic Controller
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This pipeline is used for doing image demosaic algorithm to convert RAW image to RGB mode.
|
||||
|
||||
Calling :cpp:func:`esp_isp_demosaic_configure` to configure Demosaic function, you can take following code as reference.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_demosaic_config_t demosaic_config = {
|
||||
.grad_ratio = {
|
||||
.integer = 2,
|
||||
.decimal = 5,
|
||||
},
|
||||
...
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_isp_demosaic_configure(isp_proc, &demosaic_config));
|
||||
ESP_ERROR_CHECK(esp_isp_demosaic_enable(isp_proc));
|
||||
|
||||
After calling :cpp:func:`esp_isp_demosaic_configure`, you need to enable the ISP Demosaic controller, by calling :cpp:func:`esp_isp_demosaic_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_demosaic_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
:cpp:func:`esp_isp_demosaic_configure` is allowed to be called even if the driver is in **init** state, but the demosaic configurations will only be taken into effect when in **enable** state.
|
||||
|
||||
.. _isp-gamma-correction:
|
||||
|
||||
Enable Gamma Correction
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The human visual system is non-linearly sensitive to the physical luminance. Adding gamma correction to the ISP pipeline to transform RGB coordinates into a space in which coordinates are proportional to subjective brightness.
|
||||
|
||||
The driver provides a helper API :cpp:func:`esp_isp_gamma_fill_curve_points` to fill :cpp:type:`isp_gamma_curve_points_t`, which is a group of points used to describe the gamma correction curve. Or you can manually declare the points as your desired gamma correction curve. Each R/G/B component can have its own gamma correction curve, you can set the configuration by calling :cpp:func:`esp_isp_gamma_configure`.
|
||||
|
||||
A typical code example is:
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// Set the camera gamma to be 0.7, so the gamma correction curve is y = 256 * (x / 256) ^ 0.7
|
||||
static uint32_t s_gamma_curve(uint32_t x)
|
||||
{
|
||||
return pow((double)x / 256, 0.7) * 256;
|
||||
}
|
||||
|
||||
isp_gamma_curve_points_t pts = {};
|
||||
ESP_ERROR_CHECK(esp_isp_gamma_fill_curve_points(s_gamma_curve, &pts));
|
||||
ESP_ERROR_CHECK(esp_isp_gamma_configure(isp_proc, COLOR_COMPONENT_R, &pts));
|
||||
ESP_ERROR_CHECK(esp_isp_gamma_configure(isp_proc, COLOR_COMPONENT_G, &pts));
|
||||
ESP_ERROR_CHECK(esp_isp_gamma_configure(isp_proc, COLOR_COMPONENT_B, &pts));
|
||||
|
||||
// Enable gamma module after curve parameters configured
|
||||
ESP_ERROR_CHECK(esp_isp_gamma_enable(isp_proc));
|
||||
|
||||
// Disable gamma if no longer needed
|
||||
ESP_ERROR_CHECK(esp_isp_gamma_disable(isp_proc));
|
||||
|
||||
.. _isp-sharpen:
|
||||
|
||||
ISP Sharpen Controller
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This pipeline is used for doing image input sharpening under YUV mode.
|
||||
|
||||
Calling :cpp:func:`esp_isp_sharpen_configure` to configure Sharpen function, you can take following code as reference.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_sharpen_config_t sharpen_config = {
|
||||
.h_thresh = 255,
|
||||
.sharpen_template = {
|
||||
{1, 2, 1},
|
||||
{2, 4, 2},
|
||||
{1, 2, 1},
|
||||
},
|
||||
...
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_sharpen_configure(isp_proc, &sharpen_config));
|
||||
ESP_ERROR_CHECK(esp_isp_sharpen_enable(isp_proc));
|
||||
|
||||
:cpp:member:`esp_isp_sharpen_config_t::sharpen_template` is used for sharpening. You can set the :cpp:member:`esp_isp_sharpen_config_t::sharpen_template` with a Gaussian filter template or an average filter template.
|
||||
|
||||
After calling :cpp:func:`esp_isp_sharpen_configure`, you need to enable the ISP Sharpen controller, by calling :cpp:func:`esp_isp_sharpen_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_sharpen_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
:cpp:func:`esp_isp_sharpen_configure` is allowed to be called even if the driver is in **init** state, but the sharpen configurations will only be taken into effect when in **enable** state.
|
||||
|
||||
.. _isp-crop:
|
||||
|
||||
ISP Image Crop Controller
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ISP image crop function can extract a specified region from the original image, reducing the amount of data for subsequent processing and improving processing efficiency. The crop function is executed at the end of the ISP pipeline and can output a smaller region than the input image.
|
||||
|
||||
.. note::
|
||||
|
||||
The ISP image crop function is only available on ESP32-P4 revision 3.0 and above.
|
||||
|
||||
Calling :cpp:func:`esp_isp_crop_configure` to configure the image crop function, you can take the following code as reference:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_isp_crop_config_t crop_config = {
|
||||
.window = {
|
||||
.top_left = {
|
||||
.x = 100, // Top-left X coordinate of crop region
|
||||
.y = 100, // Top-left Y coordinate of crop region
|
||||
},
|
||||
.btm_right = {
|
||||
.x = 699, // Bottom-right X coordinate of crop region
|
||||
.y = 499, // Bottom-right Y coordinate of crop region
|
||||
}
|
||||
}
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_crop_configure(isp_proc, &crop_config));
|
||||
ESP_ERROR_CHECK(esp_isp_crop_enable(isp_proc));
|
||||
|
||||
After calling :cpp:func:`esp_isp_crop_configure`, you need to enable the ISP image crop controller by calling :cpp:func:`esp_isp_crop_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_crop_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
:cpp:func:`esp_isp_crop_configure` is allowed to be called even if the driver is in **init** state, but the crop configurations will only be taken into effect when in **enable** state.
|
||||
|
||||
.. note::
|
||||
|
||||
- The top-left coordinates (top_left) of the crop region must be smaller than the bottom-right coordinates (btm_right)
|
||||
- The top-left coordinates (top_left) of the crop region must be even, and the bottom-right coordinates (btm_right) must be odd
|
||||
- The crop region cannot exceed the boundaries of the original image
|
||||
- Adjust the display medium (such as LCD) size according to the cropped resolution to ensure complete display and avoid black borders or stretching.
|
||||
|
||||
|
||||
.. _isp-callback:
|
||||
|
||||
Register Event Callbacks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After an ISP module starts up, it can generate a specific event dynamically.
|
||||
|
||||
You can save your own context to callback function as well, via the parameter ``user_data``. The user data will be directly passed to the callback function.
|
||||
|
||||
.. note::
|
||||
|
||||
The below-mentioned callback functions are called within an ISR context. You must ensure that the functions do not attempt to block (e.g., by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function).
|
||||
|
||||
Register ISP Processor Event Callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After the ISP processor is enabled, it can generate multiple events of multiple ISP submodules dynamically. You can hook your functions to the interrupt service routine by calling :cpp:func:`esp_isp_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_evt_cbs_t`:
|
||||
|
||||
- :cpp:member:`esp_isp_evt_cbs_t::on_sharpen_frame_done` sets a callback function for sharpen frame done. It will be called after the ISP sharpen submodule finishes its operation for one frame. The function prototype is declared in :cpp:type:`esp_isp_sharpen_callback_t`.
|
||||
|
||||
Register ISP AF Environment Detector Event Callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After the ISP AF environment detector starts up, it can generate a specific event dynamically. If you have some functions that should be called when the event happens, please hook your function to the interrupt service routine by calling :cpp:func:`esp_isp_af_env_detector_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_af_env_detector_evt_cbs_t`:
|
||||
|
||||
- :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_statistics_done` sets a callback function for environment statistics done. The function prototype is declared in :cpp:type:`esp_isp_af_env_detector_callback_t`.
|
||||
- :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_change` sets a callback function for environment change. The function prototype is declared in :cpp:type:`esp_isp_af_env_detector_callback_t`.
|
||||
|
||||
Register ISP AWB Statistics Done Event Callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After the ISP AWB controller finished statistics of white patches, it can generate a specific event dynamically. If you want to be informed when the statistics done event takes place, please hook your function to the interrupt service routine by calling :cpp:func:`esp_isp_awb_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_awb_cbs_t`:
|
||||
|
||||
- :cpp:member:`esp_isp_awb_cbs_t::on_statistics_done` sets a callback function when finishing statistics of the white patches. The function prototype is declared in :cpp:type:`esp_isp_awb_callback_t`.
|
||||
|
||||
|
||||
Register ISP AE Environment Detector Event Callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After the ISP AE environment detector starts up, it can generate a specific event dynamically. If you have some functions that should be called when the event happens, please hook your function to the interrupt service routine by calling :cpp:func:`esp_isp_ae_env_detector_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_ae_env_detector_evt_cbs_t`:
|
||||
|
||||
- :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_statistics_done` sets a callback function for environment statistics done. The function prototype is declared in :cpp:type:`esp_isp_ae_env_detector_callback_t`.
|
||||
- :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_change` sets a callback function for environment change. The function prototype is declared in :cpp:type:`esp_isp_ae_env_detector_callback_t`.
|
||||
|
||||
|
||||
Register ISP HIST Statistics Done Event Callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After the ISP HIST controller finished statistics of brightness, it can generate a specific event dynamically. If you want to be informed when the statistics done event takes place, please hook your function to the interrupt service routine by calling :cpp:func:`esp_isp_hist_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_hist_cbs_t`:
|
||||
|
||||
- :cpp:member:`esp_isp_hist_cbs_t::on_statistics_done` sets a callback function when finishing statistics of the brightness. The function prototype is declared in :cpp:type:`esp_isp_hist_callback_t`.
|
||||
|
||||
.. _isp-thread-safety:
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The following factory function are guaranteed to be thread safe by the driver:
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:func:`esp_isp_new_processor`
|
||||
- :cpp:func:`esp_isp_del_processor`
|
||||
- :cpp:func:`esp_isp_new_af_controller`
|
||||
- :cpp:func:`esp_isp_del_af_controller`
|
||||
- :cpp:func:`esp_isp_new_awb_controller`
|
||||
- :cpp:func:`esp_isp_del_awb_controller`
|
||||
- :cpp:func:`esp_isp_new_ae_controller`
|
||||
- :cpp:func:`esp_isp_del_ae_controller`
|
||||
- :cpp:func:`esp_isp_new_hist_controller`
|
||||
- :cpp:func:`esp_isp_del_hist_controller`
|
||||
|
||||
These functions can be called from different RTOS tasks without protection by extra locks. Other APIs are not guaranteed to be thread-safe.
|
||||
|
||||
.. _isp-kconfig-options:
|
||||
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_ISP_ISR_IRAM_SAFE` controls whether the default ISR handler should be masked when the cache is disabled.
|
||||
|
||||
.. _isp-iram-safe:
|
||||
|
||||
IRAM Safe
|
||||
^^^^^^^^^
|
||||
|
||||
By default, the ISP interrupt will be deferred when the cache is disabled because of writing or erasing the flash.
|
||||
|
||||
Kconfig option :ref:`CONFIG_ISP_ISR_IRAM_SAFE` will:
|
||||
|
||||
- Enable the interrupt being serviced even when the cache is disabled
|
||||
- Place all functions that used by the ISR into IRAM
|
||||
- Place driver object into DRAM (in case it is mapped to PSRAM by accident)
|
||||
|
||||
This allows the interrupt to run while the cache is disabled, but comes at the cost of increased IRAM consumption. With this option enabled, the ISR callbacks will be running when cache is disabled. Therefore you should make sure the callbacks and its involved context are IRAM-safe as well.
|
||||
|
||||
Kconfig option :ref:`CONFIG_ISP_CTRL_FUNC_IN_IRAM` will:
|
||||
|
||||
- Place some of the ISP control functions into IRAM, including:
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:func:`esp_isp_sharpen_configure`
|
||||
- :cpp:func:`esp_isp_demosaic_configure`
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
* :example:`peripherals/isp/multi_pipelines` demonstrates how to use the ISP pipelines to process the image signals from camera sensors and display the video on LCD screen via DSI peripheral.
|
||||
* `esp_video/examples <https://github.com/espressif/esp-video-components/tree/master/esp_video/examples>`_ provides some examples of enabling ISP control algorithms.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/isp.inc
|
||||
.. include-build-file:: inc/isp_af.inc
|
||||
.. include-build-file:: inc/isp_ae.inc
|
||||
.. include-build-file:: inc/isp_awb.inc
|
||||
.. include-build-file:: inc/isp_bf.inc
|
||||
.. include-build-file:: inc/isp_blc.inc
|
||||
.. include-build-file:: inc/isp_lsc.inc
|
||||
.. include-build-file:: inc/isp_ccm.inc
|
||||
.. include-build-file:: inc/isp_demosaic.inc
|
||||
.. include-build-file:: inc/isp_sharpen.inc
|
||||
.. include-build-file:: inc/isp_gamma.inc
|
||||
.. include-build-file:: inc/isp_hist.inc
|
||||
.. include-build-file:: inc/isp_color.inc
|
||||
.. include-build-file:: inc/isp_crop.inc
|
||||
.. include-build-file:: inc/isp_core.inc
|
||||
.. include-build-file:: inc/components/esp_driver_isp/include/driver/isp_types.inc
|
||||
.. include-build-file:: inc/components/esp_hal_cam/include/hal/isp_types.inc
|
||||
@@ -0,0 +1,628 @@
|
||||
JPEG Encoder and Decoder
|
||||
========================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
JPEG is a commonly used method of lossy compression for digital images, particularly for those images produced by digital photography. The compression level varies with changes in image size and compression quality. JPEG typically achieves 10:1 compression with little perceptible loss in image quality.
|
||||
|
||||
JPEG codec on {IDF_TARGET_NAME} is an image codec, which is based on the JPEG baseline standard, for compressing and decompressing images to reduce the bandwidth required to transmit images or the space required to store images, making it possible to process large-resolution images. But please note, at one time, the codec engine can only work as either encoder or decoder.
|
||||
|
||||
For more hardware features of JPEG codec, please refer to the `JPEG codec <{IDF_TARGET_TRM_EN_URL}#jpegcodec>`__ section in {IDF_TARGET_NAME} Technical Reference Manual, for more details.
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
This document covers the following sections:
|
||||
|
||||
- :ref:`jpeg-resource-allocation` - covers how to allocate JPEG resources with properly set of configurations. It also covers how to recycle the resources when they finished working.
|
||||
- :ref:`jpeg-finite-state-machine` - covers JPEG workflow. Introduce how jpeg driver uses internal resources and its software process.
|
||||
- :ref:`jpeg-decoder-engine` - covers behavior of JPEG decoder engine. Introduce how to use decoder engine functions to decode an image (from jpg format to raw format).
|
||||
- :ref:`jpeg-encoder-engine` - covers behavior of JPEG encoder engine. Introduce how to use encoder engine functions to encode an image (from raw format to jpg format).
|
||||
- :ref:`jpeg-performance-overview` - covers encoder and decoder performance.
|
||||
- :ref:`jpeg-pixel-storage-layout` - covers color space order overview required in this JPEG decoder and encoder.
|
||||
- :ref:`jpeg-thread-safety` - lists which APIs are guaranteed to be thread safe by the driver.
|
||||
- :ref:`jpeg-power-management` - describes how JPEG driver would be affected by power consumption.
|
||||
- :ref:`jpeg-flash-encryption` - describes how to use the JPEG codec correctly when flash/PSRAM encryption is enabled.
|
||||
- :ref:`jpeg-kconfig-options` - lists the supported Kconfig options that can bring different effects to the driver.
|
||||
|
||||
.. _jpeg-resource-allocation:
|
||||
|
||||
Resource Allocation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Install JPEG decoder engine
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
JPEG decoder engine requires the configuration that specified by :cpp:type:`jpeg_decode_engine_cfg_t`.
|
||||
|
||||
If the configurations in :cpp:type:`jpeg_decode_engine_cfg_t` is specified, users can call :cpp:func:`jpeg_new_decoder_engine` to allocate and initialize a JPEG decoder engine. This function will return an JPEG decoder handle if it runs correctly. You can take following code as reference.
|
||||
|
||||
.. code:: c
|
||||
|
||||
jpeg_decoder_handle_t decoder_engine;
|
||||
|
||||
jpeg_decode_engine_cfg_t decode_eng_cfg = {
|
||||
.intr_priority = 0,
|
||||
.timeout_ms = 40,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(jpeg_new_decoder_engine(&decode_eng_cfg, &decoder_engine));
|
||||
|
||||
|
||||
Uninstall JPEG decoder engine
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed JPEG engine is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`jpeg_del_decoder_engine`, so that the underlying hardware is released.
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_ERROR_CHECK(jpeg_del_decoder_engine(decoder_engine));
|
||||
|
||||
Install JPEG encoder engine
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The JPEG encoder engine requires the configuration specified by :cpp:type:`jpeg_encode_engine_cfg_t`.
|
||||
|
||||
If the configurations in :cpp:type:`jpeg_encode_engine_cfg_t` is specified, users can call :cpp:func:`jpeg_new_encoder_engine` to allocate and initialize a JPEG encoder engine. This function will return an JPEG encoder handle if it runs correctly. You can take following code as reference.
|
||||
|
||||
.. code:: c
|
||||
|
||||
jpeg_encoder_handle_t encoder_engine;
|
||||
|
||||
jpeg_encode_engine_cfg_t encode_eng_cfg = {
|
||||
.intr_priority = 0,
|
||||
.timeout_ms = 40,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(jpeg_new_encoder_engine(&encode_eng_cfg, &encoder_engine));
|
||||
|
||||
Uninstall JPEG encoder engine
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed JPEG engine is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`jpeg_del_encoder_engine`, so that the underlying hardware is released.
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_ERROR_CHECK(jpeg_del_encoder_engine(encoder_engine));
|
||||
|
||||
.. _jpeg-finite-state-machine:
|
||||
|
||||
Finite State Machine
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The JPEG driver usage of hardware resources and its process workflow are shown in the following graph:
|
||||
|
||||
.. figure:: ../../../_static/diagrams/jpeg/jpeg_workflow.png
|
||||
:align: center
|
||||
:alt: JPEG finite state machine
|
||||
|
||||
JPEG finite state machine
|
||||
|
||||
.. _jpeg-decoder-engine:
|
||||
|
||||
JPEG Decoder Engine
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After installing the JPEG decoder driver by :cpp:func:`jpeg_new_decoder_engine`, {IDF_TARGET_NAME} is ready to decode JPEG pictures by :cpp:func:`jpeg_decoder_process`. :cpp:func:`jpeg_decoder_process` is flexible for decoding different types of pictures by a configurable parameter called :cpp:type:`jpeg_decode_cfg_t`.
|
||||
|
||||
Moreover, our JPEG decoder API provides a helper function which helps you get the basic information of your given image. Calling :cpp:func:`jpeg_decoder_get_info` would return the picture information structure called :cpp:func:`jpeg_decoder_get_info`. If you already know the picture basic information, this functions is unnecessary to be called.
|
||||
|
||||
The format conversions supported by this driver are listed in the table below:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 50 50
|
||||
:align: center
|
||||
|
||||
* - Format of the already compressed image
|
||||
- Format after decompressing
|
||||
* - YUV444
|
||||
- RGB565/RGB888
|
||||
* - YUV422
|
||||
- RGB565/RGB888
|
||||
* - YUV420
|
||||
- RGB565/RGB888
|
||||
* - GRAY
|
||||
- GRAY
|
||||
|
||||
Overall, You can take following code as reference, the code is going to decode a 1080*1920 picture.
|
||||
|
||||
.. code:: c
|
||||
|
||||
jpeg_decode_cfg_t decode_cfg_rgb = {
|
||||
.output_format = JPEG_DECODE_OUT_FORMAT_RGB888,
|
||||
.rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR,
|
||||
};
|
||||
|
||||
size_t rx_buffer_size;
|
||||
|
||||
jpeg_decode_memory_alloc_cfg_t rx_mem_cfg = {
|
||||
.buffer_direction = JPEG_DEC_ALLOC_OUTPUT_BUFFER,
|
||||
};
|
||||
|
||||
const uint8_t *bit_stream = embedded_jpeg_start;
|
||||
uint8_t *out_buf = (uint8_t*)jpeg_alloc_decoder_mem(1920 * 1088 * 3, &rx_mem_cfg, &rx_buffer_size);
|
||||
|
||||
jpeg_decode_picture_info_t header_info;
|
||||
ESP_ERROR_CHECK(jpeg_decoder_get_info(bit_stream, bit_stream_size, &header_info));
|
||||
uint32_t out_size = 0;
|
||||
ESP_ERROR_CHECK(jpeg_decoder_process(decoder_engine, &decode_cfg_rgb, bit_stream, bit_stream_size, out_buf, &out_size));
|
||||
|
||||
|
||||
There are some tips that can help you use this driver more accurately:
|
||||
|
||||
1. In above code, you should make sure the output buffer `out_buf` follows the driver's alignment requirements. We provide a helper function :cpp:func:`jpeg_alloc_decoder_mem` to help you allocate a buffer with aligned size and address.
|
||||
|
||||
2. The content of `bit_stream` should not be changed until :cpp:func:`jpeg_decoder_process` returns. This input buffer can come directly from flash-mapped embedded data or any other memory region that stays readable for the full call.
|
||||
|
||||
3. If the source JPEG uses YUV420 or YUV422 sampling, the decoded output dimensions can be padded up to 16-pixel boundaries. For example, if the visible image size is 1080*1920, the decoder may require an output buffer sized for 1088*1920 pixels. This comes from the JPEG block layout, so please provide enough output buffer memory for the padded image, not only for the visible width and height.
|
||||
|
||||
.. _jpeg-encoder-engine:
|
||||
|
||||
JPEG Encoder Engine
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After installing the JPEG encoder driver by :cpp:func:`jpeg_new_encoder_engine`, {IDF_TARGET_NAME} is ready to encode JPEG pictures by :cpp:func:`jpeg_encoder_process`. :cpp:func:`jpeg_encoder_process` is flexible for decoding different types of pictures by a configurable parameter called :cpp:type:`jpeg_encode_cfg_t`.
|
||||
|
||||
The format conversions supported by this driver are listed in the table below:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 50 50
|
||||
:align: center
|
||||
|
||||
* - Format of Original Image
|
||||
- Down sampling method
|
||||
* - RGB565/RGB888
|
||||
- YUV444/YUV422/YUV420
|
||||
* - GRAY
|
||||
- GRAY
|
||||
|
||||
|
||||
Below is the example of code that encodes a 1280x720 picture from an embedded raw buffer:
|
||||
|
||||
.. code:: c
|
||||
|
||||
size_t raw_size_720p = EXAMPLE_WIDTH * EXAMPLE_HEIGHT * 3; /* 1280x720 bgr24 frame */
|
||||
jpeg_encode_cfg_t enc_config = {
|
||||
.src_type = JPEG_ENCODE_IN_FORMAT_RGB888,
|
||||
.sub_sample = JPEG_DOWN_SAMPLING_YUV422,
|
||||
.image_quality = 80,
|
||||
.width = 1280,
|
||||
.height = 720,
|
||||
.pixel_reverse = false, // Whether to reverse the pixel order of the input image, or pixel order detail please refer to technical reference manual
|
||||
};
|
||||
|
||||
jpeg_encode_memory_alloc_cfg_t rx_mem_cfg = {
|
||||
.buffer_direction = JPEG_ENC_ALLOC_OUTPUT_BUFFER,
|
||||
};
|
||||
size_t jpg_buffer_size = 0;
|
||||
uint8_t *jpg_buf_720p = (uint8_t*)jpeg_alloc_encoder_mem(raw_size_720p / 10, &rx_mem_cfg, &jpg_buffer_size);
|
||||
if (jpg_buf_720p == NULL) {
|
||||
ESP_LOGE(TAG, "alloc jpg_buf_720p error");
|
||||
return;
|
||||
}
|
||||
|
||||
/* The current JPEG encoder input path expects BGR24-style raw bytes for
|
||||
* JPEG_ENCODE_IN_FORMAT_RGB888. The embedded asset can be read directly
|
||||
* from flash as long as it remains valid until this call returns. */
|
||||
ESP_ERROR_CHECK(jpeg_encoder_process(jpeg_handle, &enc_config, embedded_bgr24_start, raw_size_720p, jpg_buf_720p, jpg_buffer_size, &jpg_size_720p));
|
||||
|
||||
There are some tips that can help you use this driver more accurately:
|
||||
|
||||
1. In the above code, the output buffer `jpg_buf_720p` should be allocated by calling :cpp:func:`jpeg_alloc_encoder_mem`, because the JPEG bitstream buffer must satisfy the driver's alignment requirements.
|
||||
|
||||
2. The content pointed to by `embedded_bgr24_start` should not be changed until :cpp:func:`jpeg_encoder_process` returns. This input buffer can come from flash-mapped embedded data or another memory region that stays readable for the full call.
|
||||
|
||||
3. For :cpp:enumerator:`JPEG_ENCODE_IN_FORMAT_RGB888`, the current driver expects the raw input bytes in a BGR24-style layout. Supplying RGB24 raw data would swap the red and blue channels in the encoded JPEG.
|
||||
|
||||
4. The compression ratio depends on the chosen `image_quality` and the content of the image itself. Generally, a higher `image_quality` value obviously results in better image quality but a smaller compression ratio. As for the image content, it is hard to give any specific guidelines, so this question is out of the scope of this document. Generally, the baseline JPEG compression ratio can vary from 40:1 to 10:1. Please take the actual situation into account.
|
||||
|
||||
.. _jpeg-performance-overview:
|
||||
|
||||
Performance Overview
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This section provides some measurements of the decoder and encoder performance. The data presented in the tables below gives the average values of decoding or encoding a randomly chosen picture fragments for 50 times. All tests were performed at a CPU frequency of 360MHz and a SPI RAM clock frequency of 200MHz. Only JPEG related code is run in this test, no other modules are involved (e.g. USB Camera, etc.).
|
||||
|
||||
Both decoder and encoder are not cause too much CPU involvement. Only header parse causes CPU source. Calculations related to JPEG compression, such as DCT, quantization, huffman encoding/decoding, etc., are done entirely in hardware.
|
||||
|
||||
JPEG decoder performance
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 25 25 25 25 25
|
||||
:align: center
|
||||
|
||||
* - JPEG Height
|
||||
- JPEG Width
|
||||
- Pixel Format in [#]_
|
||||
- Pixel Format out [#]_
|
||||
- Performance (fps)
|
||||
* - 1080
|
||||
- 1920
|
||||
- YUV422
|
||||
- RGB888/RGB565
|
||||
- 48
|
||||
* - 720
|
||||
- 1280
|
||||
- YUV422
|
||||
- RGB888/RGB565
|
||||
- 109
|
||||
* - 480
|
||||
- 800
|
||||
- YUV422
|
||||
- RGB888/RGB565
|
||||
- 253
|
||||
* - 480
|
||||
- 640
|
||||
- YUV422
|
||||
- RGB888/RGB565
|
||||
- 307
|
||||
* - 480
|
||||
- 320
|
||||
- YUV422
|
||||
- RGB888/RGB565
|
||||
- 571
|
||||
* - 720
|
||||
- 1280
|
||||
- GRAY
|
||||
- GRAY
|
||||
- 161
|
||||
|
||||
.. [#] Format of the already compressed image
|
||||
.. [#] Format after decompressing
|
||||
|
||||
.. only:: esp32s31
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 25 25 25 25 25
|
||||
:align: center
|
||||
|
||||
* - JPEG Height
|
||||
- JPEG Width
|
||||
- Pixel Format in [#]_
|
||||
- Pixel Format out [#]_
|
||||
- Performance (fps)
|
||||
* - 1080
|
||||
- 1920
|
||||
- YUV422
|
||||
- RGB888/RGB565
|
||||
- 28
|
||||
* - 720
|
||||
- 1280
|
||||
- YUV422
|
||||
- RGB888/RGB565
|
||||
- 62
|
||||
* - 480
|
||||
- 800
|
||||
- YUV422
|
||||
- RGB888/RGB565
|
||||
- 138
|
||||
* - 480
|
||||
- 320
|
||||
- YUV422
|
||||
- RGB888/RGB565
|
||||
- 286
|
||||
* - 720
|
||||
- 1280
|
||||
- GRAY
|
||||
- GRAY
|
||||
- 96
|
||||
|
||||
.. [#] Format of the already compressed image
|
||||
.. [#] Format after decompressing
|
||||
|
||||
JPEG encoder performance
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 25 25 25 25 25
|
||||
:align: center
|
||||
|
||||
* - JPEG Height
|
||||
- JPEG Width
|
||||
- Pixel Format in [#]_
|
||||
- Pixel Format out [#]_
|
||||
- Performance (fps)
|
||||
* - 1080
|
||||
- 1920
|
||||
- RGB888
|
||||
- YUV422
|
||||
- 26
|
||||
* - 1080
|
||||
- 1920
|
||||
- RGB565
|
||||
- YUV422
|
||||
- 36
|
||||
* - 1080
|
||||
- 1920
|
||||
- RGB565
|
||||
- YUV420
|
||||
- 40
|
||||
* - 1080
|
||||
- 1920
|
||||
- RGB565
|
||||
- YUV444
|
||||
- 24
|
||||
* - 1080
|
||||
- 1920
|
||||
- RGB888
|
||||
- YUV422
|
||||
- 26
|
||||
* - 720
|
||||
- 1280
|
||||
- RGB565
|
||||
- YUV420
|
||||
- 88
|
||||
* - 720
|
||||
- 1280
|
||||
- RGB565
|
||||
- YUV444
|
||||
- 55
|
||||
* - 720
|
||||
- 1280
|
||||
- RGB565
|
||||
- YUV422
|
||||
- 81
|
||||
* - 480
|
||||
- 800
|
||||
- RGB888
|
||||
- YUV420
|
||||
- 142
|
||||
* - 640
|
||||
- 800
|
||||
- RGB888
|
||||
- YUV420
|
||||
- 174
|
||||
* - 480
|
||||
- 320
|
||||
- RGB888
|
||||
- YUV420
|
||||
- 315
|
||||
* - 720
|
||||
- 1280
|
||||
- GRAY
|
||||
- GRAY
|
||||
- 163
|
||||
|
||||
.. [#] Format of Original Image
|
||||
.. [#] Down sampling method
|
||||
|
||||
.. only:: esp32s31
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 25 25 25 25 25
|
||||
:align: center
|
||||
|
||||
* - JPEG Height
|
||||
- JPEG Width
|
||||
- Pixel Format in [#]_
|
||||
- Pixel Format out [#]_
|
||||
- Performance (fps)
|
||||
* - 1080
|
||||
- 1920
|
||||
- RGB888
|
||||
- YUV422
|
||||
- 21
|
||||
* - 1080
|
||||
- 1920
|
||||
- RGB565
|
||||
- YUV422
|
||||
- 21
|
||||
* - 1080
|
||||
- 1920
|
||||
- RGB565
|
||||
- YUV420
|
||||
- 25
|
||||
* - 720
|
||||
- 1280
|
||||
- RGB565
|
||||
- YUV420
|
||||
- 62
|
||||
* - 720
|
||||
- 1280
|
||||
- RGB565
|
||||
- YUV422
|
||||
- 47
|
||||
* - 480
|
||||
- 800
|
||||
- RGB888
|
||||
- YUV444
|
||||
- 74
|
||||
* - 480
|
||||
- 800
|
||||
- RGB888
|
||||
- YUV422
|
||||
- 108
|
||||
* - 480
|
||||
- 800
|
||||
- RGB888
|
||||
- YUV420
|
||||
- 126
|
||||
* - 480
|
||||
- 320
|
||||
- RGB888
|
||||
- YUV444
|
||||
- 173
|
||||
* - 480
|
||||
- 320
|
||||
- RGB888
|
||||
- YUV422
|
||||
- 241
|
||||
* - 480
|
||||
- 320
|
||||
- RGB888
|
||||
- YUV420
|
||||
- 273
|
||||
* - 720
|
||||
- 1280
|
||||
- GRAY
|
||||
- GRAY
|
||||
- 92
|
||||
|
||||
.. [#] Format of Original Image
|
||||
.. [#] Down sampling method
|
||||
|
||||
.. _jpeg-pixel-storage-layout:
|
||||
|
||||
Pixel Storage Layout for Different Color Formats
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The encoder and decoder described in this guide use the same uncompressed raw image formats (RGB, YUV). Therefore, the encoder and decoder are not discussed separately in this section. The pixel layout of the following formats applies to the input direction of the encoder and the output direction of the decoder (if supported). The specific pixel layout is shown in the following figure:
|
||||
|
||||
RGB888
|
||||
~~~~~~
|
||||
|
||||
In the following picture, each small block means one bit.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/jpeg/rgb888.png
|
||||
:align: center
|
||||
:alt: RGB888 pixel order
|
||||
|
||||
RGB888 pixel order
|
||||
|
||||
For RGB888, the order can be changed via :cpp:member:`jpeg_decode_cfg_t::rgb_order` sets the pixel to `RGB` order.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/jpeg/rgb888_bigendian.png
|
||||
:align: center
|
||||
:alt: RGB888 pixel big endian order
|
||||
|
||||
RGB888 pixel big endian order
|
||||
|
||||
RGB565
|
||||
~~~~~~
|
||||
|
||||
In the following picture, each small block means one bit.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/jpeg/rgb565.png
|
||||
:align: center
|
||||
:alt: RGB565 pixel order
|
||||
|
||||
RGB565 pixel order
|
||||
|
||||
For RGB565, the order can be changed via :cpp:member:`jpeg_decode_cfg_t::rgb_order` sets the pixel to `RGB` order.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/jpeg/rgb565_bigendian.png
|
||||
:align: center
|
||||
:alt: RGB565 pixel big endian order
|
||||
|
||||
RGB565 pixel big endian order
|
||||
|
||||
YUV444
|
||||
~~~~~~
|
||||
|
||||
In the following picture, each small block means one byte.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/jpeg/yuv444.png
|
||||
:align: center
|
||||
:alt: YUV444 pixel order
|
||||
|
||||
YUV444 pixel order
|
||||
|
||||
YUV422
|
||||
~~~~~~
|
||||
|
||||
In the following picture, each small block means one byte.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/jpeg/yuv422.png
|
||||
:align: center
|
||||
:alt: YUV422 pixel order
|
||||
|
||||
YUV422 pixel order
|
||||
|
||||
YUV420
|
||||
~~~~~~
|
||||
|
||||
In the following picture, each small block means one byte.
|
||||
|
||||
.. figure:: ../../../_static/diagrams/jpeg/yuv420.png
|
||||
:align: center
|
||||
:alt: YUV420 pixel order
|
||||
|
||||
YUV420 pixel order
|
||||
|
||||
.. _jpeg-thread-safety:
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The factory function :cpp:func:`jpeg_new_decoder_engine`, :cpp:func:`jpeg_decoder_get_info`, :cpp:func:`jpeg_decoder_process`, and :cpp:func:`jpeg_del_decoder_engine` are guaranteed to be thread safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks.
|
||||
|
||||
.. _jpeg-power-management:
|
||||
|
||||
Power Management
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
When power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is set), the system needs to adjust or stop the source clock of JPEG to enter Light-sleep, thus potentially changing the JPEG decoder or encoder process. This might lead to unexpected behavior in hardware calculation. To prevent such issues, entering Light-sleep is disabled for the time when JPEG encoder or decoder is working.
|
||||
|
||||
Whenever the user is decoding or encoding via JPEG (i.e., calling :cpp:func:`jpeg_encoder_process` or :cpp:func:`jpeg_decoder_process`), the driver guarantees that the power management lock is acquired by setting it to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_CPU_FREQ_MAX`. Once the encoding or decoding is finished, the driver releases the lock and the system can enter Light-sleep.
|
||||
|
||||
.. _jpeg-flash-encryption:
|
||||
|
||||
Usage Under Encryption
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The JPEG codec moves data via the 2D-DMA, and the JPEG codec **cannot process encrypted data**. Therefore, when PSRAM encryption is enabled, the JPEG input/output buffers must reside in an unencrypted memory region, otherwise encoding/decoding fails.
|
||||
|
||||
To support the encrypted scenario, the driver does the following:
|
||||
|
||||
- When ``CONFIG_SPIRAM_ENC_EXEMPT`` is enabled, :cpp:func:`jpeg_alloc_decoder_mem` and :cpp:func:`jpeg_alloc_encoder_mem` allocate buffers from the unencrypted PSRAM region (``MALLOC_CAP_SPIRAM_NO_ENC``) automatically.
|
||||
- The allocated buffers satisfy both the cache line alignment and the byte alignment required by the 2D-DMA.
|
||||
|
||||
Please note the following when using it:
|
||||
|
||||
1. It is recommended to always allocate buffers via :cpp:func:`jpeg_alloc_encoder_mem` / :cpp:func:`jpeg_alloc_decoder_mem` to ensure correct alignment and memory region.
|
||||
|
||||
2. The size of the unencrypted region is determined by ``CONFIG_SPIRAM_ENC_EXEMPT_SIZE``. Since the JPEG buffer size depends on the image resolution and cannot be predicted automatically, configure it according to the largest image you actually process. If the region is insufficient, the allocation fails and an error log is printed, suggesting to enlarge ``CONFIG_SPIRAM_ENC_EXEMPT_SIZE``. Also note that this value must not be greater than or equal to the actual PSRAM size, otherwise the unencrypted region is disabled.
|
||||
|
||||
.. _jpeg-kconfig-options:
|
||||
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_JPEG_ENABLE_DEBUG_LOG` is used to enable the debug log at the cost of increased firmware binary size.
|
||||
|
||||
Maintainers' Notes
|
||||
------------------
|
||||
|
||||
The JPEG driver usage of hardware resources and its dependency status are shown in the following graph:
|
||||
|
||||
.. figure:: ../../../_static/diagrams/jpeg/jpeg_drv_file_structure.png
|
||||
:align: center
|
||||
:alt: JPEG driver files structure
|
||||
|
||||
JPEG driver file structure
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`peripherals/jpeg/jpeg_decode` demonstrates how to use the JPEG hardware decoder to parse one embedded JPEG, decode it into RGB888, stream the raw output as base64 over UART, and validate the result with pytest.
|
||||
|
||||
- :example:`peripherals/jpeg/jpeg_encode` demonstrates how to use the JPEG hardware encoder to encode an embedded 720p raw picture, stream the JPEG as base64 over UART, and validate the result with pytest.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. only:: SOC_JPEG_DECODE_SUPPORTED
|
||||
|
||||
.. include-build-file:: inc/jpeg_decode.inc
|
||||
|
||||
.. only:: SOC_JPEG_ENCODE_SUPPORTED
|
||||
|
||||
.. include-build-file:: inc/jpeg_encode.inc
|
||||
|
||||
.. include-build-file:: inc/components/esp_driver_jpeg/include/driver/jpeg_types.inc
|
||||
.. include-build-file:: inc/components/esp_hal_jpeg/include/hal/jpeg_types.inc
|
||||
@@ -0,0 +1,171 @@
|
||||
.. _key-manager:
|
||||
|
||||
Key Manager
|
||||
===========
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
The {IDF_TARGET_NAME}'s Key Manager peripheral provides hardware-assisted **key deployment and recovery** for cryptographic keys. It allows cryptographic keys to be provisioned and used without storing plaintext key material in flash, RAM, or eFuses.
|
||||
|
||||
The Key Manager is intended for applications that require secure handling of long-term cryptographic keys.
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
.. note::
|
||||
|
||||
The Key Manager peripheral is only supported on ESP32-P4 chip revision >= v3.0.
|
||||
|
||||
.. only:: esp32c5
|
||||
|
||||
.. note::
|
||||
|
||||
The Key Manager peripheral is only supported on ESP32-C5 chip revision >= v1.2.
|
||||
|
||||
Key Manager provides the following properties:
|
||||
|
||||
- **Device uniqueness**
|
||||
|
||||
Keys are cryptographically bound to a Hardware Unique Key (HUK) that is unique to each chip.
|
||||
|
||||
- **No plaintext key storage**
|
||||
|
||||
Key material is never exposed to software accessible memory.
|
||||
|
||||
- **Flexible key lifecycle**
|
||||
|
||||
Keys can be deployed, recovered, or replaced by a newer key without reprogramming the eFuses for each key.
|
||||
|
||||
- **Resistance to physical extraction**
|
||||
|
||||
Reading flash or eFuses contents would not reveal usable key material.
|
||||
|
||||
Hardware Unique Key (HUK)
|
||||
-------------------------
|
||||
|
||||
The Hardware Unique Key (HUK) is a device-specific unique key generated entirely in hardware HUK peripheral. It is generated using SRAM Physical Unclonable Function (PUF) and is reconstructed using the HUK recovery info stored in the key recovery info of a Key Manager deployed key. See **{IDF_TARGET_NAME} Technical Reference Manual** > **Chapter Key Manager** [`PDF <{IDF_TARGET_TRM_EN_URL}>`__] > **HUK Generator** for more details about the HUK peripheral.
|
||||
|
||||
The HUK acts as the root of trust for all keys deployed through the Key Manager.
|
||||
|
||||
Key Deployment and Key Recovery
|
||||
-------------------------------
|
||||
|
||||
The Key Manager operates in two distinct phases:
|
||||
|
||||
- **Key deployment**
|
||||
|
||||
A cryptographic key is generated or securely introduced into the chip, and it gets bound to the HUK. This step is usually performed during manufacturing, first boot up or when generating transient or persistent keys during the application runtime.
|
||||
|
||||
- **Key recovery**
|
||||
|
||||
On subsequent boots, a Key Manager-deployed persistent key is restored using the previously generated key recovery information, without exposing the key value.
|
||||
|
||||
During deployment, the Key Manager generates a data structure referred to as :cpp:type:`esp_key_mgr_key_recovery_info_t`. In case of persistent keys, the applications must store this data in non-volatile storage (for example, flash) in order to recover the key on later boots.
|
||||
|
||||
Supported Key Types
|
||||
-------------------
|
||||
|
||||
The Key Manager can manage keys for the following key types:
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_KEY_MANAGER_ECDSA_KEY_DEPLOY: - ECDSA
|
||||
:SOC_KEY_MANAGER_FE_KEY_DEPLOY: - Flash Encryption (XTS-AES)
|
||||
:SOC_KEY_MANAGER_HMAC_KEY_DEPLOY: - HMAC
|
||||
:SOC_KEY_MANAGER_DS_KEY_DEPLOY: - Digital Signature peripherals
|
||||
:SOC_KEY_MANAGER_FE_KEY_DEPLOY: - PSRAM Encryption
|
||||
|
||||
Each key is associated with a :cpp:type:`esp_key_mgr_key_purpose_t`, which defines how the key can be used by hardware peripherals.
|
||||
|
||||
Key Deployment Modes
|
||||
--------------------
|
||||
|
||||
The Key Manager provides multiple key deployment modes to support different provisioning and security requirements.
|
||||
|
||||
Random Deploy Mode
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In this mode, the Key Manager generates a random private key internally.
|
||||
|
||||
- The key value is never known to the application software.
|
||||
- No external key material is required.
|
||||
- Intended for use cases where the key does not need to be backed up or exported.
|
||||
|
||||
AES Deploy Mode
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
In this mode, a user-specified private key is securely deployed.
|
||||
|
||||
- The key is encrypted before being transmitted to the chip.
|
||||
- Auxiliary key material is used to protect the deployment process.
|
||||
- Intended for factory provisioning scenarios where the key value must be predefined.
|
||||
|
||||
ECDH0 Deploy Mode
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
In this mode, a private key is negotiated using Elliptic Curve Diffie-Hellman (ECDH).
|
||||
|
||||
- The final private key is never transmitted.
|
||||
- The deployment process can occur over an untrusted channel.
|
||||
- Intended for high-security provisioning environments.
|
||||
|
||||
ECDH1 Deploy Mode
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
This mode is similar to ECDH0 Deploy Mode, with additional flexibility for manufacturing workflows.
|
||||
|
||||
- Supports negotiated key deployment using auxiliary recovery data
|
||||
- Allows updating deployed keys by replacing auxiliary information
|
||||
- Intended for large-scale manufacturing with controlled trust assumptions
|
||||
|
||||
For detailed information various deployment modes, see **{IDF_TARGET_NAME} Technical Reference Manual** > **Chapter Key Manager** [`PDF <{IDF_TARGET_TRM_EN_URL}>`__] > **Section Key Manager**.
|
||||
|
||||
Typical Workflows
|
||||
-----------------
|
||||
|
||||
First Boot or Manufacturing
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A typical provisioning flow includes:
|
||||
|
||||
1. Generating the Hardware Unique Key (HUK)
|
||||
2. Deploying required cryptographic keys using an appropriate deployment mode
|
||||
3. Storing the generated ``key_recovery_info`` in non-volatile storage
|
||||
4. Locking relevant security configuration eFuses, if required
|
||||
|
||||
This process is usually performed once per device.
|
||||
|
||||
Normal Boot
|
||||
^^^^^^^^^^^
|
||||
|
||||
During a normal boot:
|
||||
|
||||
1. The application provides the previously generated and stored ``key_recovery_info`` of a Key Manager-deployed key.
|
||||
2. The HUK is reconstructed automatically by hardware.
|
||||
3. The Key Manager recovers the deployed key internally.
|
||||
4. Cryptographic peripherals can use the recovered key.
|
||||
|
||||
Security Considerations
|
||||
-----------------------
|
||||
|
||||
Applications using the Key Manager should consider the following:
|
||||
|
||||
- Protect the ``key_recovery_info`` of a Key Manager-deployed key against unauthorized modification or loss.
|
||||
- Lock Key Manager's security-related eFuses after successful key deployment to prevent re-deployment of a key of the same type.
|
||||
- Avoid deploying new XTS-AES keys when Flash Encryption is already enabled unless explicitly intended.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_key_mgr.inc
|
||||
.. include-build-file:: inc/key_mgr_types.inc
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
See :example:`security/key_manager` for an example demonstrating key deployment using the Key Manager and using the deployed key to perform signing operations.
|
||||
|
||||
This example shows how to:
|
||||
|
||||
- Initialize the Key Manager
|
||||
- Deploy keys using the AES deployment mode
|
||||
- Use the PSA interface to perform signing operations using the Key Manager deployed key
|
||||
@@ -0,0 +1,120 @@
|
||||
MIPI DSI Interfaced LCD
|
||||
=======================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
#. Create a DSI bus, and it will initialize the D-PHY as well.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
|
||||
esp_lcd_dsi_bus_config_t bus_config = {
|
||||
.bus_id = 0, // index from 0, specify the DSI host to use
|
||||
.num_data_lanes = 2, // Number of data lanes to use, can't set a value that exceeds the chip's capability
|
||||
.lane_bit_rate_mbps = EXAMPLE_MIPI_DSI_LANE_BITRATE_MBPS, // Bit rate of the data lanes, in Mbps
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
|
||||
|
||||
#. Derive the DBI interface from the DSI bus. The DBI interface mostly is used as the control IO layer in the esp_lcd component. This interface provides the functions to read or write the configuration registers inside the LCD device. In this step, you need to provide the following information:
|
||||
|
||||
- :cpp:member:`esp_lcd_dbi_io_config_t::virtual_channel` sets the virtual channel number to use. The virtual channel is a logical channel that is used to multiplex the data from different sources. If you only have one LCD connected, you can set this to ``0``.
|
||||
- :cpp:member:`esp_lcd_dbi_io_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_dbi_io_config_t::lcd_param_bits` set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
|
||||
esp_lcd_dbi_io_config_t dbi_config = {
|
||||
.virtual_channel = 0,
|
||||
.lcd_cmd_bits = 8, // according to the LCD spec
|
||||
.lcd_param_bits = 8, // according to the LCD spec
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
|
||||
|
||||
#. Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the MIPI DBI IO handle that allocated in the last step, and some panel specific configurations:
|
||||
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of each pixel. The LCD driver uses this value to calculate the number of bytes to send to the LCD controller chip.
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` sets the GPIO number of the reset pin. If the LCD controller chip does not have a reset pin, you can set this value to ``-1``.
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::rgb_ele_order` sets the RGB element order of the pixel data, it can be **RGB** or **BGR**.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_handle_t ili9881c_ctrl_panel = NULL;
|
||||
esp_lcd_panel_dev_config_t lcd_dev_config = {
|
||||
.bits_per_pixel = 24, // MIPI LCD usually uses 24 bit (i.e., RGB888) per pixel
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||
.reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9881c(mipi_dbi_io, &lcd_dev_config, &ili9881c_ctrl_panel));
|
||||
|
||||
#. With the LCD control panel that returned in the last step, you can reset the LCD device followed by a basic initialization. After that, you can turn on the display.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(ili9881c_ctrl_panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(ili9881c_ctrl_panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(ili9881c_ctrl_panel, true));
|
||||
|
||||
#. However, you still can't send pixel data to the MIPI LCD with the control panel, because MIPI LCD has a high resolution and there's no GRAM in the LCD controller. We need to maintain the LCD frame buffer and flush it to the LCD via the MIPI DSI DPI interface. To allocate a DPI data panel, you need to provide many essential parameters, including the DPI clock frequency, the pixel format, the video timing, and so on.
|
||||
|
||||
- :cpp:member:`esp_lcd_dpi_panel_config_t::virtual_channel` sets the virtual channel number to use. Like the DBI interface, we also need to set the virtual channel for the DPI interface. If you only have one LCD connected, you can set this to ``0``.
|
||||
- :cpp:member:`esp_lcd_dpi_panel_config_t::dpi_clk_src` sets the clock source for the DPI interface. The available clock sources are listed in :cpp:type:`mipi_dsi_dpi_clock_source_t`.
|
||||
- :cpp:member:`esp_lcd_dpi_panel_config_t::dpi_clock_freq_mhz` sets the DPI clock frequency in MHz. Higher pixel clock frequency results in higher refresh rate, but may cause flickering if the DMA bandwidth is not sufficient or the LCD controller chip does not support high pixel clock frequency.
|
||||
- :cpp:member:`esp_lcd_dpi_panel_config_t::in_color_format` sets the pixel format of the input pixel data. The available pixel formats are listed in :cpp:type:`lcd_color_format_t`. We usually use **RGB888** for MIPI LCD to get the best color depth.
|
||||
- :cpp:member:`esp_lcd_dpi_panel_config_t::video_timing` sets the LCD panel specific timing parameters. All required parameters are listed in the :cpp:type:`esp_lcd_video_timing_t`, including the LCD resolution and blanking porches. Please fill them according to the datasheet of your LCD.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_handle_t mipi_dpi_panel = NULL;
|
||||
esp_lcd_dpi_panel_config_t dpi_config = {
|
||||
.virtual_channel = 0,
|
||||
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
|
||||
.dpi_clock_freq_mhz = 1 * 1000,
|
||||
.in_color_format = LCD_COLOR_FMT_RGB888,
|
||||
.video_timing = {
|
||||
.h_size = EXAMPLE_MIPI_DSI_LCD_H_RES,
|
||||
.v_size = EXAMPLE_MIPI_DSI_LCD_V_RES,
|
||||
.hsync_back_porch = EXAMPLE_MIPI_DSI_LCD_HBP,
|
||||
.hsync_pulse_width = EXAMPLE_MIPI_DSI_LCD_HSYNC,
|
||||
.hsync_front_porch = EXAMPLE_MIPI_DSI_LCD_HFP,
|
||||
.vsync_back_porch = EXAMPLE_MIPI_DSI_LCD_VBP,
|
||||
.vsync_pulse_width = EXAMPLE_MIPI_DSI_LCD_VSYNC,
|
||||
.vsync_front_porch = EXAMPLE_MIPI_DSI_LCD_VFP,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_dpi(mipi_dsi_bus, &dpi_config, &mipi_dpi_panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(mipi_dpi_panel));
|
||||
|
||||
#. Configure draw bitmap hook function (optional)
|
||||
|
||||
If you want to use DMA2D to implement draw bitmap, the driver has already implemented the DMA2D draw bitmap hook function, you only need to call :func:`esp_lcd_dpi_panel_enable_dma2d` to enable it.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_dpi_panel_enable_dma2d(mipi_dpi_panel));
|
||||
|
||||
.. note::
|
||||
|
||||
Due to hardware limitation, if external memory encryption is enabled, DMA2D can only access address and length that are aligned to {IDF_TARGET_SOC_MEMSPI_ENCRYPTION_ALIGNMENT} bytes. You need to ensure that your draw buffer's address and length are aligned to {IDF_TARGET_SOC_MEMSPI_ENCRYPTION_ALIGNMENT} bytes. :example:`peripherals/lcd/mipi_dsi` shows how to use LVGL to constrain the redrawn area to ensure alignment.
|
||||
|
||||
If you need more advanced applications, you can add a custom hook for draw bitmap, such as using PPA to implement rotation, scaling, etc.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_hooks_t hooks = {
|
||||
.draw_bitmap_hook = custom_draw_bitmap_hook,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_hooks(mipi_dpi_panel, &hooks, &user_ctx));
|
||||
|
||||
Power Supply for MIPI DPHY
|
||||
--------------------------
|
||||
|
||||
The MIPI DPHY on {IDF_TARGET_NAME} requires a dedicated 2.5V power supply. Please refer to your schematic and ensure that the power pin (often labeled ``VDD_MIPI_DPHY``) is properly connected to a 2.5V power source before using the MIPI DSI driver.
|
||||
|
||||
.. only:: SOC_GP_LDO_SUPPORTED
|
||||
|
||||
On {IDF_TARGET_NAME}, the MIPI DPHY can be powered by the internal adjustable LDO. Connect the output pin of the LDO channel to the MIPI DPHY power pin. Before initializing the DSI driver, use the API provided in :doc:`/api-reference/peripherals/ldo_regulator` to configure the LDO output voltage to 2.5V.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_lcd_mipi_dsi.inc
|
||||
@@ -0,0 +1,58 @@
|
||||
I2C Interfaced LCD
|
||||
==================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
#. Create I2C bus. Please refer to :doc:`I2C API doc </api-reference/peripherals/i2c>` for more details.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
i2c_master_bus_handle_t i2c_bus = NULL;
|
||||
i2c_master_bus_config_t bus_config = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.i2c_port = I2C_BUS_PORT,
|
||||
.sda_io_num = EXAMPLE_PIN_NUM_SDA,
|
||||
.scl_io_num = EXAMPLE_PIN_NUM_SCL,
|
||||
.flags.enable_internal_pullup = true,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &i2c_bus));
|
||||
|
||||
#. Allocate an LCD IO device handle from the I2C bus. In this step, you need to provide the following information:
|
||||
|
||||
- :cpp:member:`esp_lcd_panel_io_i2c_config_t::dev_addr` sets the I2C device address of the LCD controller chip. The LCD driver uses this address to communicate with the LCD controller chip.
|
||||
- :cpp:member:`esp_lcd_panel_io_i2c_config_t::scl_speed_hz` sets the I2C clock frequency in Hz. The value should not exceed the range recommended in the LCD spec.
|
||||
- :cpp:member:`esp_lcd_panel_io_i2c_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_panel_io_i2c_config_t::lcd_param_bits` set the bit width of the command and parameter recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
|
||||
- :cpp:member:`esp_lcd_panel_io_i2c_config_t::transaction_timeout_ms` sets the timeout (in milliseconds) for each underlying I2C transfer. Setting this to 0 or -1 means to wait indefinitely. If a positive value is specified, panel IO calls will return ``ESP_ERR_TIMEOUT`` when the timeout is reached. This is useful for cases like shared buses or when a slave device could potentially hang the bus.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
esp_lcd_panel_io_i2c_config_t io_config = {
|
||||
.dev_addr = EXAMPLE_I2C_HW_ADDR,
|
||||
.scl_speed_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
|
||||
.control_phase_bytes = 1, // refer to LCD spec
|
||||
.dc_bit_offset = 6, // refer to LCD spec
|
||||
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
|
||||
.lcd_param_bits = EXAMPLE_LCD_CMD_BITS,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus, &io_config, &io_handle));
|
||||
|
||||
#. Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the I2C IO device handle that allocated in the last step, and some panel specific configurations:
|
||||
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` sets the LCD's hardware reset GPIO number. If the LCD does not have a hardware reset pin, set this to ``-1``.
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver uses this value to calculate the number of bytes to send to the LCD controller chip.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_panel_dev_config_t panel_config = {
|
||||
.bits_per_pixel = 1,
|
||||
.reset_gpio_num = EXAMPLE_PIN_NUM_RST,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(io_handle, &panel_config, &panel_handle));
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_lcd_io_i2c.inc
|
||||
@@ -0,0 +1,88 @@
|
||||
I80 Interfaced LCD
|
||||
==================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
#. Create I80 bus by :cpp:func:`esp_lcd_new_i80_bus`. You need to set up the following parameters for an Intel 8080 parallel bus:
|
||||
|
||||
- :cpp:member:`esp_lcd_i80_bus_config_t::clk_src` sets the clock source of the I80 bus. Note, the default clock source may be different between ESP targets.
|
||||
- :cpp:member:`esp_lcd_i80_bus_config_t::wr_gpio_num` sets the GPIO number of the pixel clock (also referred as ``WR`` in some LCD spec)
|
||||
- :cpp:member:`esp_lcd_i80_bus_config_t::dc_gpio_num` sets the GPIO number of the data or command select pin (also referred as ``RS`` in some LCD spec)
|
||||
- :cpp:member:`esp_lcd_i80_bus_config_t::bus_width` sets the bit width of the data bus (only support ``8`` or ``16``)
|
||||
- :cpp:member:`esp_lcd_i80_bus_config_t::data_gpio_nums` is the array of the GPIO number of the data bus. The number of GPIOs should be equal to the :cpp:member:`esp_lcd_i80_bus_config_t::bus_width` value.
|
||||
- :cpp:member:`esp_lcd_i80_bus_config_t::max_transfer_bytes` sets the maximum number of bytes that can be transferred in one transaction.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_i80_bus_handle_t i80_bus = NULL;
|
||||
esp_lcd_i80_bus_config_t bus_config = {
|
||||
.clk_src = LCD_CLK_SRC_DEFAULT,
|
||||
.dc_gpio_num = EXAMPLE_PIN_NUM_DC,
|
||||
.wr_gpio_num = EXAMPLE_PIN_NUM_PCLK,
|
||||
.data_gpio_nums = {
|
||||
EXAMPLE_PIN_NUM_DATA0,
|
||||
EXAMPLE_PIN_NUM_DATA1,
|
||||
EXAMPLE_PIN_NUM_DATA2,
|
||||
EXAMPLE_PIN_NUM_DATA3,
|
||||
EXAMPLE_PIN_NUM_DATA4,
|
||||
EXAMPLE_PIN_NUM_DATA5,
|
||||
EXAMPLE_PIN_NUM_DATA6,
|
||||
EXAMPLE_PIN_NUM_DATA7,
|
||||
},
|
||||
.bus_width = 8,
|
||||
.max_transfer_bytes = EXAMPLE_LCD_H_RES * 100 * sizeof(uint16_t), // transfer 100 lines of pixels (assume pixel is RGB565) at most in one transaction
|
||||
.dma_burst_size = EXAMPLE_DMA_BURST_SIZE,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_i80_bus(&bus_config, &i80_bus));
|
||||
|
||||
#. Allocate an LCD IO device handle from the I80 bus. In this step, you need to provide the following information:
|
||||
|
||||
- :cpp:member:`esp_lcd_panel_io_i80_config_t::cs_gpio_num` sets the GPIO number of the chip select pin.
|
||||
- :cpp:member:`esp_lcd_panel_io_i80_config_t::pclk_hz` sets the pixel clock frequency in Hz. Higher pixel clock frequency results in higher refresh rate, but may cause flickering if the DMA bandwidth is not sufficient or the LCD controller chip does not support high pixel clock frequency.
|
||||
- :cpp:member:`esp_lcd_panel_io_i80_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_panel_io_i80_config_t::lcd_param_bits` set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
|
||||
- :cpp:member:`esp_lcd_panel_io_i80_config_t::trans_queue_depth` sets the maximum number of transactions that can be queued in the LCD IO device. A bigger value means more transactions can be queued up, but it also consumes more memory.
|
||||
|
||||
.. note::
|
||||
|
||||
The output pixel clock PCLK frequency has an upper limit that depends on the data bus width:
|
||||
|
||||
- When :cpp:member:`esp_lcd_i80_bus_config_t::bus_width` is 8, the PCLK frequency is recommended to be less than 80 MHz.
|
||||
- When :cpp:member:`esp_lcd_i80_bus_config_t::bus_width` is 16, the PCLK frequency is recommended to be less than 40 MHz.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
esp_lcd_panel_io_i80_config_t io_config = {
|
||||
.cs_gpio_num = EXAMPLE_PIN_NUM_CS,
|
||||
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
|
||||
.trans_queue_depth = 10,
|
||||
.dc_levels = {
|
||||
.dc_idle_level = 0,
|
||||
.dc_cmd_level = 0,
|
||||
.dc_dummy_level = 0,
|
||||
.dc_data_level = 1,
|
||||
},
|
||||
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
|
||||
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle));
|
||||
|
||||
#. Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the I80 IO device handle that allocated in the last step, and some panel specific configurations:
|
||||
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver uses this value to calculate the number of bytes to send to the LCD controller chip.
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` sets the GPIO number of the reset pin. If the LCD controller chip does not have a reset pin, you can set this value to ``-1``.
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::rgb_ele_order` sets the color order the pixel color data.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = EXAMPLE_PIN_NUM_RST,
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||
.bits_per_pixel = 16,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_lcd_io_i80.inc
|
||||
@@ -0,0 +1,108 @@
|
||||
LCD
|
||||
===
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
ESP chips can generate various kinds of timings needed by common LCDs on the market, like SPI LCD, I2C LCD, Parallel LCD (Intel 8080), RGB/SRGB LCD, MIPI DSI LCD, etc. The ``esp_lcd`` component offers an abstracted driver framework to support them in an unified way.
|
||||
|
||||
An LCD typically consists of two main planes:
|
||||
|
||||
* **Control Plane**: This plane allows us to read and write to the internal registers of the LCD device controller. Host typically uses this plane for tasks such as initializing the LCD power supply and performing gamma calibration.
|
||||
* **Data Plane**: The data plane is responsible for transmitting pixel data to the LCD device.
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
In the context of ``esp_lcd``, both the data plane and the control plane are represented by the :cpp:type:`esp_lcd_panel_handle_t` type.
|
||||
|
||||
On some LCDs, these two planes may be combined into a single plane. In this configuration, pixel data is transmitted through the control plane, achieving functionality similar to that of the data plane. This merging is common in SPI LCDs and I2C LCDs.
|
||||
|
||||
Additionally, there are LCDs that do not require a separate control plane. For instance, certain RGB LCDs automatically execute necessary initialization procedures after power-up. Host devices only need to continuously refresh pixel data through the data plane. However, it's essential to note that not all RGB LCDs eliminate the control plane entirely. Some LCD devices can simultaneously support multiple interfaces, requiring the Host to send specific commands via the control plane (such as those based on the SPI interface) to enable the RGB mode.
|
||||
|
||||
This document will discuss how to create the control plane and data plane, as mentioned earlier, based on different types of LCDs.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
:SOC_GPSPI_SUPPORTED: spi_lcd
|
||||
:SOC_I2C_SUPPORTED: i2c_lcd
|
||||
:SOC_LCD_I80_SUPPORTED: i80_lcd
|
||||
:SOC_LCD_RGB_SUPPORTED: rgb_lcd
|
||||
:SOC_MIPI_DSI_SUPPORTED: dsi_lcd
|
||||
:SOC_PARLIO_LCD_SUPPORTED: parl_lcd
|
||||
|
||||
.. note::
|
||||
|
||||
ESP-IDF provides only a limited number of LCD device controller drivers out of the box (e.g., ST7789). More drivers are available in the `ESP Component Registry <https://components.espressif.com/components?q=esp_lcd>`__.
|
||||
|
||||
LCD Control Panel Operations
|
||||
----------------------------
|
||||
|
||||
* :cpp:func:`esp_lcd_panel_reset` can reset the LCD control panel.
|
||||
* :cpp:func:`esp_lcd_panel_init` performs a basic initialization of the control panel. To perform more manufacturer specific initialization, please refer to :ref:`steps_add_manufacture_init`.
|
||||
* By combining using :cpp:func:`esp_lcd_panel_swap_xy` and :cpp:func:`esp_lcd_panel_mirror`, you can achieve the functionality of rotating or mirroring the LCD screen.
|
||||
* :cpp:func:`esp_lcd_panel_disp_on_off` can turn on or off the LCD screen by cutting down the output path from the frame buffer to the LCD screen. Please note, this is not controlling the LCD backlight. Backlight control is not covered by the ``esp_lcd`` driver.
|
||||
* :cpp:func:`esp_lcd_panel_disp_sleep` can reduce the power consumption of the LCD screen by entering the sleep mode. The internal frame buffer is still retained.
|
||||
|
||||
LCD Data Panel Operations
|
||||
-------------------------
|
||||
|
||||
* :cpp:func:`esp_lcd_panel_reset` can reset the LCD data panel.
|
||||
* :cpp:func:`esp_lcd_panel_init` performs a basic initialization of the data panel.
|
||||
* :cpp:func:`esp_lcd_panel_draw_bitmap` is the function which does the magic to flush the user draw buffer to the LCD screen, where the target draw window is configurable. Please note, this function expects that the draw buffer is a 1-D array and there's no stride in between each lines.
|
||||
* :cpp:func:`esp_lcd_panel_draw_bitmap_2d` is the function which does the magic to flush the user draw buffer to the LCD screen, where the source and target draw windows are configurable. Please note, the draw buffer can be a 2-D array or a 1-D array with no stride in between each lines.
|
||||
|
||||
Advanced Frame Buffer Debugging
|
||||
-------------------------------
|
||||
|
||||
For advanced debugging, ESP-IDF can load the ``idf-drivers-gdb`` Python package in a GDB session. This package provides the ``framebuffer_display`` command, which reads LCD frame buffer memory from the target and renders it on the host side.
|
||||
|
||||
This is useful when the panel does not show the expected image and you need to check whether the frame buffer content is already wrong, or whether the problem is in the LCD interface timing, GPIO routing, panel initialization, or color format configuration.
|
||||
|
||||
For installation steps, supported pixel formats, command syntax, examples, and troubleshooting tips, see the `idf-drivers-gdb package documentation <https://pypi.org/project/idf-drivers-gdb/>`__.
|
||||
|
||||
.. _steps_add_manufacture_init:
|
||||
|
||||
Steps to Add Manufacturer Specific Initialization
|
||||
-------------------------------------------------
|
||||
|
||||
The LCD controller drivers (e.g., st7789) in ESP-IDF only provide basic initialization in the :cpp:func:`esp_lcd_panel_init`, leaving the vast majority of settings to the default values. Some LCD modules need to set a bunch of manufacturer specific configurations before it can display normally. These configurations usually include gamma, power voltage and so on. If you want to add manufacturer specific initialization, please follow the steps below:
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_lcd_panel_reset(panel_handle);
|
||||
esp_lcd_panel_init(panel_handle);
|
||||
// set extra configurations e.g., gamma control
|
||||
// with the underlying IO handle
|
||||
// please consult your manufacturer for special commands and corresponding values
|
||||
esp_lcd_panel_io_tx_param(io_handle, GAMMA_CMD, (uint8_t[]) {
|
||||
GAMMA_ARRAY
|
||||
}, N);
|
||||
// turn on the display
|
||||
esp_lcd_panel_disp_on_off(panel_handle, true);
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
.. list::
|
||||
|
||||
* :example:`peripherals/lcd/tjpgd` shows how to decode a JPEG image and display it on an SPI-interfaced LCD, and rotate the image periodically.
|
||||
:SOC_GPSPI_SUPPORTED: * :example:`peripherals/lcd/spi_lcd_touch` demonstrates how to drive the LCD and touch panel on the same SPI bus, and display a simple GUI using the LVGL library.
|
||||
:SOC_LCD_I80_SUPPORTED: * :example:`peripherals/lcd/i80_controller` demonstrates how to port the LVGL library onto the `esp_lcd` driver layer to create GUIs.
|
||||
:SOC_LCD_RGB_SUPPORTED: * :example:`peripherals/lcd/rgb_panel` demonstrates how to install an RGB panel driver, display a scatter chart on the screen based on the LVGL library.
|
||||
:SOC_I2C_SUPPORTED: * :example:`peripherals/lcd/i2c_oled` demonstrates how to use the SSD1306 panel driver from the `esp_lcd` component to facilitate the porting of LVGL library and display a scrolling text on the OLED screen.
|
||||
:SOC_MIPI_DSI_SUPPORTED: * :example:`peripherals/lcd/mipi_dsi` demonstrates the general process of installing a MIPI DSI LCD driver, and displays a LVGL widget on the screen.
|
||||
:SOC_PARLIO_LCD_SUPPORTED: * :example:`peripherals/lcd/parlio_simulate` demonstrates how to use Parallel IO peripheral to drive an SPI or I80 Interfaced LCD.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/lcd_types.inc
|
||||
.. include-build-file:: inc/esp_lcd_types.inc
|
||||
.. include-build-file:: inc/esp_lcd_panel_io.inc
|
||||
.. include-build-file:: inc/esp_lcd_panel_ops.inc
|
||||
.. include-build-file:: inc/esp_lcd_panel_vendor.inc
|
||||
@@ -0,0 +1,76 @@
|
||||
Parallel IO simulation of SPI or I80 Interfaced LCD
|
||||
===================================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Parallel IO is not a bus-type peripheral. The driver directly creates a Parallel IO device for the LCD. Currently the driver supports SPI (1 bit data width) and I80 (8 bit data width) modes.
|
||||
|
||||
#. Create Parallel IO device by :cpp:func:`esp_lcd_new_panel_io_parl`. You need to set up the following parameters for a Parallel IO device:
|
||||
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::clk_src` sets the clock source of the Parallel IO device. Note, the default clock source may be different between ESP targets.
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::clk_gpio_num` sets the GPIO number of the pixel clock (also referred as ``WR`` or ``SCLK`` in some LCD spec)
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::dc_gpio_num` sets the GPIO number of the data or command select pin (also referred as ``RS`` in some LCD spec)
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::cs_gpio_num` sets the GPIO number of the chip select pin. (Note that the Parallel IO LCD driver only supports a single LCD device).
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::data_width` sets the bit width of the data bus (only support ``1`` or ``8``)
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::data_gpio_nums` is the array of the GPIO number of the data bus. The number of GPIOs should be equal to the :cpp:member:`esp_lcd_panel_io_parl_config_t::data_width` value.
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::max_transfer_bytes` sets the maximum number of bytes that can be transferred in one transaction.
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::dma_burst_size` sets the number of bytes transferred by dma burst.
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::pclk_hz` sets the pixel clock frequency in Hz. Higher pixel clock frequency results in higher refresh rate, but may cause display abnormalities if the DMA bandwidth is not sufficient or the LCD controller chip does not support high pixel clock frequency.
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::dc_levels` sets the effective level for DC data selection and command selection.
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_panel_io_parl_config_t::lcd_param_bits` set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::trans_queue_depth` sets the maximum number of transactions that can be queued in the Parallel IO device. A bigger value means more transactions can be queued up, but it also consumes more memory.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
esp_lcd_panel_io_parl_config_t io_config = {
|
||||
.clk_src = PARLIO_CLK_SRC_DEFAULT,
|
||||
.dc_gpio_num = EXAMPLE_PIN_NUM_DC,
|
||||
.clk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
|
||||
.data_gpio_nums = {
|
||||
EXAMPLE_PIN_NUM_DATA0, // set DATA0 to drive SPI interfaced LCD or set DATA0~7 to drive I80 interfaced LCD
|
||||
},
|
||||
.data_width = 1, // set 1 to drive SPI interfaced LCD or set 8 to drive I80 interfaced LCD
|
||||
.max_transfer_bytes = EXAMPLE_LCD_H_RES * 100 * sizeof(uint16_t), // transfer 100 lines of pixels (assume pixel is RGB565) at most in one transaction
|
||||
.dma_burst_size = EXAMPLE_DMA_BURST_SIZE,
|
||||
.cs_gpio_num = EXAMPLE_PIN_NUM_CS,
|
||||
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
|
||||
.trans_queue_depth = 10,
|
||||
.dc_levels = {
|
||||
.dc_cmd_level = 0,
|
||||
.dc_data_level = 1,
|
||||
},
|
||||
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
|
||||
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_parl(&io_config, io_handle));
|
||||
|
||||
.. only:: not SOC_PARLIO_SUPPORT_I80_LCD
|
||||
|
||||
.. note::
|
||||
|
||||
Due to hardware limitations, {IDF_TARGET_NAME} can not drive I80 interfaced LCD by Parallel IO.
|
||||
|
||||
#. Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the Parallel IO device handle that allocated in the last step, and some panel specific configurations:
|
||||
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` sets the LCD's hardware reset GPIO number. If the LCD does not have a hardware reset pin, set this to ``-1``.
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::rgb_ele_order` sets the RGB element order of each color data.
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver uses this value to calculate the number of bytes to send to the LCD controller chip.
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::data_endian` specifies the data endian to be transmitted to the screen. No need to specify for color data within one byte, like RGB232. For drivers that do not support specifying data endian, this field would be ignored.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = EXAMPLE_PIN_NUM_RST,
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
|
||||
.bits_per_pixel = 16,
|
||||
};
|
||||
// Create LCD panel handle for ST7789, with the Parallel IO device handle
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_lcd_io_parl.inc
|
||||
@@ -0,0 +1,316 @@
|
||||
RGB Interfaced LCD
|
||||
==================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
RGB LCD panel is created by :cpp:func:`esp_lcd_new_rgb_panel`, with various configurations specified in :cpp:type:`esp_lcd_rgb_panel_config_t`.
|
||||
|
||||
- :cpp:member:`esp_lcd_rgb_panel_config_t::clk_src` selects the clock source of the RGB LCD controller. The available clock sources are listed in :cpp:type:`lcd_clock_source_t`.
|
||||
- :cpp:member:`esp_lcd_rgb_panel_config_t::data_width` sets number of data lines consumed by the RGB interface. It can be 8/16/24.
|
||||
- :cpp:member:`esp_lcd_rgb_panel_config_t::bits_per_pixel` specifies the number of bits per pixel. This differs from :cpp:member:`esp_lcd_rgb_panel_config_t::data_width`. By default, if this field is set to 0, the driver will automatically match the bpp to the value set in :cpp:member:`esp_lcd_rgb_panel_config_t::data_width`. However, in some scenarios, these values need to be different. For instance, a serial RGB interfaced LCD might only require ``8`` data lines, but the color depth could be ``RGB888``, meaning :cpp:member:`esp_lcd_rgb_panel_config_t::bits_per_pixel` should be set to ``24``.
|
||||
- :cpp:member:`esp_lcd_rgb_panel_config_t::hsync_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::vsync_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::de_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::pclk_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::disp_gpio_num` and :cpp:member:`esp_lcd_rgb_panel_config_t::data_gpio_nums` are GPIO pins consumed by the RGB LCD controller. If any of them are not used, please set them to ``-1``.
|
||||
- :cpp:member:`esp_lcd_rgb_panel_config_t::dma_burst_size` specifies the size of the DMA transfer burst. Ensure this value is a power of 2.
|
||||
- :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px` specifies the size of the bounce buffer. This is required only for the "bounce buffer" mode. For more details, see :ref:`bounce_buffer_with_single_psram_frame_buffer`.
|
||||
- :cpp:member:`esp_lcd_rgb_panel_config_t::timings` specifies the timing parameters unique to the LCD panel. These parameters, detailed in :cpp:type:`esp_lcd_rgb_timing_t`, include the LCD resolution and blanking porches. Ensure they are set according to your LCD's datasheet.
|
||||
- :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` determines if the frame buffer should be allocated from PSRAM. For further details, see :ref:`single_frame_buffer_in_psram`.
|
||||
- :cpp:member:`esp_lcd_rgb_panel_config_t::num_fbs` specifies how many frame buffers the driver should allocate. For backward compatibility, setting this to ``0`` will allocate a single frame buffer. If you don't want to allocate any frame buffer, use :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` instead.
|
||||
- :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` determines whether frame buffer will be allocated. When it is set, no frame buffer will be allocated. This is also called the :ref:`bounce_buffer_only` mode.
|
||||
|
||||
.. note::
|
||||
|
||||
- When :cpp:member:`esp_lcd_rgb_panel_config_t::data_width` is 8:
|
||||
|
||||
- The PCLK frequency is recommended to be less than 80 MHz.
|
||||
- If YUV-RGB format conversion is also configured via :cpp:func:`esp_lcd_rgb_panel_set_yuv_conversion`, the PCLK frequency is recommended to be less than 60 MHz.
|
||||
|
||||
- When :cpp:member:`esp_lcd_rgb_panel_config_t::data_width` is 16:
|
||||
|
||||
- The PCLK frequency is recommended to be less than 40 MHz.
|
||||
- If YUV-RGB format conversion is also configured, the PCLK frequency is recommended to be less than 30 MHz.
|
||||
|
||||
GPIO Matrix and IOMUX Pins
|
||||
--------------------------
|
||||
|
||||
.. only:: esp32s31
|
||||
|
||||
On {IDF_TARGET_NAME}, the RGB LCD driver can use IOMUX automatically when all required signals are mapped to dedicated pins:
|
||||
|
||||
- If ``data_gpio_nums``, ``hsync_gpio_num``, ``vsync_gpio_num``, ``pclk_gpio_num``, and ``de_gpio_num`` all match their dedicated IOMUX pins, the driver bypasses the GPIO matrix automatically.
|
||||
- If any one of these signals does not match the dedicated IOMUX pin, the driver falls back to GPIO matrix routing automatically.
|
||||
|
||||
For higher pixel clock configurations, using IOMUX pins is recommended for better timing robustness.
|
||||
|
||||
Dedicated RGB IOMUX pins on {IDF_TARGET_NAME} are listed below:
|
||||
|
||||
- When ``data_width = 8``, ``DATA[0:7]`` is used
|
||||
- When ``data_width = 16``, ``DATA[0:15]`` is used
|
||||
- When ``data_width = 24``, ``DATA[0:23]`` is used
|
||||
|
||||
.. list-table::
|
||||
:widths: 35 65
|
||||
:header-rows: 1
|
||||
|
||||
* - Signal
|
||||
- GPIO
|
||||
* - DATA[0:7]
|
||||
- 8, 9, 10, 11, 12, 13, 14, 15
|
||||
* - DATA[8:15]
|
||||
- 16, 17, 18, 19, 33, 34, 35, 36
|
||||
* - DATA[16:23]
|
||||
- 37, 38, 39, 2, 3, 4, 5, 7
|
||||
* - HSYNC
|
||||
- 44
|
||||
* - VSYNC
|
||||
- 45
|
||||
* - PCLK
|
||||
- 40
|
||||
* - DE
|
||||
- 43
|
||||
|
||||
.. only:: not esp32s31
|
||||
|
||||
On {IDF_TARGET_NAME}, RGB signals are routed through the GPIO matrix.
|
||||
|
||||
RGB LCD Frame Buffer Operation Modes
|
||||
------------------------------------
|
||||
|
||||
Most of the time, the RGB LCD driver should maintain at least one screen sized frame buffer. According to the number and location of the frame buffer, the driver provides several different buffer modes.
|
||||
|
||||
Single Frame Buffer in Internal Memory
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This is the default and simplest and you do not have to specify flags or bounce buffer options. A frame buffer is allocated from the internal memory. The frame data is read out by DMA to the LCD verbatim. It needs no CPU intervention to function, but it has the downside that it uses up a fair bit of the limited amount of internal memory.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_rgb_panel_config_t panel_config = {
|
||||
.data_width = 16, // RGB565 in parallel mode, thus 16 bits in width
|
||||
.clk_src = LCD_CLK_SRC_DEFAULT,
|
||||
.disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
|
||||
.pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
|
||||
.vsync_gpio_num = EXAMPLE_PIN_NUM_VSYNC,
|
||||
.hsync_gpio_num = EXAMPLE_PIN_NUM_HSYNC,
|
||||
.de_gpio_num = EXAMPLE_PIN_NUM_DE,
|
||||
.data_gpio_nums = {
|
||||
EXAMPLE_PIN_NUM_DATA0,
|
||||
EXAMPLE_PIN_NUM_DATA1,
|
||||
EXAMPLE_PIN_NUM_DATA2,
|
||||
// other GPIOs
|
||||
// The number of GPIOs here should be the same to the value of "data_width" above
|
||||
...
|
||||
},
|
||||
// The timing parameters should refer to your LCD spec
|
||||
.timings = {
|
||||
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
|
||||
.h_res = EXAMPLE_LCD_H_RES,
|
||||
.v_res = EXAMPLE_LCD_V_RES,
|
||||
.hsync_back_porch = 40,
|
||||
.hsync_front_porch = 20,
|
||||
.hsync_pulse_width = 1,
|
||||
.vsync_back_porch = 8,
|
||||
.vsync_front_porch = 4,
|
||||
.vsync_pulse_width = 1,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
|
||||
|
||||
.. _single_frame_buffer_in_psram:
|
||||
|
||||
Single Frame Buffer in PSRAM
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have PSRAM and prefer to store the frame buffer there instead of using the limited internal memory, the LCD peripheral can utilize EDMA to fetch frame data directly from PSRAM, bypassing the internal cache. This can be enabled by setting :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` to ``true``. The trade-off is that when both the CPU and EDMA need access to PSRAM, the bandwidth is **shared** between them, meaning EDMA and the CPU each get half. If other peripherals are also using EDMA, a high pixel clock might cause LCD peripheral starvation, leading to display corruption. However, with a sufficiently low pixel clock, this approach minimizes CPU intervention.
|
||||
|
||||
.. only:: esp32s3
|
||||
|
||||
The PSRAM shares the same SPI bus with the main flash (the one stores your firmware binary). At any given time, there can only be one consumer of the SPI bus. When you also use the main flash to serve your file system (e.g., :doc:`SPIFFS </api-reference/storage/spiffs>`), the bandwidth of the underlying SPI bus will also be shared, leading to display corruption. You can use :cpp:func:`esp_lcd_rgb_panel_set_pclk` to update the pixel clock frequency to a lower value.
|
||||
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_rgb_panel_config_t panel_config = {
|
||||
.data_width = 16, // RGB565 in parallel mode, thus 16 bits in width
|
||||
.clk_src = LCD_CLK_SRC_DEFAULT,
|
||||
.disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
|
||||
.pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
|
||||
.vsync_gpio_num = EXAMPLE_PIN_NUM_VSYNC,
|
||||
.hsync_gpio_num = EXAMPLE_PIN_NUM_HSYNC,
|
||||
.de_gpio_num = EXAMPLE_PIN_NUM_DE,
|
||||
.data_gpio_nums = {
|
||||
EXAMPLE_PIN_NUM_DATA0,
|
||||
EXAMPLE_PIN_NUM_DATA1,
|
||||
EXAMPLE_PIN_NUM_DATA2,
|
||||
// other GPIOs
|
||||
// The number of GPIOs here should be the same to the value of "data_width" above
|
||||
...
|
||||
},
|
||||
// The timing parameters should refer to your LCD spec
|
||||
.timings = {
|
||||
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
|
||||
.h_res = EXAMPLE_LCD_H_RES,
|
||||
.v_res = EXAMPLE_LCD_V_RES,
|
||||
.hsync_back_porch = 40,
|
||||
.hsync_front_porch = 20,
|
||||
.hsync_pulse_width = 1,
|
||||
.vsync_back_porch = 8,
|
||||
.vsync_front_porch = 4,
|
||||
.vsync_pulse_width = 1,
|
||||
},
|
||||
.flags.fb_in_psram = true, // allocate frame buffer from PSRAM
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
|
||||
|
||||
.. _double_frame_buffer_in_psram:
|
||||
|
||||
Double Frame Buffer in PSRAM
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To prevent tearing effects, the simplest method is to use two screen-sized frame buffers. Given the limited internal memory, these buffers must be allocated from PSRAM. This ensures that the frame buffer being written to by the CPU and the one being read by the EDMA are always distinct and independent. The EDMA will only switch between the two buffers once the current write operation is complete and the frame has been fully transmitted to the LCD. The main drawback of this approach is the need to maintain synchronization between the two frame buffers.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_rgb_panel_config_t panel_config = {
|
||||
.data_width = 16, // RGB565 in parallel mode, thus 16 bits in width
|
||||
.num_fbs = 2, // allocate double frame buffer
|
||||
.clk_src = LCD_CLK_SRC_DEFAULT,
|
||||
.disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
|
||||
.pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
|
||||
.vsync_gpio_num = EXAMPLE_PIN_NUM_VSYNC,
|
||||
.hsync_gpio_num = EXAMPLE_PIN_NUM_HSYNC,
|
||||
.de_gpio_num = EXAMPLE_PIN_NUM_DE,
|
||||
.data_gpio_nums = {
|
||||
EXAMPLE_PIN_NUM_DATA0,
|
||||
EXAMPLE_PIN_NUM_DATA1,
|
||||
EXAMPLE_PIN_NUM_DATA2,
|
||||
// other GPIOs
|
||||
// The number of GPIOs here should be the same to the value of "data_width" above
|
||||
...
|
||||
},
|
||||
// The timing parameters should refer to your LCD spec
|
||||
.timings = {
|
||||
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
|
||||
.h_res = EXAMPLE_LCD_H_RES,
|
||||
.v_res = EXAMPLE_LCD_V_RES,
|
||||
.hsync_back_porch = 40,
|
||||
.hsync_front_porch = 20,
|
||||
.hsync_pulse_width = 1,
|
||||
.vsync_back_porch = 8,
|
||||
.vsync_front_porch = 4,
|
||||
.vsync_pulse_width = 1,
|
||||
},
|
||||
.flags.fb_in_psram = true, // allocate frame buffer from PSRAM
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
|
||||
|
||||
.. _user_custom_frame_buffer:
|
||||
|
||||
User Custom Frame Buffer
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
User can provide their own frame buffer instead of letting the driver allocate it. In this mode, user needs to manage the lifecycle of the frame buffer by themselves.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_rgb_panel_config_t panel_config = {
|
||||
.data_width = 16, // RGB565 in parallel mode, thus 16 bits in width
|
||||
.clk_src = LCD_CLK_SRC_DEFAULT,
|
||||
.disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
|
||||
.pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
|
||||
.vsync_gpio_num = EXAMPLE_PIN_NUM_VSYNC,
|
||||
.hsync_gpio_num = EXAMPLE_PIN_NUM_HSYNC,
|
||||
.de_gpio_num = EXAMPLE_PIN_NUM_DE,
|
||||
.data_gpio_nums = {
|
||||
EXAMPLE_PIN_NUM_DATA0,
|
||||
EXAMPLE_PIN_NUM_DATA1,
|
||||
EXAMPLE_PIN_NUM_DATA2,
|
||||
// other GPIOs
|
||||
// The number of GPIOs here should be the same to the value of "data_width" above
|
||||
...
|
||||
},
|
||||
// The timing parameters should refer to your LCD spec
|
||||
.timings = {
|
||||
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
|
||||
.h_res = EXAMPLE_LCD_H_RES,
|
||||
.v_res = EXAMPLE_LCD_V_RES,
|
||||
.hsync_back_porch = 40,
|
||||
.hsync_front_porch = 20,
|
||||
.hsync_pulse_width = 1,
|
||||
.vsync_back_porch = 8,
|
||||
.vsync_front_porch = 4,
|
||||
.vsync_pulse_width = 1,
|
||||
},
|
||||
.user_fbs[0] = user_frame_buffer, // use user custom frame buffer
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
|
||||
|
||||
.. _bounce_buffer_with_single_psram_frame_buffer:
|
||||
|
||||
Bounce Buffer with Single PSRAM Frame Buffer
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This mode allocates two "bounce buffers" from internal memory and a main frame buffer in PSRAM. To enable this mode, set the :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` flag and specify a non-zero value for :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px`. The bounce buffers only need to hold a few lines of display data, which is much smaller than the main frame buffer. The LCD peripheral uses DMA to read data from one bounce buffer while an interrupt routine uses the CPU DCache to copy data from the main PSRAM frame buffer into the other bounce buffer. Once the LCD peripheral finishes reading from the bounce buffer, the buffers swap roles, allowing the CPU to fill the other one. The advantage of this mode is achieving a higher pixel clock frequency. Since the bounce buffers are larger than the FIFOs in the EDMA path, this method is also more robust against short bandwidth spikes. The downside is a significant increase in CPU usage, and the LCD **CANNOT** function if the external memory cache is disabled, such as during OTA or NVS writes to the main flash.
|
||||
|
||||
.. note::
|
||||
|
||||
For optimal performance in this mode, it is highly recommended to enable the "PSRAM XIP (Execute In Place)" feature by turning on the Kconfig option: :ref:`CONFIG_SPIRAM_XIP_FROM_PSRAM`. This allows the CPU to fetch instructions and read-only data directly from PSRAM instead of the main flash. Additionally, the external memory cache remains active even when writing to the main flash via SPI 1, making it feasible to display an OTA progress bar during your application updates.
|
||||
|
||||
.. note::
|
||||
|
||||
This mode also faces issues due to limited PSRAM bandwidth. For instance, if your draw buffers are in PSRAM and their contents are copied to the internal frame buffer by CPU Core 1, while CPU Core 0 is performing another memory copy in the DMA EOF ISR, both CPUs will be accessing PSRAM via cache, sharing its bandwidth. This significantly increases the memory copy time in the DMA EOF ISR, causing the driver to fail in switching the bounce buffer promptly, resulting in a screen shift. Although the driver can detect this condition and restart in the LCD's VSYNC interrupt handler, you may still notice flickering on the screen.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_rgb_panel_config_t panel_config = {
|
||||
.data_width = 16, // RGB565 in parallel mode, thus 16 bits in width
|
||||
.clk_src = LCD_CLK_SRC_DEFAULT,
|
||||
.bounce_buffer_size_px = 10 * EXAMPLE_LCD_H_RES, // allocate 10 lines data as bounce buffer from internal memory
|
||||
.disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
|
||||
.pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
|
||||
.vsync_gpio_num = EXAMPLE_PIN_NUM_VSYNC,
|
||||
.hsync_gpio_num = EXAMPLE_PIN_NUM_HSYNC,
|
||||
.de_gpio_num = EXAMPLE_PIN_NUM_DE,
|
||||
.data_gpio_nums = {
|
||||
EXAMPLE_PIN_NUM_DATA0,
|
||||
EXAMPLE_PIN_NUM_DATA1,
|
||||
EXAMPLE_PIN_NUM_DATA2,
|
||||
// other GPIOs
|
||||
// The number of GPIOs here should be the same to the value of "data_width" above
|
||||
...
|
||||
},
|
||||
// The timing parameters should refer to your LCD spec
|
||||
.timings = {
|
||||
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
|
||||
.h_res = EXAMPLE_LCD_H_RES,
|
||||
.v_res = EXAMPLE_LCD_V_RES,
|
||||
.hsync_back_porch = 40,
|
||||
.hsync_front_porch = 20,
|
||||
.hsync_pulse_width = 1,
|
||||
.vsync_back_porch = 8,
|
||||
.vsync_front_porch = 4,
|
||||
.vsync_pulse_width = 1,
|
||||
},
|
||||
.flags.fb_in_psram = true, // allocate frame buffer from PSRAM
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
|
||||
|
||||
.. _bounce_buffer_only:
|
||||
|
||||
Bounce Buffer Only
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This mode is similar to :ref:`bounce_buffer_with_single_psram_frame_buffer`, but there is no PSRAM frame buffer initialized by the LCD driver. Instead, the user supplies a callback function that is responsible for filling the bounce buffers. As this driver does not care where the written pixels come from, this allows for the callback doing e.g., on-the-fly conversion from a smaller, 8-bit-per-pixel PSRAM frame buffer to a 16-bit LCD, or even procedurally generated frame-buffer-less graphics. This option is selected by setting the :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` flag and supplying a :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px` value. And then register the :cpp:member:`esp_lcd_rgb_panel_event_callbacks_t::on_bounce_empty` callback by calling :cpp:func:`esp_lcd_rgb_panel_register_event_callbacks`.
|
||||
|
||||
.. note::
|
||||
|
||||
In a well-designed embedded application, situations where the DMA cannot deliver data as fast as the LCD consumes it should be avoided. However, such scenarios can theoretically occur. In the {IDF_TARGET_NAME} hardware, this results in the LCD outputting dummy bytes while the DMA waits for data. If the DMA were to run in a continuous stream, it could cause a desynchronization between the LCD address from which the DMA reads data and the address from which the LCD peripheral outputs data, leading to a **permanently** shifted image.
|
||||
To prevent this, you can either enable the :ref:`CONFIG_LCD_RGB_RESTART_IN_VSYNC` option, allowing the driver to automatically restart the DMA during the VBlank interrupt, or call :cpp:func:`esp_lcd_rgb_panel_restart` to manually restart the DMA. Note that :cpp:func:`esp_lcd_rgb_panel_restart` does not restart the DMA immediately; instead, the DMA will be restarted at the next VSYNC event.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_lcd_panel_rgb.inc
|
||||
@@ -0,0 +1,69 @@
|
||||
SPI Interfaced LCD
|
||||
==================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
#. Create an SPI bus. Please refer to :doc:`SPI Master API doc </api-reference/peripherals/spi_master>` for more details.
|
||||
|
||||
Currently the driver supports SPI, Quad SPI and Octal SPI (simulate Intel 8080 timing) modes.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
spi_bus_config_t buscfg = {
|
||||
.sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
|
||||
.mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
|
||||
.miso_io_num = EXAMPLE_PIN_NUM_MISO,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = EXAMPLE_LCD_H_RES * 80 * sizeof(uint16_t), // transfer 80 lines of pixels (assume pixel is RGB565) at most in one SPI transaction
|
||||
};
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO)); // Enable the DMA feature
|
||||
|
||||
#. Allocate an LCD IO device handle from the SPI bus. In this step, you need to provide the following information:
|
||||
|
||||
- :cpp:member:`esp_lcd_panel_io_spi_config_t::dc_gpio_num` sets the GPIO number for the DC signal line (some LCD calls this ``RS`` line). The LCD driver uses this GPIO to switch between sending command and sending data.
|
||||
- :cpp:member:`esp_lcd_panel_io_spi_config_t::cs_gpio_num` sets the GPIO number for the CS signal line. The LCD driver uses this GPIO to select the LCD chip. If the SPI bus only has one device attached (i.e., this LCD), you can set the GPIO number to ``-1`` to occupy the bus exclusively.
|
||||
- :cpp:member:`esp_lcd_panel_io_spi_config_t::pclk_hz` sets the frequency of the pixel clock, in Hz. The value should not exceed the range recommended in the LCD spec.
|
||||
- :cpp:member:`esp_lcd_panel_io_spi_config_t::spi_mode` sets the SPI mode. The LCD driver uses this mode to communicate with the LCD. For the meaning of the SPI mode, please refer to the :doc:`SPI Master API doc </api-reference/peripherals/spi_master>`.
|
||||
- :cpp:member:`esp_lcd_panel_io_spi_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_panel_io_spi_config_t::lcd_param_bits` set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
|
||||
- :cpp:member:`esp_lcd_panel_io_spi_config_t::trans_queue_depth` sets the depth of the SPI transaction queue. A bigger value means more transactions can be queued up, but it also consumes more memory.
|
||||
- :cpp:member:`esp_lcd_panel_io_spi_config_t::cs_ena_pretrans` sets the amount of SPI bit-cycles which the cs should be activated before the transmission (0-16).
|
||||
- :cpp:member:`esp_lcd_panel_io_spi_config_t::cs_ena_posttrans` sets the amount of SPI bit-cycles which the cs should stay active after the transmission (0-16).
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
esp_lcd_panel_io_spi_config_t io_config = {
|
||||
.dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
|
||||
.cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
|
||||
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
|
||||
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
|
||||
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
|
||||
.spi_mode = 0,
|
||||
.trans_queue_depth = 10,
|
||||
};
|
||||
// Attach the LCD to the SPI bus
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));
|
||||
|
||||
#. Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the SPI IO device handle that allocated in the last step, and some panel specific configurations:
|
||||
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` sets the LCD's hardware reset GPIO number. If the LCD does not have a hardware reset pin, set this to ``-1``.
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::rgb_ele_order` sets the RGB element order of each color data.
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver uses this value to calculate the number of bytes to send to the LCD controller chip.
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::data_endian` specifies the data endian to be transmitted to the screen. No need to specify for color data within one byte, like RGB232. For drivers that do not support specifying data endian, this field would be ignored.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = EXAMPLE_PIN_NUM_RST,
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
|
||||
.bits_per_pixel = 16,
|
||||
};
|
||||
// Create LCD panel handle for ST7789, with the SPI IO device handle
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_lcd_io_spi.inc
|
||||
@@ -0,0 +1,57 @@
|
||||
Low Dropout Voltage Regulator (LDO)
|
||||
===================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The {IDF_TARGET_NAME} chip internally integrates {IDF_TARGET_SOC_GP_LDO_NUM_UNITS} channels of low-dropout voltage regulators (LDOs). Each channel's voltage is programmable. In our hardware reference designs, some of these LDO outputs are typically used to power the internal Flash and PSRAM, while the remaining LDOs can be used to supply external devices.
|
||||
|
||||
.. note::
|
||||
|
||||
It's essential to read the manual first and ensure that the required current does not exceed the chip's specifications.
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
The description of the LDO driver is divided into the following sections:
|
||||
|
||||
- :ref:`ldo-channel-acquisition` - Introduces the types of LDO channels and how to apply for LDO channel resources.
|
||||
- :ref:`ldo-adjust-voltage` - Describes how to adjust the voltage of the LDO channel.
|
||||
|
||||
.. _ldo-channel-acquisition:
|
||||
|
||||
LDO Channel Acquisition
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
LDO channels can be classified into two types: fixed voltage and adjustable voltage. For a fixed voltage channel, it allows multiple users to simultaneously use it (in software, we allow a variable to have multiple immutable references ). However, for an adjustable voltage channel, only one user is allowed to use it at a time (in software, we don't allow a variable to have multiple mutable references or coexistence of mutable and immutable references).
|
||||
|
||||
In the driver, the LDO channel is represented by the :cpp:type:`esp_ldo_channel_handle_t`. You can use the :cpp:func:`esp_ldo_acquire_channel` function to request LDO channel resources. Upon successful acquisition, a handle for the LDO channel will be returned, which can be used for subsequent voltage adjustment operations. When applying for a channel, the :cpp:type:`esp_ldo_channel_config_t` structure is used to specify the basic information of the LDO channel, including the channel ID, the desired output voltage, and whether the voltage can be dynamically adjusted.
|
||||
|
||||
- :cpp:member:`esp_ldo_channel_config_t::chan_id` - LDO channels are uniquely identified by a label, which is used to distinguish different LDO channels. Please note that this information needs to be determined based on the circuit schematic and chip datasheet. For example, an LDO channel labeled as ``LDO_VO3`` corresponds to an ID of ``3``.
|
||||
- :cpp:member:`esp_ldo_channel_config_t::voltage_mv` - The desired output voltage of the LDO channel, in millivolts.
|
||||
- :cpp:member:`esp_ldo_channel_config_t::ldo_extra_flags::adjustable` - Whether the LDO channel's output voltage can be dynamically adjusted. Only when it is set to `true`, can the :cpp:func:`esp_ldo_channel_adjust_voltage` function be used to dynamically adjust the output voltage.
|
||||
|
||||
Since multiple users are allowed to use a fixed voltage LDO channel simultaneously, the driver internally maintains a reference counter. The LDO channel will be automatically closed when the last user releases the LDO channel resources. The function to release LDO channel resources is :cpp:func:`esp_ldo_release_channel`. Additionally, it is important to note that the acquisition and release of LDO channels should appear in pairs during usage.
|
||||
|
||||
.. _ldo-adjust-voltage:
|
||||
|
||||
LDO Voltage Adjustment
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
:cpp:func:`esp_ldo_channel_adjust_voltage` function is used to adjust the output voltage of an LDO channel at runtime. However, please note that this function can only be used for LDO channels with adjustable voltage. Attempting to use this function on a fixed voltage LDO channel will result in an error.
|
||||
|
||||
Also, it is important to keep in mind that due to hardware limitations, the LDO channel voltage may have a deviation of approximately 50-100mV. Therefore, it is not advisable to rely on the LDO channel's output voltage for precise analog control.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_MIPI_DSI_SUPPORTED: * Use the internal LDO channel to power up the MIPI DPHY: :example:`peripherals/lcd/mipi_dsi`
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/esp_ldo_regulator.inc
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user