mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
docs: Provide translation for usb_host_notes_design
This commit is contained in:
parent
6e20be1d95
commit
93eff2080f
@ -1,6 +1,8 @@
|
||||
USB Host Maintainers Notes (Design Guidelines)
|
||||
==============================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Design Considerations
|
||||
---------------------
|
||||
|
||||
@ -10,11 +12,11 @@ The design of the Host Stack takes into account the following design considerati
|
||||
|
||||
The embedded nature of Host Stack means limited hardware resources (such as memory and processing power) when compared to larger host systems.
|
||||
|
||||
**USB2.0 Chapter 10:**
|
||||
**USB 2.0 Chapter 10:**
|
||||
|
||||
Chapter 10 of the USB 2.0 specification specifies certain requirements of USB Host systems, in particular the required layers of the USB Host's system software.
|
||||
|
||||
**Diverse Use Case Complexities:**
|
||||
**Varying Complexity of Different Use Cases:**
|
||||
|
||||
The embedded nature of the Host Stack also means a wide range of use cases with differing complexities. Some USB Host applications aim to only support a single vendor specific device, whereas other applications require support for a wide range of devices of different classes.
|
||||
|
||||
@ -28,21 +30,21 @@ DMA Support
|
||||
|
||||
**Requirement: The Host Stack must support DMA.**
|
||||
|
||||
The Host Stack must support DMA in order to reduce CPU's workload. DMA support allows the automatic copying of USB transfer data to/from the Host Controller without CPU intervention. This is especially critical given the embedded nature of the CPU (i.e., lower CPU frequencies) and large maximum data payloads of USB transfers (e.g., 1023 bytes for isochronous transfers).
|
||||
The Host Stack must support DMA in order to reduce CPU's workload. DMA support allows the automatic copying of USB transfer data to and from the Host Controller without CPU intervention. This is especially critical given the embedded nature of the CPU (i.e., lower CPU frequencies) and large maximum data payloads of USB transfers (e.g., 1023 bytes for isochronous transfers).
|
||||
|
||||
Minimize Memory Copies
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
**Requirement: The Host Stack should minimize the amount of memory copies when passing data between layers.**
|
||||
|
||||
Various data and objects (e.g., USB transfers) need to be passed between multiple layers of the Host Stack. The Host Stack should minimize the amount of memory copies that occur between layers by allocating the data's/object's memory once, and simply passing a pointer to that data/object between the layers. Therefore, the Host Stack requires some standardized data types shared across multiple layers (see USB2.0 Section 10.3.4).
|
||||
Various data and objects (e.g., USB transfers) need to be passed between multiple layers of the Host Stack. The Host Stack should minimize the amount of memory copies that occur between layers by allocating the data's or object's memory once, and simply passing a pointer to that data or object between the layers. Therefore, the Host Stack requires some standardized data types shared across multiple layers (see USB 2.0 Section 10.3.4).
|
||||
|
||||
Event Driven
|
||||
^^^^^^^^^^^^
|
||||
|
||||
**Requirement: The Host Stack must allow for event driven operation (i.e., the Host Stack's API must not be polling).**
|
||||
|
||||
The Host Stack needs to support some CPU intensive use application scenarios such as video streaming (i.e., UVC class). Therefore, the Host Stack should minimize CPU usage by allowing for completely event driven operation, thus reserving the majority of CPU time for the application itself (i.e., video encoding/decoding in this case).
|
||||
The Host Stack needs to support some CPU intensive application scenarios such as video streaming (i.e., UVC class). Therefore, the Host Stack should minimize CPU usage by supporting completely event driven operation, thus reserving the majority of CPU time for the application itself (i.e., video encoding or decoding in this case).
|
||||
|
||||
The Host Stack needs to communicate events across the layers using interrupts, callbacks, and FreeRTOS synchronization primitives (e.g., queues and semaphores).
|
||||
|
||||
@ -51,7 +53,7 @@ No Task Creation
|
||||
|
||||
**Requirement: All layers of the Host Stack below (and including) the Host Library layer must not create any tasks.**
|
||||
|
||||
Task stacks are generally one of the most memory intensive parts of an ESP-IDF applications. Given the wide range of applications scenarios, the number of tasks created (and their stack sizes) can vary greatly. For example...
|
||||
Task stacks are generally one of the most memory intensive parts of an ESP-IDF applications. Given the wide range of applications scenarios, the number of tasks created (and their stack sizes) can vary greatly. For example:
|
||||
|
||||
- applications that require low latency or high throughput (such as isochronous transfers) may choose to create a dedicated task to handle those transfers in order to minimize latency.
|
||||
- applications that do not have strict latency requirements (such as bulk transfers) may choose to handle those transfers from a shared task in order to save some memory.
|
||||
@ -63,7 +65,7 @@ Operable at Different Layers
|
||||
|
||||
Given the wide range of use case complexities, the Host Stack must be operable at different layers, allowing users to use the Host Stack at a lower layer (e.g., the HCD or HAL) or at a higher layer (e.g., a class driver).
|
||||
|
||||
Being operable at different layers allows the users to decide on the appropriate trade-off between convenience, control, and optimization for their application when using the Host Stack. For example...
|
||||
Being operable at different layers allows the users to decide on the appropriate trade-off between convenience, control, and optimization for their application when using the Host Stack. For example:
|
||||
|
||||
- Host Stack applications that support a dedicated custom device may want to use a lower level of abstraction for better optimization, control, and simpler API.
|
||||
- Host Stack applications that need to support a wide range of device classes requires the full Host Stack so that device enumeration is automatically handled.
|
||||
@ -71,26 +73,26 @@ Being operable at different layers allows the users to decide on the appropriate
|
||||
Coding Conventions
|
||||
------------------
|
||||
|
||||
The Host Stack follows the following set of coding rules/guidelines for better code readability and maintainability:
|
||||
The Host Stack follows the following set of coding conventions for better code readability and maintainability:
|
||||
|
||||
Symbols Use Layer Name As Prefix
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For each layer of the Host Stack, the symbols exposed by that layer (i.e., functions, types, macros) must be prefixed with that layer's name. For example, the symbols exposed by the HCD layer will be prefixed ``hcd_...``/``HCD_...``.
|
||||
For each layer of the Host Stack, the symbols exposed by that layer (i.e., functions, types, macros) must be prefixed with that layer's name. For example, the symbols exposed by the HCD layer will be prefixed ``hcd_...`` or ``HCD_...``.
|
||||
|
||||
However, internal symbols (e.g., static functions) **should not** be prefixed with their layer's name. This makes it easier to differentiate between internal and external symbols when modifying that layer's source code.
|
||||
|
||||
Critical Section Functions Prefixed With ``_``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In each layer of the Host Stack, there are various static functions that must be called inside a critical section. The names of these functions are prefixed with ``_`` (e.g., ``_func_called_from_crit()``) to make it easier for maintainers to differentiate which functions should be called from critical sections. For example...
|
||||
In each layer of the Host Stack, there are various static functions that must be called inside a critical section. The names of these functions are prefixed with ``_`` (e.g., ``_func_called_from_crit()``) to make it easier for maintainers to differentiate which functions should be called from critical sections. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
some_func(); // Called outside critical section
|
||||
taskENTER_CRITICAL(&some_lock);
|
||||
_some_func_crit(); // Called inside critical section. _ prefix makes it easier to differentiate
|
||||
taskEXIT_CRITICAL(&some_lock);
|
||||
some_func(); // Called outside critical section
|
||||
taskENTER_CRITICAL(&some_lock);
|
||||
_some_func_crit(); // Called inside critical section. _ prefix makes it easier to differentiate
|
||||
taskEXIT_CRITICAL(&some_lock);
|
||||
|
||||
Grouping Structure Members by Locking Mechanism
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -115,7 +117,7 @@ Some layers of the Host Stack utilize multiple locking schemes (e.g., critical s
|
||||
- Data that is only ever accessed by the same task do not require the use of any locks.
|
||||
* - Constant
|
||||
- ``constant``
|
||||
- Constant data is set once during the object's instantiation and never changed again. Thus, any task or ISR can freely the constant data without the use of locks, so long as the variable is never written to.
|
||||
- Constant data is set once during the object's instantiation and never changed again. Thus, any task or ISR can freely access the constant data without the use of locks, so long as the variable is never written to.
|
||||
|
||||
Grouping structure members by locking scheme makes the code more maintainable as it makes clear which locking scheme is required when accessing a particular member variable, as demonstrated in the code snippet below:
|
||||
|
||||
|
@ -1 +1,144 @@
|
||||
.. include:: ../../../../en/api-reference/peripherals/usb_host/usb_host_notes_design.rst
|
||||
USB 主机维护者注意事项(设计指南)
|
||||
=====================================
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
设计考虑
|
||||
--------
|
||||
|
||||
设计主机协议栈时应考虑以下要点:
|
||||
|
||||
**硬件资源有限:**
|
||||
|
||||
与较大的主机系统相比,嵌入式主机协议栈的硬件资源(如内存和处理能力)有限。
|
||||
|
||||
**USB 2.0 第 10 章:**
|
||||
|
||||
USB 2.0 规范的第 10 章中规定了 USB 主机系统的某些要求,特别是 USB 主机系统软件所需的必要层次。
|
||||
|
||||
**用例复杂性广泛:**
|
||||
|
||||
嵌入式主机协议栈用例广泛,包含不同的复杂性。一些 USB 主机应用只支持单个厂商的特定设备,而其他应用则需要支持不同类别的各种设备。
|
||||
|
||||
要求
|
||||
----
|
||||
|
||||
考虑到以上设计要素,在设计主机协议栈时满足了以下要求:
|
||||
|
||||
支持 DMA
|
||||
^^^^^^^^^^
|
||||
|
||||
**要求:主机协议栈必须支持 DMA。**
|
||||
|
||||
为降低 CPU 负载,主机协议栈必须支持 DMA。通过 DMA,无需 CPU 干预,即可自动复制 USB 传输数据到主机控制器中,以及自动从主机控制器中复制 USB 传输数据。这一点对于嵌入式 CPU(即,较低的 CPU 频率)和较大的最大 USB 传输数据负载(例如,同步传输的 1023 字节)来说尤为重要。
|
||||
|
||||
减少内存拷贝
|
||||
^^^^^^^^^^^^
|
||||
|
||||
**要求:在不同层之间传递数据时主机协议栈应尽量减少内存拷贝次数。**
|
||||
|
||||
各种数据和对象(例如 USB 传输)需要在主机协议栈的多个层之间传递。主机协议栈应一次性为数据或对象分配内存,并在各层之间传递指向该数据或对象的指针,从而减少各层间的内存拷贝次数。因此,主机协议栈需要能够在不同层之间共享的标准化数据类型(参见 USB 2.0 规范的第 10.3.4 节)。
|
||||
|
||||
支持事件驱动
|
||||
^^^^^^^^^^^^
|
||||
|
||||
**要求:主机协议栈必须支持事件驱动(即,主机协议栈的 API 不是轮询的方式)。**
|
||||
|
||||
主机协议栈应支持 CPU 密集型应用场景,例如视频流传输(即,UVC Class)。因此,主机协议栈应支持事件驱动操作,减少 CPU 占用,从而将大部分 CPU 时间留给应用本身(如,视频编码或解码)。
|
||||
|
||||
主机协议栈需要通过中断、回调和 FreeRTOS 同步原语(例如队列和信号量)在各层之间传递事件。
|
||||
|
||||
不得创建任务
|
||||
^^^^^^^^^^^^
|
||||
|
||||
**要求:主机库层及其下层的所有主机协议栈层都不得创建任何任务。**
|
||||
|
||||
任务栈通常是 ESP-IDF 应用程序中最占内存的部分之一。由于应用场景广泛,创建的任务数量(以及任务的栈大小)差别可能很大。例如:
|
||||
|
||||
- 需要低延迟或高吞吐量(例如同步传输)的应用可能会创建一个专门的任务来处理传输数据,确保延迟最小化。
|
||||
- 对延迟要求不高(例如批量传输)的应用可能会通过共享任务来处理传输数据,以节省内存。
|
||||
|
||||
因此,主机库层及其下层的所有主机协议栈层都不得创建任何任务,而应公开处理函数,供上层创建的任务调用。任务创建将委托给 Class 驱动程序或应用程序层。
|
||||
|
||||
可在不同层操作
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
主机协议栈用例广泛,情况复杂,应确保其可在不同层操作。因此无论是在较低层(例如 HCD 或 HAL),还是在较高层(例如 Class 驱动程序),用户都可以使用主机协议栈。
|
||||
|
||||
用户可以在不同的层上使用主机协议栈,并根据应用程序的特点,权衡便利性、可控性和优化度,例如:
|
||||
|
||||
- 支持专用定制设备的主机协议栈应用可能会使用较低级别的抽象,从而调用优化度高、可控性强、简单便利的 API。
|
||||
- 支持各种设备 Class 的主机协议栈应用需要使用完整的主机协议栈,以便自动处理设备枚举。
|
||||
|
||||
编码约定
|
||||
--------
|
||||
|
||||
主机协议栈遵循下列编码规范,以提高代码可读性与可维护性:
|
||||
|
||||
符号应使用层名作为前缀
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
主机协议栈每层公开的符号(即函数、类型、宏)必须以该层的名称作为前缀。例如,HCD 层公开的符号将以 ``hcd_...`` 或 ``HCD_...`` 为前缀。
|
||||
|
||||
但内部符号(例如静态函数)**不应** 以其所在层的名称作为前缀,以便修改该层源代码时能更好地区分内部和外部符号。
|
||||
|
||||
临界区函数应以 ``_`` 为前缀
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
主机协议栈的每层中都有各种必须在临界区内调用的静态函数。这些函数的名称以 ``_`` 为前缀(例如,``_func_called_from_crit()``),以便维护者更好地区分哪些函数应该从临界区内调用。例如:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
some_func(); // 从临界区外调用
|
||||
taskENTER_CRITICAL(&some_lock);
|
||||
_some_func_crit(); // 从临界区内调用。使用 _ 前缀,使其便于区分
|
||||
taskEXIT_CRITICAL(&some_lock);
|
||||
|
||||
根据锁机制对结构体成员进行分组
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
主机协议栈的某些层使用多种锁机制(例如临界区和任务互斥锁)来确保线程安全。每种锁机制能提供不同级别的保护,而同一对象的成员变量受不同锁机制保护。因此,为了清晰地区分不同的锁机制及其相关变量,结构体成员按锁机制被分组为嵌套结构体。
|
||||
|
||||
.. list-table:: 锁机制
|
||||
:widths: 20 10 80
|
||||
:header-rows: 1
|
||||
|
||||
* - 锁机制
|
||||
- 嵌套结构体
|
||||
- 描述
|
||||
* - 临界区
|
||||
- ``dynamic``
|
||||
- 从任务上下文和 ISR 上下文访问的共享数据受临界区保护。
|
||||
* - 任务互斥锁
|
||||
- ``mux_protected``
|
||||
- 仅从任务上下文访问的共享数据受 FreeRTOS 互斥锁保护。
|
||||
* - 单线程
|
||||
- ``single_thread``
|
||||
- 只由同一任务访问的数据无需锁保护。
|
||||
* - 常量
|
||||
- ``constant``
|
||||
- 在对象实例化时设置常量数据,之后常量将不再改变。因此,只要变量不被写入,任何任务或 ISR 都可以自由访问常量数据。
|
||||
|
||||
根据锁机制对结构体成员进行分组,能清晰地显示访问特定成员变量时需要哪种锁机制,使得代码易于维护,如下所示:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct some_obj some_obj_t;
|
||||
|
||||
some_obj_t obj;
|
||||
|
||||
// 访问动态成员需要临界区
|
||||
taskENTER_CRITICAL(&some_lock);
|
||||
obj.dynamic.varA = 1;
|
||||
taskEXIT_CRITICAL(&some_lock);
|
||||
|
||||
// 访问受互斥锁保护的成员需要获取互斥锁
|
||||
xSemaphoreTake(&some_mux, portMAX_DELAY);
|
||||
obj.mux_protected.varB = 1;
|
||||
xSemaphoreGive(&some_mux);
|
||||
|
||||
// 只由某一任务访问的单线程成员数据无需锁保护
|
||||
obj.single_thread.varC = 1;
|
||||
|
||||
// 访问常量成员无需锁保护,但此种访问为只读
|
||||
int local_var = obj.constant.varD;
|
||||
|
Loading…
Reference in New Issue
Block a user