Merge branch 'doc/update_CN_translation_for_API_guides' into 'master'

updated CN translation for api-guides

See merge request espressif/esp-idf!12506
This commit is contained in:
Krzysztof Budzynski 2021-03-15 05:01:24 +00:00
commit aa3791b929
6 changed files with 238 additions and 272 deletions

View File

@ -2,8 +2,7 @@ JTAG Debugging
==============
:link_to_translation:`zh_CN:[中文]`
This document provides a guide to installing OpenOCD for {IDF_TARGET_NAME} and debugging using
GDB. The document is structured as follows:
This document provides a guide to installing OpenOCD for {IDF_TARGET_NAME} and debugging using GDB. The document is structured as follows:
:ref:`jtag-debugging-introduction`
Introduction to the purpose of this guide.
@ -199,7 +198,7 @@ You are now ready to start application debugging. Follow steps described in sect
.. _jtag-debugging-launching-debugger:
Launching Debugger
------------------
---------------------
The toolchain for {IDF_TARGET_NAME} features GNU Debugger, in short GDB. It is available with other toolchain programs under filename: {IDF_TARGET_TOOLCHAIN_PREFIX}-gdb. GDB can be called and operated directly from command line in a terminal. Another option is to call it from within IDE (like Eclipse, Visual Studio Code, etc.) and operate indirectly with help of GUI instead of typing commands in a terminal.
@ -214,7 +213,7 @@ It is recommended to first check if debugger works from :ref:`jtag-debugging-usi
.. _jtag-debugging-examples:
Debugging Examples
------------------
----------------------
This section is intended for users not familiar with GDB. It presents example debugging session from :ref:`jtag-debugging-examples-eclipse` using simple application available under :example:`get-started/blink` and covers the following debugging actions:
@ -234,7 +233,7 @@ Before proceeding to examples, set up your {IDF_TARGET_NAME} target and load it
.. _jtag-debugging-building-openocd:
Building OpenOCD from Sources
-----------------------------
---------------------------------
Please refer to separate documents listed below, that describe build process.
@ -276,7 +275,7 @@ and Windows:
.. _jtag-debugging-tips-and-quirks:
Tips and Quirks
---------------
------------------
This section provides collection of links to all tips and quirks referred to from various parts of this guide.
@ -287,7 +286,7 @@ This section provides collection of links to all tips and quirks referred to fro
Related Documents
-----------------
---------------------
.. toctree::
:maxdepth: 1

View File

@ -6,7 +6,7 @@ This section covers configuration and running debugger using several methods:
* from :ref:`jtag-debugging-using-debugger-eclipse`
* from :ref:`jtag-debugging-using-debugger-command-line`
* using :ref:`jtag-debugging-with-idf-py`.
* using :ref:`jtag-debugging-with-idf-py`
.. _jtag-debugging-using-debugger-eclipse:
@ -206,24 +206,19 @@ It is also possible to execute the described debugging tools conveniently from `
1. ``idf.py openocd``
Runs OpenOCD in a console with configuration defined in the environment or via command line.
It uses default script directory defined as ``OPENOCD_SCRIPTS`` environmental variable, which is automatically added
from an Export script (``export.sh`` or ``export.bat``). It is possible to override the script location
using command line argument ``--openocd-scripts``.
Runs OpenOCD in a console with configuration defined in the environment or via command line. It uses default script directory defined as ``OPENOCD_SCRIPTS`` environmental variable, which is automatically added from an Export script (``export.sh`` or ``export.bat``).
It is possible to override the script location using command line argument ``--openocd-scripts``.
.. include:: {IDF_TARGET_PATH_NAME}.inc
:start-after: idf-py-openocd-default-cfg
:end-before: ---
As for the JTAG configuration of the current board, please use the environmental variable ``OPENOCD_COMMANDS``
or ``--openocd-commands`` command line argument. If none of the above is defined,
OpenOCD is started with |idf-py-def-cfg| board definition.
As for the JTAG configuration of the current board, please use the environmental variable ``OPENOCD_COMMANDS`` or ``--openocd-commands`` command line argument. If none of the above is defined, OpenOCD is started with |idf-py-def-cfg| board definition.
2. ``idf.py gdb``
Starts the gdb the same way as the :ref:`jtag-debugging-using-debugger-command-line`, but generates the initial gdb scripts
referring to the current project elf file.
Starts the gdb the same way as the :ref:`jtag-debugging-using-debugger-command-line`, but generates the initial gdb scripts referring to the current project elf file.
3. ``idf.py gdbtui``
@ -236,14 +231,11 @@ It is also possible to execute the described debugging tools conveniently from `
Starts `gdbgui <https://www.gdbgui.com>`_ debugger frontend enabling out-of-the-box debugging in a browser window.
It is possible to combine these debugging actions on a single command line allowing convenient
setup of blocking and non-blocking actions in one step. ``idf.py`` implements a simple logic to move the background
actions (such as openocd) to the beginning and the interactive ones (such as gdb, monitor) to the end of the action list.
It is possible to combine these debugging actions on a single command line allowing convenient setup of blocking and non-blocking actions in one step. ``idf.py`` implements a simple logic to move the background actions (such as openocd) to the beginning and the interactive ones (such as gdb, monitor) to the end of the action list.
An example of a very useful combination is::
idf.py openocd gdbgui monitor
The above command runs OpenOCD in the background, starts `gdbgui <https://www.gdbgui.com>`_ to open a browser window
with active debugger frontend and opens a serial monitor in the active console.
The above command runs OpenOCD in the background, starts `gdbgui <https://www.gdbgui.com>`_ to open a browser window with active debugger frontend and opens a serial monitor in the active console.

View File

@ -7,9 +7,7 @@ ESP-IDF comes with a unit test application that is based on the Unity - unit tes
Normal Test Cases
------------------
Unit tests are located in the ``test`` subdirectory of a component.
Tests are written in C, and a single C source file can contain multiple test cases.
Test files start with the word "test".
Unit tests are located in the ``test`` subdirectory of a component. Tests are written in C, and a single C source file can contain multiple test cases. Test files start with the word "test".
Each test file should include the ``unity.h`` header and the header for the C module to be tested.
@ -22,8 +20,8 @@ Tests are added in a function in the C file as follows:
// Add test here
}
The first argument is a descriptive name for the test, the second argument is an identifier in square brackets.
Identifiers are used to group related test, or tests with specific properties.
- The first argument is a descriptive name for the test.
- The second argument is an identifier in square brackets. Identifiers are used to group related test, or tests with specific properties.
.. note::
There is no need to add a main function with ``UNITY_BEGIN()`` and ``UNITY_END()`` in each test case. ``unity_platform.c`` will run ``UNITY_BEGIN()`` autonomously, and run the test cases, then call ``UNITY_END()``.
@ -44,8 +42,7 @@ See http://www.throwtheswitch.org/unity for more information about writing tests
Multi-device Test Cases
-------------------------
The normal test cases will be executed on one DUT (Device Under Test). However, components that require some form of communication (e.g., GPIO, SPI) require another device to communicate with, thus cannot be tested normal test cases.
Multi-device test cases involve writing multiple test functions, and running them on multiple DUTs.
The normal test cases will be executed on one DUT (Device Under Test). However, components that require some form of communication (e.g., GPIO, SPI) require another device to communicate with, thus cannot be tested normal test cases. Multi-device test cases involve writing multiple test functions, and running them on multiple DUTs.
The following is an example of a multi-device test case:
@ -75,13 +72,13 @@ The following is an example of a multi-device test case:
TEST_CASE_MULTIPLE_DEVICES("gpio multiple devices test example", "[driver]", gpio_master_test, gpio_slave_test);
The macro ``TEST_CASE_MULTIPLE_DEVICES`` is used to declare a multi-device test case.
The first argument is test case name, the second argument is test case description.
From the third argument, up to 5 test functions can be defined, each function will be the entry point of tests running on each DUT.
Running test cases from different DUTs could require synchronizing between DUTs. We provide ``unity_wait_for_signal`` and ``unity_send_signal`` to support synchronizing with UART.
As the scenario in the above example, the slave should get GPIO level after master set level. DUT UART console will prompt and user interaction is required:
- The first argument is test case name.
- The second argument is test case description.
- From the third argument, up to 5 test functions can be defined, each function will be the entry point of tests running on each DUT.
Running test cases from different DUTs could require synchronizing between DUTs. We provide ``unity_wait_for_signal`` and ``unity_send_signal`` to support synchronizing with UART. As the scenario in the above example, the slave should get GPIO level after master set level. DUT UART console will prompt and user interaction is required:
DUT1 (master) console::
@ -98,9 +95,7 @@ Once the signal is sent from DUT2, you need to press "Enter" on DUT1, then DUT1
Multi-stage Test Cases
-----------------------
The normal test cases are expected to finish without reset (or only need to check if reset happens). Sometimes we expect to run some specific tests after certain kinds of reset.
For example, we expect to test if the reset reason is correct after a wakeup from deep sleep. We need to create a deep-sleep reset first and then check the reset reason.
To support this, we can define multi-stage test cases, to group a set of test functions::
The normal test cases are expected to finish without reset (or only need to check if reset happens). Sometimes we expect to run some specific tests after certain kinds of reset. For example, we expect to test if the reset reason is correct after a wakeup from deep sleep. We need to create a deep-sleep reset first and then check the reset reason. To support this, we can define multi-stage test cases, to group a set of test functions::
static void trigger_deepsleep(void)
{
@ -118,16 +113,13 @@ To support this, we can define multi-stage test cases, to group a set of test fu
Multi-stage test cases present a group of test functions to users. It needs user interactions (select cases and select different stages) to run the case.
Tests For Different Targets
---------------------------
------------------------------
Some tests (especially those related to hardware) cannot run on all targets. Below is a guide how
to make your unit tests run on only specified targets.
Some tests (especially those related to hardware) cannot run on all targets. Below is a guide how to make your unit tests run on only specified targets.
1. Wrap your test code by ``!(TEMPORARY_)DISABLED_FOR_TARGETS()`` macros and place them either in
the original test file, or sepeprate the code into files grouped by functions, but make sure all
these files will be processed by the compiler. E.g.: ::
1. Wrap your test code by ``!(TEMPORARY_)DISABLED_FOR_TARGETS()`` macros and place them either in the original test file, or sepeprate the code into files grouped by functions, but make sure all these files will be processed by the compiler. E.g.
::
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP8266)
TEST_CASE("a test that is not ready for esp32 and esp8266 yet", "[]")
@ -135,11 +127,7 @@ to make your unit tests run on only specified targets.
}
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP8266)
Once you need one of the tests to be compiled on a specified target, just modify the targets
in the disabled list. It's more encouraged to use some general conception that can be
described in ``soc_caps.h`` to control the disabling of tests. If this is done but some of the
tests are not ready yet, use both of them (and remove ``!(TEMPORARY_)DISABLED_FOR_TARGETS()``
later). E.g.: ::
Once you need one of the tests to be compiled on a specified target, just modify the targets in the disabled list. It's more encouraged to use some general conception that can be described in ``soc_caps.h`` to control the disabling of tests. If this is done but some of the tests are not ready yet, use both of them (and remove ``!(TEMPORARY_)DISABLED_FOR_TARGETS()`` later). E.g.: ::
#if SOC_SDIO_SLAVE_SUPPORTED
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP64)
@ -150,56 +138,40 @@ to make your unit tests run on only specified targets.
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP64)
#endif //SOC_SDIO_SLAVE_SUPPORTED
2. For test code that you are 100% for sure that will not be supported (e.g. no peripheral at
all), use ``DISABLED_FOR_TARGETS``; for test code that should be disabled temporarily, or due to
lack of runners, etc., use ``TEMPORARY_DISABLED_FOR_TARGETS``.
2. For test code that you are 100% for sure that will not be supported (e.g. no peripheral at all), use ``DISABLED_FOR_TARGETS``; for test code that should be disabled temporarily, or due to lack of runners, etc., use ``TEMPORARY_DISABLED_FOR_TARGETS``.
Some old ways of disabling unit tests for targets, that have obvious disadvantages, are deprecated:
- DON'T put the test code under ``test/target`` folder and use CMakeLists.txt to choose one of the
target folder. This is prevented because test code is more likely to be reused than the
implementations. If you put something into ``test/esp32`` just to avoid building it on esp32s2,
it's hard to make the code tidy if you want to enable the test again on esp32s3.
- DON'T put the test code under ``test/target`` folder and use CMakeLists.txt to choose one of the target folder. This is prevented because test code is more likely to be reused than the implementations. If you put something into ``test/esp32`` just to avoid building it on esp32s2, it's hard to make the code tidy if you want to enable the test again on esp32s3.
- DON'T use ``CONFIG_IDF_TARGET_xxx`` macros to disable the test items any more. This makes it
harder to track disabled tests and enable them again. Also, a black-list style ``#if !disabled``
is preferred to white-list style ``#if CONFIG_IDF_TARGET_xxx``, since you will not silently
disable cases when new targets are added in the future. But for test implementations, it's
allowed to use ``#if CONFIG_IDF_TARGET_xxx`` to pick one of the implementation code.
- DON'T use ``CONFIG_IDF_TARGET_xxx`` macros to disable the test items any more. This makes it harder to track disabled tests and enable them again. Also, a black-list style ``#if !disabled`` is preferred to white-list style ``#if CONFIG_IDF_TARGET_xxx``, since you will not silently disable cases when new targets are added in the future. But for test implementations, it's allowed to use ``#if CONFIG_IDF_TARGET_xxx`` to pick one of the implementation code.
- Test item: some items that will be performed on some targets, but skipped on other
targets. E.g.
- Test item: some items that will be performed on some targets, but skipped on other targets. E.g.
There are three test items SD 1-bit, SD 4-bit and SDSPI. For ESP32-S2, which doesn't have
SD host, among the tests only SDSPI is enabled on ESP32-S2.
There are three test items SD 1-bit, SD 4-bit and SDSPI. For ESP32-S2, which doesn't have SD host, among the tests only SDSPI is enabled on ESP32-S2.
- Test implementation: some code will always happen, but in different ways. E.g.
There is no SDIO PKT_LEN register on ESP8266. If you want to get the length from the slave
as a step in the test process, you can have different implementation code protected by
``#if CONFIG_IDF_TARGET_`` reading in different ways.
There is no SDIO PKT_LEN register on ESP8266. If you want to get the length from the slave as a step in the test process, you can have different implementation code protected by ``#if CONFIG_IDF_TARGET_`` reading in different ways.
But please avoid using ``#else`` macro. When new target is added, the test case will fail at
building stage, so that the maintainer will be aware of this, and choose one of the
implementations explicitly.
But please avoid using ``#else`` macro. When new target is added, the test case will fail at building stage, so that the maintainer will be aware of this, and choose one of the implementations explicitly.
Building Unit Test App
----------------------
Follow the setup instructions in the top-level esp-idf README.
Make sure that ``IDF_PATH`` environment variable is set to point to the path of esp-idf top-level directory.
Follow the setup instructions in the top-level esp-idf README. Make sure that ``IDF_PATH`` environment variable is set to point to the path of esp-idf top-level directory.
Change into ``tools/unit-test-app`` directory to configure and build it:
* ``idf.py menuconfig`` - configure unit test app.
* ``idf.py -T all build`` - build unit test app with tests for each component having tests in the ``test`` subdirectory.
* ``idf.py -T "xxx yyy" build`` - build unit test app with tests for some space-separated specific components (For instance: ``idf.py -T heap build`` - build unit tests only for ``heap`` component directory).
* ``idf.py -T all -E "xxx yyy" build`` - build unit test app with all unit tests, except for unit tests of some components (For instance: ``idf.py -T all -E "ulp mbedtls" build`` - build all unit tests exludes ``ulp`` and ``mbedtls`` components).
.. note::
Due to inherent limitations of Windows command prompt, following syntax has to be used in order to build unit-test-app with multiple components: ``idf.py -T xxx -T yyy build`` or with escaped quoates: ``idf.py -T \`"xxx yyy\`" build`` in PowerShell or ``idf.py -T \^"ssd1306 hts221\^" build`` in Windows command prompt.
Due to inherent limitations of Windows command prompt, following syntax has to be used in order to build unit-test-app with multiple components: ``idf.py -T xxx -T yyy build`` or with escaped quoates: ``idf.py -T \`"xxx yyy\`" build`` in PowerShell or ``idf.py -T \^"ssd1306 hts221\^" build`` in Windows command prompt.
When the build finishes, it will print instructions for flashing the chip. You can simply run ``idf.py flash`` to flash all build output.
You can also run ``idf.py -T all flash`` or ``idf.py -T xxx flash`` to build and flash. Everything needed will be rebuilt automatically before flashing.
@ -251,8 +223,7 @@ Test cases can be run by inputting one of the following:
- An asterisk to run all test cases
``[multi_device]`` and ``[multi_stage]`` tags tell the test runner whether a test case is a multiple devices or multiple stages of test case.
These tags are automatically added by ```TEST_CASE_MULTIPLE_STAGES`` and ``TEST_CASE_MULTIPLE_DEVICES`` macros.
``[multi_device]`` and ``[multi_stage]`` tags tell the test runner whether a test case is a multiple devices or multiple stages of test case. These tags are automatically added by ```TEST_CASE_MULTIPLE_STAGES`` and ``TEST_CASE_MULTIPLE_DEVICES`` macros.
After you select a multi-device test case, it will print sub-menu::
@ -270,10 +241,7 @@ Similar to multi-device test cases, multi-stage test cases will also print sub-m
(1) "trigger_deepsleep"
(2) "check_deepsleep_reset_reason"
First time you execute this case, input ``1`` to run first stage (trigger deepsleep).
After DUT is rebooted and able to run test cases, select this case again and input ``2`` to run the second stage.
The case only passes if the last stage passes and all previous stages trigger reset.
First time you execute this case, input ``1`` to run first stage (trigger deepsleep). After DUT is rebooted and able to run test cases, select this case again and input ``2`` to run the second stage. The case only passes if the last stage passes and all previous stages trigger reset.
Timing Code with Cache Compensated Timer
@ -285,13 +253,9 @@ However, if the instruction or data is not in cache, it needs to be fetched from
Code and data placements can vary between builds, and some arrangements may be more favorable with regards to cache access (i.e., minimizing cache misses). This can technically affect execution speed, however these factors are usually irrelevant as their effect 'average out' over the device's operation.
The effect of the cache on execution speed, however, can be relevant in benchmarking scenarios (espcially microbenchmarks). There might be some variability in measured time
between runs and between different builds. A technique for eliminating for some of the
variability is to place code and data in instruction or data RAM (IRAM/DRAM), respectively. The CPU can access IRAM and DRAM directly, eliminating the cache out of the equation.
However, this might not always be viable as the size of IRAM and DRAM is limited.
The effect of the cache on execution speed, however, can be relevant in benchmarking scenarios (espcially microbenchmarks). There might be some variability in measured time between runs and between different builds. A technique for eliminating for some of the variability is to place code and data in instruction or data RAM (IRAM/DRAM), respectively. The CPU can access IRAM and DRAM directly, eliminating the cache out of the equation. However, this might not always be viable as the size of IRAM and DRAM is limited.
The cache compensated timer is an alternative to placing the code/data to be benchmarked in IRAM/DRAM. This timer uses the processor's internal event counters in order to determine the amount
of time spent on waiting for code/data in case of a cache miss, then subtract that from the recorded wall time.
The cache compensated timer is an alternative to placing the code/data to be benchmarked in IRAM/DRAM. This timer uses the processor's internal event counters in order to determine the amount of time spent on waiting for code/data in case of a cache miss, then subtract that from the recorded wall time.
.. code-block:: c
@ -311,10 +275,10 @@ One limitation of the cache compensated timer is that the task that benchmarked
Mocks
-----
ESP-IDF has a component which integrates the CMock mocking framework.
CMock usually uses Unity as a submodule, but due to some Espressif-internal limitations with CI, we still have Unity as an ordinary module in ESP-IDF.
To use the IDF-supplied Unity component which isn't a submodule, the build system needs to pass an environment variable ``UNITY_IDR`` to CMock.
This variable simply contains the path to the Unity directory in IDF, e.g. ``export "UNITY_DIR=${IDF_PATH}/components/unity/unity"``.
ESP-IDF has a component which integrates the CMock mocking framework. CMock usually uses Unity as a submodule, but due to some Espressif-internal limitations with CI, we still have Unity as an ordinary module in ESP-IDF.
To use the IDF-supplied Unity component which isn't a submodule, the build system needs to pass an environment variable ``UNITY_IDR`` to CMock. This variable simply contains the path to the Unity directory in IDF, e.g. ``export "UNITY_DIR=${IDF_PATH}/components/unity/unity"``.
Refer to :component_file:`cmock/CMock/lib/cmock_generator.rb` to see how the Unity directory is determined in CMock.
An example cmake build command to create mocks of a component inside that component's CMakeLists.txt may look like this:

View File

@ -45,6 +45,7 @@ JTAG 调试
本文将指导如何在 LinuxWindows 和 MacOS 环境下为 {IDF_TARGET_NAME} 安装 OpenOCD并使用 GDB 进行软件调试。除了个别操作系统的安装过程有所差别以外,软件用户界面和使用流程都是一样的。
.. note::
本文使用的图片素材来自于 Ubuntu 16.04 LTS 上 Eclipse Neon 3 软件的截图不同的操作系统Windows MacOS 或者 Linux和 Eclipse 软件版本在用户界面上可能会有细微的差别。
.. _jtag-debugging-how-it-works:
@ -112,7 +113,6 @@ JTAG 正常工作至少需要连接的信号线有TDITDOTCKTMS 和 G
另外,我们还可以从源代码编译 OpenOCD 工具,相关详细信息请参阅 :ref:`jtag-debugging-building-openocd` 章节。
.. _jtag-debugging-configuring-target:
配置 {IDF_TARGET_NAME} 目标板
@ -244,11 +244,8 @@ JTAG 正常工作至少需要连接的信号线有TDITDOTCKTMS 和 G
Linux <building-openocd-linux>
MacOS <building-openocd-macos>
本文档演示所使用的 OpenOCD 是 :ref:`jtag-debugging-setup-openocd` 章节中介绍的预编译好的二进制发行版。
.. highlight:: bash
如果要使用本地从源代码编译的 OpenOCD 程序,需要将相应可执行文件的路径修改为 ``src/openocd``,并设置 ``OPENOCD_SCRIPTS`` 环境变量,这样 OpenOCD 才能找到配置文件。Linux 和 macOS 用户可以执行:
.. code-block:: bash
@ -275,7 +272,6 @@ Windows 用户:
:start-after: run-openocd-src-win
:end-before: ---
.. _jtag-debugging-tips-and-quirks:
注意事项和补充内容
@ -290,7 +286,7 @@ Windows 用户:
相关文档
--------
------------
.. toctree::
:maxdepth: 1

View File

@ -2,29 +2,37 @@
----------
:link_to_translation:`en:[English]`
本节会在 :ref:`Eclipse <jtag-debugging-using-debugger-eclipse>`:ref:`命令行 <jtag-debugging-using-debugger-command-line>` 中分别介绍配置和运行调试器的方法。我们建议你首先通过 :ref:`命令行 <jtag-debugging-using-debugger-command-line>` 检查调试器是否正常工作,然后再转到使用 :ref:`Eclipse <jtag-debugging-using-debugger-eclipse>` 平台。
本节介绍以下几种配置和运行调试器的方法:
* :ref:`jtag-debugging-using-debugger-eclipse`
* :ref:`jtag-debugging-using-debugger-command-line`
* :ref:`jtag-debugging-with-idf-py`
.. _jtag-debugging-using-debugger-eclipse:
在 Eclipse 中使用 GDB
^^^^^^^^^^^^^^^^^^^^^
使用 Eclipse 调试
^^^^^^^^^^^^^^^^^^^^^^^
标准的 Eclipse 安装流程默认安装调试功能,另外我们还可以使用插件来调试,比如 “GDB Hardware Debugging”。这个插件用起来非常方便本指南会详细介绍该插件的使用方法。
.. note::
首先,通过打开 Eclipse 并转到 “Help” > “Install New Software” 来安装 “GDB Hardware Debugging” 插件
建议您首先通过 :ref:`idf.py <jtag-debugging-with-idf-py>`:ref:`命令行 <jtag-debugging-using-debugger-command-line>` 检查调试器是否正常工作,然后再转到使用 :ref:`Eclipse <jtag-debugging-using-debugger-eclipse>` 平台
安装完成后,按照以下步骤配置调试会话。请注意,一些配置参数是通用的,有些则针对特定项目。我们会通过配置 "blink" 示例项目的调试环境来进行展示,请先按照 :doc:`使用 Eclipse IDE 编译和烧写 <../../get-started/eclipse-setup>` 文章介绍的方法将该示例项目添加到 Eclipse 的工作空间。示例项目 :example:`get-started/blink` 的源代码可以在 ESP-IDF 仓库的 :idf:`examples` 目录下找到
标准的 Eclipse 安装流程默认安装调试功能,另外您还可以使用插件来调试,比如 “GDB Hardware Debugging”。这个插件用起来非常方便本指南会详细介绍该插件的使用方法
1. 在 Eclipse 中,进入 `Run` > `Debug Configuration`,会出现一个新的窗口。在窗口的左侧窗格中,双击 “GDB Hardware Debugging” (或者选择 “GDB Hardware Debugging” 然后按下 “New” 按钮)来新建一个配置
首先,打开 Eclipse 并转到 “Help” > “Install New Software” 来安装 “GDB Hardware Debugging” 插件
2. 在右边显示的表单中“Name:” 一栏中输入配置的名称,例如: “Blink checking”
安装完成后,按照以下步骤配置调试会话。请注意,一些配置参数是通用的,有些则针对特定项目。我们会通过配置 "blink" 示例项目的调试环境来进行展示,请先按照 :doc:`使用 Eclipse IDE 编译和烧写 <../../get-started/eclipse-setup>` 介绍的方法将该示例项目添加到 Eclipse 的工作空间。示例项目 :example:`get-started/blink` 的源代码可以在 ESP-IDF 仓库的 :idf:`examples` 目录下找到
3. 在下面的 “Main” 选项卡中, 点击 “Project:” 边上的 “Browse” 按钮,然后选择当前的 “blink” 项目
1. 在 Eclipse 中,进入 `Run` > `Debug Configuration`,会出现一个新的窗口。在窗口的左侧窗格中,双击 “GDB Hardware Debugging”(或者选择 “GDB Hardware Debugging” 然后按下 “New” 按钮)来新建一个配置
4. 在下一行的 “C/C++ Application:” 中,点击 “Browse” 按钮,选择 “blink.elf” 文件。如果 “blink.elf” 文件不存在,那么很有可能该项目还没有编译,请参考 :doc:`使用 Eclipse IDE 编辑和烧写 <../../get-started/eclipse-setup>` 指南中的介绍
2. 在右边显示的表单中“Name:” 一栏中输入配置的名称,例如: “Blink checking”
5. 最后,在 “Build (if required) before launching” 下面点击 “Disable auto build”。
3. 在下面的 “Main” 选项卡中, 点击 “Project:” 边上的 “Browse” 按钮,然后选择当前的 “blink” 项目。
4. 在下一行的 “C/C++ Application:” 中,点击 “Browse” 按钮,选择 “blink.elf” 文件。如果 “blink.elf” 文件不存在,那么很有可能该项目还没有编译,请参考 :doc:`使用 Eclipse IDE 编辑和烧写 <../../get-started/eclipse-setup>` 指南中的介绍。
5. 最后,在 “Build (if required) before launching” 下面点击 “Disable auto build”。
上述步骤 1 - 5 的示例输入如下图所示。
@ -35,9 +43,9 @@
GDB 硬件调试的配置 - Main 选项卡
6. 点击 “Debugger” 选项卡,在 “GDB Command” 栏中输入 ``{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb`` 来调用调试器。
6. 点击 “Debugger” 选项卡,在 “GDB Command” 栏中输入 ``{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb`` 来调用调试器。
7. 更改 “Remote host” 的默认配置,在 “Port number” 下面输入 ``3333``
7. 更改 “Remote host” 的默认配置,在 “Port number” 下面输入 ``3333``
上述步骤 6 - 7 的示例输入如下图所示。
@ -48,7 +56,7 @@
GDB 硬件调试的配置 - Debugger 选项卡
8. 最后一个需要更改默认配置的选项卡是 “Startup” 选项卡。在 “Initialization Commands” 下,取消选中 “Reset and Delay (seconds)” 和 “Halt”然后在下面一栏中输入以下命令
8. 最后一个需要更改默认配置的选项卡是 “Startup” 选项卡。在 “Initialization Commands” 下,取消选中 “Reset and Delay (seconds)” 和 “Halt”然后在下面一栏中输入以下命令
::
@ -57,19 +65,18 @@
set remote hardware-watchpoint-limit 2
.. note::
如果想在启动新的调试会话之前自动更新闪存中的镜像,请在 “Initialization Commands” 文本框的开头添加以下命令行::
如果想在启动新的调试会话之前自动更新闪存中的镜像,请在 “Initialization Commands” 文本框的开头添加以下命令行::
mon reset halt
mon program_esp ${workspace_loc:blink/build/blink.bin} 0x10000 verify
有关 ``program_esp`` 命令的说明请参考 :ref:`jtag-upload-app-debug` 章节。
9. 在 “Load Image and Symbols” 下,取消选中 “Load image” 选项。
9. 在 “Load Image and Symbols” 下,取消选中 “Load image” 选项。
10. 在同一个选项卡中继续往下浏览,建立一个初始断点用来在调试器复位后暂停 CPU。插件会根据 “Set break point at:” 一栏中输入的函数名,在该函数的开头设置断点。选中这一选项,并在相应的字段中输入 ``app_main``
11. 选中 “Resume” 选项,这会使得程序在每次调用步骤 8 中的 ``mon reset halt`` 后恢复,然后在 ``app_main`` 的断点处停止。
11. 选中 “Resume” 选项,这会使得程序在每次调用步骤 8 中的 ``mon reset halt`` 后恢复,然后在 ``app_main`` 的断点处停止。
上述步骤 8 - 11 的示例输入如下图所示。
@ -80,9 +87,9 @@
GDB 硬件调试的配置 - Startup 选项卡
上面的启动序列看起来有些复杂,如果对其中的初始化命令不太熟悉,请查阅 :ref:`jtag-debugging-tip-debugger-startup-commands` 章节获取更多说明。
上面的启动序列看起来有些复杂,如果对其中的初始化命令不太熟悉,请查阅 :ref:`jtag-debugging-tip-debugger-startup-commands` 章节获取更多说明。
12. 如果前面已经完成 :ref:`jtag-debugging-configuring-target` 中介绍的步骤,那么目标正在运行并准备与调试器进行对话。按下 “Debug” 按钮就可以直接调试。否则请按下 “Apply” 按钮保存配置,返回 :ref:`jtag-debugging-configuring-target` 章节进行配置,最后再回到这里开始调试。
12. 如果前面已经完成 :ref:`jtag-debugging-configuring-target` 中介绍的步骤,那么目标正在运行并准备与调试器进行对话。按下 “Debug” 按钮就可以直接调试。否则请按下 “Apply” 按钮保存配置,返回 :ref:`jtag-debugging-configuring-target` 章节进行配置,最后再回到这里开始调试。
一旦所有 1 - 12 的配置步骤都已经完成Eclipse 就会打开 “Debug” 视图,如下图所示。
@ -93,19 +100,19 @@
Eclipse 中的调试视图
如果不太了解 GDB 的常用方法,请查阅 :ref:`jtag-debugging-examples-eclipse` 文章中的调试示例章节 :ref:`jtag-debugging-examples`
如果不太了解 GDB 的常用方法,请查阅 :ref:`jtag-debugging-examples-eclipse` 文章中的调试示例章节 :ref:`jtag-debugging-examples`
.. _jtag-debugging-using-debugger-command-line:
在命令行中使用 GDB
^^^^^^^^^^^^^^^^^^
使用命令行调试
^^^^^^^^^^^^^^^^
1. 为了能够启动调试会话,需要先启动并运行目标,如果还没有完成,请按照 :ref:`jtag-debugging-configuring-target` 中的介绍进行操作。
1. 为了能够启动调试会话,需要先启动并运行目标,如果还没有完成,请按照 :ref:`jtag-debugging-configuring-target` 中的介绍进行操作。
.. highlight:: bash
2. 打开一个新的终端会话并前往待调试的项目目录,比如:
2. 打开一个新的终端会话并前往待调试的项目目录,比如:
::
@ -113,7 +120,7 @@
.. highlight:: none
3. 当启动调试器时,通常需要提供几个配置参数和命令,为了避免每次都在命令行中逐行输入这些命令,我们可以新建一个配置文件,并将其命名为 ``gdbinit``:
3. 当启动调试器时,通常需要提供几个配置参数和命令,为了避免每次都在命令行中逐行输入这些命令,可以新建一个配置文件,并将其命名为 ``gdbinit``:
::
@ -130,7 +137,7 @@
.. highlight:: bash
4. 准备好启动 GDB请在终端中输入以下内容
4. 准备好启动 GDB请在终端中输入以下内容
::
@ -138,7 +145,7 @@
.. highlight:: none
5. 如果前面的步骤已经正确完成,会看到如下所示的输出日志,在日志的最后会出现 ``(gdb)`` 提示符:
5. 如果前面的步骤已经正确完成,会看到如下所示的输出日志,在日志的最后会出现 ``(gdb)`` 提示符:
::
@ -185,9 +192,9 @@
43 xTaskCreate(&blink_task, "blink_task", 512, NULL, 5, NULL);
(gdb)
注意上面日志的倒数第三行显示了调试器已经在 ``app_main()`` 函数的断点处停止,该断点在 ``gdbinit`` 文件中设定。由于处理器已经暂停运行LED 也不会闪烁。如果这也是你看到的现象,你可以开始调试了。
注意上面日志的倒数第三行显示了调试器已经在 ``app_main()`` 函数的断点处停止,该断点在 ``gdbinit`` 文件中设定。由于处理器已经暂停运行LED 也不会闪烁。如果这也是您看到的现象,您可以开始调试了。
如果不太了解 GDB 的常用方法,请查阅 :ref:`jtag-debugging-examples-command-line` 文章中的调试示例章节 :ref:`jtag-debugging-examples`
如果不太了解 GDB 的常用方法,请查阅 :ref:`jtag-debugging-examples-command-line` 文章中的调试示例章节 :ref:`jtag-debugging-examples`
.. _jtag-debugging-with-idf-py:
@ -195,23 +202,23 @@
使用 idf.py 进行调试
^^^^^^^^^^^^^^^^^^^^
我们还可以使用 ``idf.py`` 更方便地执行上述提到的调试命令:
还可以使用 ``idf.py`` 更方便地执行上述提到的调试命令,可以使用以下命令:
1. ``idf.py openocd``
在终端中运行 OpenOCD其配置信息来源于环境变量或者命令行。默认会使用 ``OPENOCD_SCRIPTS`` 环境变量中指定的脚本路径,它是由 ESP-IDF 项目仓库中的导出脚本(``export.sh`` or ``export.bat``)添加到系统环境变量中的。
当然,我们可以在命令行中通过 ``--openocd-scripts`` 来覆盖这个变量的值。
当然,可以在命令行中通过 ``--openocd-scripts`` 参数来覆盖这个变量的值。
.. include:: {IDF_TARGET_PATH_NAME}.inc
:start-after: idf-py-openocd-default-cfg
:end-before: ---
你可以定义 ``OPENOCD_COMMANDS`` 环境变量来指定当前开发板的 JTAG 配置,或者通过 ``--openocd-commands`` 传递该参数。如果这两者都没有被定义,那么 OpenOCD 会使用 |idf-py-def-cfg| 参数来启动。
至于当前开发板的 JTAG 配置,请使用环境变量 ``OPENOCD_COMMANDS`` 或命令行参数 ``--openocd-commands``。如果这两者都没有被定义,那么 OpenOCD 会使用 |idf-py-def-cfg| 参数来启动。
2. ``idf.py gdb``
根据当前项目的 elf 文件自动生成 gdb 启动脚本, 然后会按照 :ref:`jtag-debugging-using-debugger-command-line` 中所描述的步骤启动 GDB。
根据当前项目的 elf 文件自动生成 GDB 启动脚本,然后会按照 :ref:`jtag-debugging-using-debugger-command-line` 中所描述的步骤启动 GDB。
3. ``idf.py gdbtui``
@ -224,7 +231,7 @@
启动 `gdbgui <https://www.gdbgui.com>`_,在浏览器中打开调试器的前端界面。
上述这些命令也可以合并到一起使用,``idf.py`` 会自动将后台进程(比如 openocd最先运行交互式进程图 gdb monitor最后运行。
上述这些命令也可以合并到一起使用,``idf.py`` 会自动将后台进程(比如 openocd最先运行交互式进程如 GDBmonitor最后运行。
常用的组合命令如下所示::

View File

@ -2,39 +2,31 @@
============================
:link_to_translation:`en:[English]`
ESP-IDF
中附带了一个基于 ``Unity`` 的单元测试应用程序框架,且所有的单元测试用例分别保存在
ESP-IDF 仓库中每个组件的 ``test`` 子目录中。
ESP-IDF 中附带了一个基于 ``Unity`` 的单元测试应用程序框架,且所有的单元测试用例分别保存在 ESP-IDF 仓库中每个组件的 ``test`` 子目录中。
添加常规测试用例
----------------
单元测试被添加在相应组件的 ``test`` 子目录中,测试用例写在 C 文件中,一个
C 文件可以包含多个测试用例。测试文件的名字要以 “test” 开头。
单元测试被添加在相应组件的 ``test`` 子目录中,测试用例写在 C 文件中,一个 C 文件可以包含多个测试用例。测试文件的名字要以 “test” 开头。
测试文件需要包含 ``unity.h`` 头文件,此外还需要包含待测试 C
模块需要的头文件。
测试文件需要包含 ``unity.h`` 头文件,此外还需要包含待测试 C 模块需要的头文件。
测试用例需要通过 C 文件中特定的函数来添加,如下所示:
.. code-block:: c
TEST_CASE("test name", "[module name]"
TEST_CASE("test name", "[module name]")
{
// 在这里添加测试用例
}
- 第一个参数是字符串,用来描述当前测试。
- 第二个参数是字符串,用方括号中的标识符来表示,标识符用来对相关测试或具有特定属性的测试进行分组。
- 第一个参数是此测试的描述性名称。
- 第二个参数用方括号中的标识符来表示,标识符用来对相关测试或具有特定属性的测试进行分组。
.. note::
没有必要在每个测试用例中使用 ``UNITY_BEGIN()````UNITY_END()``
来声明主函数的区域, ``unity_platform.c`` 会自动调用 ``UNITY_BEGIN()``\ 然后运行测试用例,最后调用 ``UNITY_END()``
没有必要在每个测试用例中使用 ``UNITY_BEGIN()````UNITY_END()`` 来声明主函数的区域, ``unity_platform.c`` 会自动调用 ``UNITY_BEGIN()`` 然后运行测试用例,最后调用 ``UNITY_END()``
``test`` 子目录应包含 ref`组件 CMakeLists.txt <component-directories>`因为他们本身就是一种组件。ESP-IDF 使用了
``unity`` 测试框架,需要将其指定为组件的依赖项。通常,组件
ref`需要手动指定待编译的源文件 <cmake-file-globbing>`;但是,对于测试组件来说,这个要求被放宽为仅建议将参数 ``SRC_DIRS`` 用于 ``idf_component_register``
``test`` 子目录应包含 :ref`组件 CMakeLists.txt <component-directories>`因为他们本身就是一种组件。ESP-IDF 使用了 ``unity`` 测试框架,需要将其指定为组件的依赖项。通常,组件 :ref`需要手动指定待编译的源文件 <cmake-file-globbing>;但是,对于测试组件来说,这个要求被放宽为仅建议将参数 ``SRC_DIRS`` 用于 ``idf_component_register``。
总的来说,``test`` 子目录下最小的 CMakeLists.txt 文件可能如下所示:
@ -44,82 +36,66 @@ C 文件可以包含多个测试用例。测试文件的名字要以 “test”
INCLUDE_DIRS "."
REQUIRES unity)
更多关于如何在 Unity 下编写测试用例的信息,请查阅
http://www.throwtheswitch.org/unity 。
更多关于如何在 Unity 下编写测试用例的信息,请查阅 http://www.throwtheswitch.org/unity。
添加多设备测试用例
------------------
常规测试用例会在一个 DUTDevice Under Test在试设备上执行.但是,那些需要互相通信的组件(比如
GPIO、SPI需要与其通信的其他设备因此不能使用常规测试用例进行测试。多设备测试用例包括写入多个测试函数并在多个 DUT 进行运行测试。
常规测试用例会在一个 DUTDevice Under Test在试设备上执行。但是那些需要互相通信的组件比如 GPIO、SPI需要其他设备与其通信因此不能使用常规测试用例进行测试。多设备测试用例包括写入多个测试函数并在多个 DUT 进行运行测试。
以下是一个多设备测试用例:
.. code:: c
.. code-block:: c
void gpio_master_test()
{
gpio_config_t slave_config = {
.pin_bit_mask = 1 << MASTER_GPIO_PIN,
.mode = GPIO_MODE_INPUT,
};
gpio_config(&slave_config);
unity_wait_for_signal("output high level");
TEST_ASSERT(gpio_get_level(MASTER_GPIO_PIN) == 1);
}
void gpio_master_test()
{
gpio_config_t slave_config = {
.pin_bit_mask = 1 << MASTER_GPIO_PIN,
.mode = GPIO_MODE_INPUT,
};
gpio_config(&slave_config);
unity_wait_for_signal("output high level");
TEST_ASSERT(gpio_get_level(MASTER_GPIO_PIN) == 1);
}
void gpio_slave_test()
{
gpio_config_t master_config = {
.pin_bit_mask = 1 << SLAVE_GPIO_PIN,
.mode = GPIO_MODE_OUTPUT,
};
gpio_config(&master_config);
gpio_set_level(SLAVE_GPIO_PIN, 1);
unity_send_signal("output high level");
}
void gpio_slave_test()
{
gpio_config_t master_config = {
.pin_bit_mask = 1 << SLAVE_GPIO_PIN,
.mode = GPIO_MODE_OUTPUT,
};
gpio_config(&master_config);
gpio_set_level(SLAVE_GPIO_PIN, 1);
unity_send_signal("output high level");
}
TEST_CASE_MULTIPLE_DEVICES("gpio multiple devices test example", "[driver]", gpio_master_test, gpio_slave_test);
TEST_CASE_MULTIPLE_DEVICES("gpio multiple devices test example", "[driver]", gpio_master_test, gpio_slave_test);
``TEST_CASE_MULTIPLE_DEVICES`` 用来声明多设备测试用例,
- 第一个参数指定测试用例的名字。
- 第二个参数是测试用例的描述。
- 从第三个参数开始,可以指定最多 5 个测试函数,每个函数都是单独运行在一个 DUT 上的测试入口点。
- 从第三个参数开始可以指定最多5个测试函数每个函数都是单独运行在一个
DUT 上的测试入口点。
在不同的 DUT 上运行的测试用例,通常会要求它们之间进行同步。我们提供 ``unity_wait_for_signal````unity_send_signal`` 这两个函数来使用 UART 去支持同步操作。如上例中的场景slave 应该在 master 设置好 GPIO 电平后再去读取 GPIO 电平DUT 的 UART 终端会打印提示信息,并要求用户进行交互。
在不同的 DUT 上运行的测试用例,通常会要求它们之间进行同步。我们提供
``unity_wait_for_signal````unity_send_signal`` 这两个函数来使用 UART
去支持同步操作。如上例中的场景slave 应该在在 master 设置好 GPIO
电平后再去读取 GPIO 电平DUT 的 UART
终端会打印提示信息,并要求用户进行交互。
DUT1master终端
.. code:: bash
DUT1master终端::
Waiting for signal: [output high level]!
Please press "Enter" key once any board send this signal.
DUT2slave终端
.. code:: bash
DUT2slave终端::
Send signal: [output high level]!
一旦 DUT2 发送了该信号,您需要在 DUT1 的终端按回车键,然后 DUT1 会从
``unity_wait_for_signal`` 函数中解除阻塞,并开始更改 GPIO 的电平。
一旦 DUT2 发送了该信号,您需要在 DUT1 的终端按回车键,然后 DUT1 会从 ``unity_wait_for_signal`` 函数中解除阻塞,并开始更改 GPIO 的电平。
添加多阶段测试用例
------------------
常规的测试用例无需重启就会结束(或者仅需要检查是否发生了重启),可有些时候我们想在某些特定类型的重启事件后运行指定的测试代码,例如,我们想在深度睡眠唤醒后检查复位的原因是否正确。首先我们需要出发深度睡眠复位事件,然后检查复位的原因。为了实现这一点,我们可以定义多阶段测试用例来将这些测试函数组合在一起。
.. code:: c
常规的测试用例无需重启就会结束(或者仅需要检查是否发生了重启),可有些时候我们想在某些特定类型的重启事件后运行指定的测试代码,例如,我们想在深度睡眠唤醒后检查复位的原因是否正确。首先我们需要触发深度睡眠复位事件,然后检查复位的原因。为了实现这一点,我们可以定义多阶段测试用例来将这些测试函数组合在一起::
static void trigger_deepsleep(void)
{
@ -142,14 +118,15 @@ DUT2slave终端
某些测试(尤其与硬件相关的)无法在所有的芯片上执行。请参照本节让你的单元测试只在其中一部分芯片上执行。
1. 使用宏 ``!(TEMPORARY_)DISABLED_FOR_TARGETS()`` 保护你的测试代码,并将其放于原来的位置,或者放在另外准备以功能区分的文件。但请确保所有这些文件都会被编译器处理到。例: ::
1. 使用宏 ``!(TEMPORARY_)DISABLED_FOR_TARGETS()`` 包装你的测试代码,并将其放于原始的测试文件中,或将代码分成按功能分组的文件。但请确保所有这些文件都会由编译器处理。例::
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP8266) TEST_CASE("a test that is not ready for esp32 and esp8266 yet", "[]")
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP8266)
TEST_CASE("a test that is not ready for esp32 and esp8266 yet", "[]")
{
}
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP8266)
一旦你需要其中一个测试在某个芯片上被编译,只需要修改禁止的芯片列表。我们更鼓励使用一些通用的概念(能在 ``soc_caps.h`` 中被清楚描述)来禁止某些单元测试。如果你已经这样做,但有一些测试还没有在新的芯片版本中被调试通过,请同时使用上述两种方法,当调试完成后再移除 ``!(TEMPORARY_)DISABLED_FOR_TARGETS()`` 。例: ::
一旦你需要其中一个测试在某个芯片上被编译,只需要修改禁止的芯片列表。我们更鼓励使用一些通用的概念(能在 ``soc_caps.h`` 中被清楚描述)来禁止某些单元测试。如果你已经这样做,但有一些测试还没有在新的芯片版本中被调试通过,请同时使用上述两种方法,当调试完成后再移除 ``!(TEMPORARY_)DISABLED_FOR_TARGETS()`` 。例: ::
#if SOC_SDIO_SLAVE_SUPPORTED
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP64)
@ -160,96 +137,84 @@ DUT2slave终端
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP64)
#endif //SOC_SDIO_SLAVE_SUPPORTED
2. 对于某些你绝对肯定不可能被支持的测试(例如,芯片根本没有该外设),使用 ``DISABLED_FOR_TARGETS`` 来禁止该测试;对于其他只是临时性需要关闭的(例如,没有 runner 资源等),使用 ``TEMPORARY_DISABLED_FOR_TARGETS`` 来暂时关闭该测试。
2. 对于某些你确定不会支持的测试(例如,芯片根本没有该外设),使用 ``DISABLED_FOR_TARGETS`` 来禁止该测试;对于其他只是临时性需要关闭的(例如,没有 runner 资源等),使用 ``TEMPORARY_DISABLED_FOR_TARGETS`` 来暂时关闭该测试。
请勿继续使用一些旧的禁止单元测试在一些芯片上执行的做法,因为他们具有明显的坏处
一些禁用目标芯片测试用例的旧方法,由于它们具有明显的缺陷,已经被废弃,请勿继续使用
- 请勿将测试代码放在 ``test/芯片版本`` 目录下面,然后用 CMakeLists.txt 来选择其中一个进行编译。这是因为测试代码比实现代码更容易被复用。如果你将一些代码放在 ``test/esp32`` 目录下来避免 esp32s2 芯片执行它,一旦你需要在新的芯片(比如 esp32s3 )中启用该测试,你会发现这种结构非常难以保持代码的整洁。
- 请勿继续使用 ``CONFIG_IDF_TARGET_xxx`` 宏来禁止某些测试在一些芯片上编译。这种 方法会让被禁止的测试项目难以追踪和重新打开。并且,相比于白名单式的 ``#if CONFIG_IDF_TARGET_xxx`` ,黑名单式的 ``#if !disabled`` 能避免新芯片引入时,这些测试被自动关闭。
但对于用于测试的一些实现, ``#if CONFIG_IDF_TARGET_xxx`` 仍可用于给不同芯片版本选择实现代码。测试项目和测试实现区分如下:
- 请勿继续使用 ``CONFIG_IDF_TARGET_xxx`` 宏来禁止某些测试在一些芯片上编译。这种 方法会让被禁止的测试项目难以追踪和重新打开。并且,相比于白名单式的 ``#if CONFIG_IDF_TARGET_xxx`` ,黑名单式的 ``#if !disabled`` 能避免新芯片引入时,这些测试被自动关闭。但对于用于测试的一些实现, ``#if CONFIG_IDF_TARGET_xxx`` 仍可用于给不同芯片版本选择实现代码。测试项目和测试实现区分如下:
- 测试项目:某些你会在一些芯片上执行,而在另外一些上跳过的项目,例如:
有三个测试项目 SD 1-bit 、 SD 4-bit 和 SDSPI 。对于不支持 SD Host 外设的ESP32-S2 芯片,只有 SDSPI 一个项目需要被执行。
- 测试实现:某些代码永远会发生,但采取不同的做法。例如:
- 测试实现:某些代码始终会发生,但采取不同的做法。例如:
ESP8266 芯片没有 SDIO_PKT_LEN 寄存器。如果在测试过程中需要获取从机准备好的数据长度,你可以用 ``#if CONFIG_IDF_TARGET_`` 宏来选择读取该长度的不同方法实现
ESP8266 芯片没有 SDIO_PKT_LEN 寄存器。如果在测试过程中需要获取从设备的数据长度,你可以用不同方式读取的 ``#if CONFIG_IDF_TARGET_`` 宏来保护不同的实现代码
但请注意避免使用 ``#else`` 宏。这样当新芯片被引入时,测试就会在编译阶段失败,提示维护者去显示选择一个正确的测试实现。
编译单元测试程序
----------------
按照 esp-idf 顶层目录的 README 文件中的说明进行操作,请确保 ``IDF_PATH``
环境变量已经被设置指向了 esp-idf 的顶层目录。
按照 esp-idf 顶层目录的 README 文件中的说明进行操作,请确保 ``IDF_PATH`` 环境变量已经被设置指向了 esp-idf 的顶层目录。
切换到 ``tools/unit-test-app`` 目录下进行配置和编译:
- ``idf.py menuconfig`` - 配置单元测试程序。
* ``idf.py menuconfig`` - 配置单元测试程序。
* ``idf.py -T all build`` - 编译单元测试程序,测试每个组件 ``test`` 子目录下的用例。
* ``idf.py -T "xxx yyy" build`` - 编译单元测试程序,测试指定的组件。(如 ``idf.py -T heap build`` - 仅对 ``heap`` 组件目录下的单元测试程序进行编译)
* ``idf.py -T all -E "xxx yyy" build`` - 编译单元测试程序,测试除指定组件之外的所有组件。(例如 ``idf.py -T all -E "ulp mbedtls" build`` - 编译所有的单元测试,不包括 ``ulp````mbedtls`` 组件。)
- ``idf.py -T all build`` - 编译单元测试程序,测试每个组件 ``test``
子目录下的用例。
.. note::
- ``idf.py -T "xxx yyy" build`` - 编译单元测试程序,测试指定的组件。
由于 Windows 命令提示符固有限制,需使用以下语法来编译多个组件的单元测试程序:``idf.py -T xxx -T yyy build`` 或者在 PowerShell 中使用 ``idf.py -T \`"xxx yyy\`" build``,在 Windows 命令提示符中使用 ``idf.py -T \^"ssd1306 hts221\^" build``
当编译完成时,它会打印出烧写芯片的指令。您只需要运行 ``idf.py flash`` 即可烧写所有编译输出的文件。
- ``idf.py -T all -E "xxx yyy" build`` -
编译单元测试程序,测试所有(除开指定)的组件。(例如
``idf.py -T all -E "ulp mbedtls" build`` -
编译所有的单元测试,不包括 ``ulp````mbedtls`` 组件。)
当编译完成时,它会打印出烧写芯片的指令。您只需要运行 ``idf.py flash``
即可烧写所有编译输出的文件。
您还可以运行 ``idf.py -T all flash`` 或者
``idf.py -T xxx flash``
来编译并烧写,所有需要的文件都会在烧写之前自动重新编译。
您还可以运行 ``idf.py -T all flash`` 或者 ``idf.py -T xxx flash`` 来编译并烧写,所有需要的文件都会在烧写之前自动重新编译。
使用 ``menuconfig`` 可以设置烧写测试程序所使用的串口。
运行单元测试
------------
--------------
烧写完成后重启 {IDF_TARGET_NAME} 它将启动单元测试程序。
当单元测试应用程序空闲时,输入回车键,它会打印出测试菜单,其中包含所有的测试项目
当单元测试应用程序空闲时,输入回车键,它会打印出测试菜单,其中包含所有的测试项目::
.. code::
Here's the test menu, pick your combo:
(1) "esp_ota_begin() verifies arguments" [ota]
(2) "esp_ota_get_next_update_partition logic" [ota]
(3) "Verify bootloader image in flash" [bootloader_support]
(4) "Verify unit test app image" [bootloader_support]
(5) "can use new and delete" [cxx]
(6) "can call virtual functions" [cxx]
(7) "can use static initializers for non-POD types" [cxx]
(8) "can use std::vector" [cxx]
(9) "static initialization guards work as expected" [cxx]
(10) "global initializers run in the correct order" [cxx]
(11) "before scheduler has started, static initializers work correctly" [cxx]
(12) "adc2 work with wifi" [adc]
(13) "gpio master/slave test example" [ignore][misc][test_env=UT_T2_1][multi_device]
(1) "gpio_master_test"
(2) "gpio_slave_test"
(14) "SPI Master clockdiv calculation routines" [spi]
(15) "SPI Master test" [spi][ignore]
(16) "SPI Master test, interaction of multiple devs" [spi][ignore]
(17) "SPI Master no response when switch from host1 (HSPI) to host2 (VSPI)" [spi]
(18) "SPI Master DMA test, TX and RX in different regions" [spi]
(19) "SPI Master DMA test: length, start, not aligned" [spi]
(20) "reset reason check for deepsleep" [{IDF_TARGET_PATH_NAME}][test_env=UT_T2_1][multi_stage]
(1) "trigger_deepsleep"
(2) "check_deepsleep_reset_reason"
Here's the test menu, pick your combo:
(1) "esp_ota_begin() verifies arguments" [ota]
(2) "esp_ota_get_next_update_partition logic" [ota]
(3) "Verify bootloader image in flash" [bootloader_support]
(4) "Verify unit test app image" [bootloader_support]
(5) "can use new and delete" [cxx]
(6) "can call virtual functions" [cxx]
(7) "can use static initializers for non-POD types" [cxx]
(8) "can use std::vector" [cxx]
(9) "static initialization guards work as expected" [cxx]
(10) "global initializers run in the correct order" [cxx]
(11) "before scheduler has started, static initializers work correctly" [cxx]
(12) "adc2 work with wifi" [adc]
(13) "gpio master/slave test example" [ignore][misc][test_env=UT_T2_1][multi_device]
(1) "gpio_master_test"
(2) "gpio_slave_test"
(14) "SPI Master clockdiv calculation routines" [spi]
(15) "SPI Master test" [spi][ignore]
(16) "SPI Master test, interaction of multiple devs" [spi][ignore]
(17) "SPI Master no response when switch from host1 (HSPI) to host2 (VSPI)" [spi]
(18) "SPI Master DMA test, TX and RX in different regions" [spi]
(19) "SPI Master DMA test: length, start, not aligned" [spi]
(20) "reset reason check for deepsleep" [{IDF_TARGET_PATH_NAME}][test_env=UT_T2_1][multi_stage]
(1) "trigger_deepsleep"
(2) "check_deepsleep_reset_reason"
常规测试用例会打印用例名字和描述,主从测试用例还会打印子菜单(已注册的测试函数的名字)。
可以输入以下任意一项来运行测试用例:
- 引号中测试用例的名字,运行单个测试用例。
- 引号中写入测试用例的名字,运行单个测试用例。
- 测试用例的序号,运行单个测试用例。
@ -257,31 +222,74 @@ DUT2slave终端
- 星号,运行所有测试用例。
``[multi_device]````[multi_stage]``
标签告诉测试运行者该用例是多设备测试还是多阶段测试。这些标签由
``TEST_CASE_MULTIPLE_STAGES````TEST_CASE_MULTIPLE_DEVICES``
宏自动生成。
``[multi_device]````[multi_stage]``标签告诉测试运行者该用例是多设备测试还是多阶段测试。这些标签由 ``TEST_CASE_MULTIPLE_STAGES````TEST_CASE_MULTIPLE_DEVICES`` 宏自动生成。
一旦选择了多设备测试用例,它会打印一个子菜单
一旦选择了多设备测试用例,它会打印一个子菜单::
.. code::
Running gpio master/slave test example...
gpio master/slave test example
(1) "gpio_master_test"
(2) "gpio_slave_test"
Running gpio master/slave test example...
gpio master/slave test example
(1) "gpio_master_test"
(2) "gpio_slave_test"
您需要输入数字以选择在 DUT 上运行的测试。
与多设备测试用例相似,多阶段测试用例也会打印子菜单
与多设备测试用例相似,多阶段测试用例也会打印子菜单::
.. code::
Running reset reason check for deepsleep...
reset reason check for deepsleep
(1) "trigger_deepsleep"
(2) "check_deepsleep_reset_reason"
Running reset reason check for deepsleep...
reset reason check for deepsleep
(1) "trigger_deepsleep"
(2) "check_deepsleep_reset_reason"
第一次执行此用例时,输入 ``1`` 来运行第一阶段(触发深度睡眠)。在重启 DUT 并再次选择运行此用例后,输入 ``2`` 来运行第二阶段。只有在最后一个阶段通过并且之前所有的阶段都成功触发了复位的情况下,该测试才算通过。
第一次执行此用例时,输入 ``1`` 来运行第一阶段(触发深度睡眠)。在重启
DUT 并再次选择运行此用例后,输入 ``2``
来运行第二阶段。只有在最后一个阶段通过并且之前所有的阶段都成功触发了复位的情况下,该测试才算通过。
带缓存补偿定时器的定时代码
-----------------------------------------
存储在外部存储器(如 SPI Flash 和 SPI RAM中的指令和数据是通过 CPU 的统一指令和数据缓存来访问的。当代码或数据在缓存中时,访问速度会非常快(即缓存命中)。
然而,如果指令或数据不在缓存中,则需要从外部内存中获取(即缓存缺失)。访问外部存储器的速度明显较慢,因为 CPU 在等待从外部存储器获取指令或数据时会陷入停滞。这导致整体代码执行速度会依据缓存命中或缓存缺失的次数而变化。
在不同的编译中,代码和数据的位置可能会有所不同,一些可能会更有利于缓存访问(即,最大限度地减少缓存缺失)。理论上说这会影响执行速度,但这些因素通常却是无关紧要,因为它们的影响会在设备的运行过程中“平均化”。
然而,高速缓存对执行速度的影响可能与基准测试场景(尤其是微基准测试)有关。每次运行和构建时的测量时间可能会有所差异,消除部分差异的方法之一是将代码和数据分别放在指令或数据 RAMIRAM/DRAM中。CPU 可以直接访问 IRAM 和 DRAM从而消除了高速缓存的影响因素。然而由于 IRAM 和 DRAM 容量有限,该方法并不总是可行。
缓存补偿定时器是将要基准测试的代码/数据放置在 IRAM/DRAM 中的替代方法,该计时器使用处理器的内部事件计数器来确定在发生高速缓存未命中时等待代码/数据所花费的时间,然后从记录的实时时间中减去该时间。
.. code-block:: c
// Start the timer
ccomp_timer_start();
// Function to time
func_code_to_time();
// Stop the timer, and return the elapsed time in microseconds relative to
// ccomp_timer_start
int64_t t = ccomp_timer_stop();
缓存补偿定时器的限制之一是基准功能必须固定在一个内核上。这是由于每个内核都有自己的事件计数器,这些事件计数器彼此独立。例如,如果在一个内核上调用 ``ccomp_timer_start``,使调度器进入睡眠状态,唤醒并在在另一个内核上重新调度,那么对应的 ``ccomp_timer_stop`` 将无效。
Mocks
----------
ESP-IDF 有一个集成 CMock mocking 框架的组件。CMock 通常使用 Unity 作为一个子模块,但由于一些 Espressif 内部 CI 的限制,我们仍然将 Unity 作为 ESP-IDF 中的一个普通模块。
要使用 IDF 提供的 Unity 组件(不是子模块),构建系统需要传递一个环境变量 ``UNITY_IDR`` 给 CMock。该变量仅包含 IDF 中 Unity 目录的路径,如 ``export "UNITY_DIR=${IDF_PATH}/components/unity/unity"``
关于 CMock 中 Unity 目录是如何确定的,请参考 :component_file:`cmock/CMock/lib/cmock_generator.rb`
在组件的 CMakeLists.txt 中创建组件的 mock 的 cmake 编译命令可能如下所示:
.. code-block:: cmake
add_custom_command(
OUTPUT ${MOCK_OUTPUT}
COMMAND ruby ${CMOCK_DIR}/lib/cmock.rb -o${CMAKE_CURRENT_SOURCE_DIR}/mock/mock_config.yaml ${MOCK_HEADERS}
COMMAND ${CMAKE_COMMAND} -E env "UNITY_DIR=${IDF_PATH}/components/unity/unity" ruby ${CMOCK_DIR}/lib/cmock.rb -o${CMAKE_CURRENT_SOURCE_DIR}/mock/mock_config.yaml ${MOCK_HEADERS}
)
${MOCK_OUTPUT} 包含所有 CMock 生成的输出文件,${MOCK_HEADERS} 包含所有要 mock 的头文件, ${CMOCK_DIR} 需要设置为 IDF 内的 CMock 目录。${CMAKE_COMMAND} 会自动设置。
更多关于 CMock 如何工作以及如何创建和使用 mock 的信息,请参考 :component_file:`cmock/CMock/docs/CMock_Summary.md`