mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
add updates to some files in api guides
modified errors in making html add modifications based on the first review modified grammar problems of the original text
This commit is contained in:
parent
0be0859e73
commit
8070746530
@ -1,5 +1,7 @@
|
|||||||
ULP coprocessor (Legacy GNU Make)
|
The ULP Coprocessor (Legacy GNU Make)
|
||||||
=================================
|
======================================
|
||||||
|
|
||||||
|
:link_to_translation:`zh_CN:[中文]`
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
@ -9,26 +11,26 @@ ULP coprocessor (Legacy GNU Make)
|
|||||||
|
|
||||||
.. include:: ../gnu-make-legacy.rst
|
.. include:: ../gnu-make-legacy.rst
|
||||||
|
|
||||||
ULP (Ultra Low Power) coprocessor is a simple FSM which is designed to perform measurements using ADC, temperature sensor, and external I2C sensors, while main processors are in deep sleep mode. ULP coprocessor can access RTC_SLOW_MEM memory region, and registers in RTC_CNTL, RTC_IO, and SARADC peripherals. ULP coprocessor uses fixed-width 32-bit instructions, 32-bit memory addressing, and has 4 general purpose 16-bit registers.
|
The ULP (Ultra Low Power) coprocessor is a simple FSM (Finite State Machine) which is designed to perform measurements using the ADC, temperature sensor, and external I2C sensors, while the main processors are in deep sleep mode. The ULP coprocessor can access the RTC_SLOW_MEM memory region, and registers in RTC_CNTL, RTC_IO, and SARADC peripherals. The ULP coprocessor uses fixed-width 32-bit instructions, 32-bit memory addressing, and has 4 general-purpose 16-bit registers.
|
||||||
|
|
||||||
Installing the toolchain
|
Installing the Toolchain
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
ULP coprocessor code is written in assembly and compiled using the `binutils-esp32ulp toolchain`_.
|
The ULP coprocessor code is written in assembly and compiled using the `binutils-esp32ulp toolchain`.
|
||||||
|
|
||||||
1. Download pre-built binaries of the latest toolchain release from:
|
1. Download pre-built binaries of the latest toolchain release from:
|
||||||
https://github.com/espressif/binutils-esp32ulp/releases.
|
https://github.com/espressif/binutils-esp32ulp/releases.
|
||||||
|
|
||||||
2. Extract the toolchain into a directory, and add the path to the ``bin/`` directory of the toolchain to the ``PATH`` environment variable.
|
2. Extract the toolchain into a directory, and add the path to the ``bin/`` directory of the toolchain to the ``PATH`` environment variable.
|
||||||
|
|
||||||
Compiling ULP code
|
Compiling the ULP Code
|
||||||
------------------
|
------------------------
|
||||||
|
|
||||||
To compile ULP code as part of a component, the following steps must be taken:
|
To compile the ULP code as part of the component, the following steps must be taken:
|
||||||
|
|
||||||
1. ULP code, written in assembly, must be added to one or more files with `.S` extension. These files must be placed into a separate directory inside component directory, for instance `ulp/`.
|
1. The ULP code, written in assembly, must be added to one or more files with `.S` extension. These files must be placed into a separate directory inside the component directory, for instance `ulp/`.
|
||||||
|
|
||||||
.. note: This directory should not be added to the ``COMPONENT_SRCDIRS`` environment variable. The logic behind this is that the ESP-IDF build system will compile files found in ``COMPONENT_SRCDIRS`` based on their extensions. For ``.S`` files, ``xtensa-esp32-elf-as`` assembler is used. This is not desirable for ULP assembly files, so the easiest way to achieve the distinction is by placing ULP assembly files into a separate directory.
|
.. note: This directory should not be added to the ``COMPONENT_SRCDIRS`` environment variable. The logic behind this is that the ESP-IDF build system will compile files found in ``COMPONENT_SRCDIRS`` based on their extensions. For ``.S`` files, ``xtensa-esp32-elf-as`` assembler is used. This is not desirable for ULP assembly files, thus the easiest way to achieve this distinction is by placing ULP assembly files into a separate directory.
|
||||||
|
|
||||||
2. Modify the component makefile, adding the following::
|
2. Modify the component makefile, adding the following::
|
||||||
|
|
||||||
@ -55,28 +57,28 @@ To compile ULP code as part of a component, the following steps must be taken:
|
|||||||
|
|
||||||
Inside, the build system will take the following steps to build ULP program:
|
Inside, the build system will take the following steps to build ULP program:
|
||||||
|
|
||||||
1. **Run each assembly file (foo.S) through C preprocessor.** This step generates the preprocessed assembly files (foo.ulp.pS) in the component build directory. This step also generates dependency files (foo.ulp.d).
|
1. **Run each assembly file** (``foo.S``) **through the C preprocessor.** This step generates the preprocessed assembly files (``foo.ulp.pS``) in the component build directory. This step also generates dependency files (``foo.ulp.d``).
|
||||||
|
|
||||||
2. **Run preprocessed assembly sources through assembler.** This produces objects (foo.ulp.o) and listing (foo.ulp.lst) files. Listing files are generated for debugging purposes and are not used at later stages of build process.
|
2. **Run the preprocessed assembly sources through the assembler.** This produces object (``foo.ulp.o``) and listing (``foo.ulp.lst``) files. Listing files are generated for debugging purposes and are not used at later stages of the build process.
|
||||||
|
|
||||||
3. **Run linker script template through C preprocessor.** The template is located in components/ulp/ld directory.
|
3. **Run the linker script template through the C preprocessor.** The template is located in ``components/ulp/ld`` directory.
|
||||||
|
|
||||||
4. **Link object files into an output ELF file** (ulp_app_name.elf). Map file (ulp_app_name.map) generated at this stage may be useful for debugging purposes.
|
4. **Link the object files into an output ELF file** (``ulp_app_name.elf``). The Map file (``ulp_app_name.map``) generated at this stage may be useful for debugging purposes.
|
||||||
|
|
||||||
5. **Dump contents of the ELF file into binary** (ulp_app_name.bin) for embedding into the application.
|
5. **Dump the contents of the ELF file into a binary** (``ulp_app_name.bin``) which can then be embedded into the application.
|
||||||
|
|
||||||
6. **Generate list of global symbols** (ulp_app_name.sym) in the ELF file using esp32ulp-elf-nm.
|
6. **Generate a list of global symbols** (``ulp_app_name.sym``) in the ELF file using ``esp32ulp-elf-nm``.
|
||||||
|
|
||||||
7. **Create LD export script and header file** (ulp_app_name.ld and ulp_app_name.h) containing the symbols from ulp_app_name.sym. This is done using esp32ulp_mapgen.py utility.
|
7. **Create an LD export script and header file** (``ulp_app_name.ld`` and ``ulp_app_name.h``) containing the symbols from ``ulp_app_name.sym``. This is done using the ``esp32ulp_mapgen.py`` utility.
|
||||||
|
|
||||||
8. **Add the generated binary to the list of binary files** to be emedded into the application.
|
8. **Add the generated binary to the list of binary files** to be embedded into the application.
|
||||||
|
|
||||||
Accessing ULP program variables
|
Accessing the ULP Program Variables
|
||||||
-------------------------------
|
------------------------------------
|
||||||
|
|
||||||
Global symbols defined in the ULP program may be used inside the main program.
|
Global symbols defined in the ULP program may be used inside the main program.
|
||||||
|
|
||||||
For example, ULP program may define a variable ``measurement_count`` which will define the number of ADC measurements the program needs to make before waking up the chip from deep sleep::
|
For example, the ULP program may define a variable ``measurement_count`` which will define the number of the ADC measurements the program needs to make before waking up the chip from deep sleep::
|
||||||
|
|
||||||
.global measurement_count
|
.global measurement_count
|
||||||
measurement_count: .long 0
|
measurement_count: .long 0
|
||||||
@ -85,19 +87,19 @@ For example, ULP program may define a variable ``measurement_count`` which will
|
|||||||
move r3, measurement_count
|
move r3, measurement_count
|
||||||
ld r3, r3, 0
|
ld r3, r3, 0
|
||||||
|
|
||||||
Main program needs to initialize this variable before ULP program is started. Build system makes this possible by generating a ``$(ULP_APP_NAME).h`` and ``$(ULP_APP_NAME).ld`` files which define global symbols present in the ULP program. This files include each global symbol defined in the ULP program, prefixed with ``ulp_``.
|
The main program needs to initialize this variable before the ULP program is started. The build system makes this possible by generating ``$(ULP_APP_NAME).h`` and ``$(ULP_APP_NAME).ld`` files which define global symbols present in the ULP program. Each global symbol defined in the ULP program is included in these files and are prefixed with ``ulp_``.
|
||||||
|
|
||||||
The header file contains declaration of the symbol::
|
The header file contains the declaration of the symbol::
|
||||||
|
|
||||||
extern uint32_t ulp_measurement_count;
|
extern uint32_t ulp_measurement_count;
|
||||||
|
|
||||||
Note that all symbols (variables, arrays, functions) are declared as ``uint32_t``. For functions and arrays, take address of the symbol and cast to the appropriate type.
|
Note that all symbols (variables, arrays, functions) are declared as ``uint32_t``. For functions and arrays, take the address of the symbol and cast it to the appropriate type.
|
||||||
|
|
||||||
The generated linker script file defines locations of symbols in RTC_SLOW_MEM::
|
The generated linker script file defines locations of symbols in RTC_SLOW_MEM::
|
||||||
|
|
||||||
PROVIDE ( ulp_measurement_count = 0x50000060 );
|
PROVIDE ( ulp_measurement_count = 0x50000060 );
|
||||||
|
|
||||||
To access ULP program variables from the main program, include the generated header file and use variables as one normally would::
|
To access the ULP program variables from the main program, the generated header file should be included using an ``include`` statement. This will allow the ULP program variables to be accessed as regular variables::
|
||||||
|
|
||||||
#include "ulp_app_name.h"
|
#include "ulp_app_name.h"
|
||||||
|
|
||||||
@ -106,20 +108,20 @@ To access ULP program variables from the main program, include the generated hea
|
|||||||
ulp_measurement_count = 64;
|
ulp_measurement_count = 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
Note that ULP program can only use lower 16 bits of each 32-bit word in RTC memory, because the registers are 16-bit, and there is no instruction to load from high part of the word.
|
Note that the ULP program can only use lower 16 bits of each 32-bit word in RTC memory, because the registers are 16-bit, and there is no instruction to load from the high part of the word.
|
||||||
|
|
||||||
Likewise, ULP store instruction writes register value into the lower 16 bit part of the 32-bit word. Upper 16 bits are written with a value which depends on the address of the store instruction, so when reading variables written by the ULP, main application needs to mask upper 16 bits, e.g.::
|
Likewise, the ULP store instruction writes register value into the lower 16 bits part of the 32-bit word. The upper 16 bits are written with a value which depends on the address of the store instruction, thus when reading variables written by the ULP, the main application needs to mask the upper 16 bits, e.g.::
|
||||||
|
|
||||||
printf("Last measurement value: %d\n", ulp_last_measurement & UINT16_MAX);
|
printf("Last measurement value: %d\n", ulp_last_measurement & UINT16_MAX);
|
||||||
|
|
||||||
Starting the ULP program
|
Starting the ULP Program
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
To run a ULP program, main application needs to load the ULP program into RTC memory using ``ulp_load_binary`` function, and then start it using ``ulp_run`` function.
|
To run a ULP program, the main application needs to load the ULP program into RTC memory using the ``ulp_load_binary`` function, and then start it using the ``ulp_run`` function.
|
||||||
|
|
||||||
Note that "Enable Ultra Low Power (ULP) Coprocessor" option must be enabled in menuconfig in order to reserve memory for the ULP. "RTC slow memory reserved for coprocessor" option must be set to a value sufficient to store ULP code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one.
|
Note that "Enable Ultra Low Power (ULP) Coprocessor" option must be enabled in menuconfig to reserve memory for the ULP. "RTC slow memory reserved for coprocessor" option must be set to a value sufficient to store ULP code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one.
|
||||||
|
|
||||||
Each ULP program is embedded into the ESP-IDF application as a binary blob. Application can reference this blob and load it in the following way (suppose ULP_APP_NAME was defined to ``ulp_app_name``::
|
Each ULP program is embedded into the ESP-IDF application as a binary blob. The application can reference this blob and load it in the following way (suppose ULP_APP_NAME was defined to ``ulp_app_name``)::
|
||||||
|
|
||||||
extern const uint8_t bin_start[] asm("_binary_ulp_app_name_bin_start");
|
extern const uint8_t bin_start[] asm("_binary_ulp_app_name_bin_start");
|
||||||
extern const uint8_t bin_end[] asm("_binary_ulp_app_name_bin_end");
|
extern const uint8_t bin_end[] asm("_binary_ulp_app_name_bin_end");
|
||||||
@ -133,34 +135,32 @@ Each ULP program is embedded into the ESP-IDF application as a binary blob. Appl
|
|||||||
|
|
||||||
.. doxygenfunction:: ulp_load_binary
|
.. doxygenfunction:: ulp_load_binary
|
||||||
|
|
||||||
Once the program is loaded into RTC memory, application can start it, passing the address of the entry point to ``ulp_run`` function::
|
Once the program is loaded into RTC memory, the application can start it, passing the address of the entry point to the ``ulp_run`` function::
|
||||||
|
|
||||||
ESP_ERROR_CHECK( ulp_run(&ulp_entry - RTC_SLOW_MEM) );
|
ESP_ERROR_CHECK( ulp_run(&ulp_entry - RTC_SLOW_MEM) );
|
||||||
|
|
||||||
.. doxygenfunction:: ulp_run
|
.. doxygenfunction:: ulp_run
|
||||||
|
|
||||||
Declaration of the entry point symbol comes from the above mentioned generated header file, ``$(ULP_APP_NAME).h``. In assembly source of the ULP application, this symbol must be marked as ``.global``::
|
Declaration of the entry point symbol comes from the generated header file mentioned above, ``$(ULP_APP_NAME).h``. In the assembly source of the ULP application, this symbol must be marked as ``.global``::
|
||||||
|
|
||||||
|
|
||||||
.global entry
|
.global entry
|
||||||
entry:
|
entry:
|
||||||
/* code starts here */
|
/* code starts here */
|
||||||
|
|
||||||
|
ULP Program Flow
|
||||||
ULP program flow
|
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
ULP coprocessor is started by a timer. The timer is started once ``ulp_run`` is called. The timer counts a number of RTC_SLOW_CLK ticks (by default, produced by an internal 150kHz RC oscillator). The number of ticks is set using ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers (x = 0..4). When starting the ULP for the first time, ``SENS_ULP_CP_SLEEP_CYC0_REG`` will be used to set the number of timer ticks. Later the ULP program can select another ``SENS_ULP_CP_SLEEP_CYCx_REG`` register using ``sleep`` instruction.
|
The ULP coprocessor is started by a timer. The timer is started once ``ulp_run`` is called. The timer counts the number of RTC_SLOW_CLK ticks (by default, produced by an internal 150 kHz RC oscillator). The number of ticks is set using ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers (x = 0..4). When starting the ULP for the first time, ``SENS_ULP_CP_SLEEP_CYC0_REG`` will be used to set the number of timer ticks. Later the ULP program can select another ``SENS_ULP_CP_SLEEP_CYCx_REG`` register using the ``sleep`` instruction.
|
||||||
|
|
||||||
The application can set ULP timer period values (SENS_ULP_CP_SLEEP_CYCx_REG, x = 0..4) using ``ulp_set_wakeup_period`` function.
|
The application can set ULP timer period values (SENS_ULP_CP_SLEEP_CYCx_REG, x = 0..4) using the ``ulp_set_wakeup_period`` function.
|
||||||
|
|
||||||
.. doxygenfunction:: ulp_set_wakeup_period
|
.. doxygenfunction:: ulp_set_wakeup_period
|
||||||
|
|
||||||
Once the timer counts the number of ticks set in the selected ``SENS_ULP_CP_SLEEP_CYCx_REG`` register, ULP coprocessor powers up and starts running the program from the entry point set in the call to ``ulp_run``.
|
Once the timer counts the number of ticks set in the selected ``SENS_ULP_CP_SLEEP_CYCx_REG`` register, the ULP coprocessor will power up and start running the program from the entry point set in the call to ``ulp_run``.
|
||||||
|
|
||||||
The program runs until it encounters a ``halt`` instruction or an illegal instruction. Once the program halts, ULP coprocessor powers down, and the timer is started again.
|
The program runs until it encounters a ``halt`` instruction or an illegal instruction. Once the program halts, ULP coprocessor will power down, and the timer will be started again.
|
||||||
|
|
||||||
To disable the timer (effectively preventing the ULP program from running again), clear the ``RTC_CNTL_ULP_CP_SLP_TIMER_EN`` bit in the ``RTC_CNTL_STATE0_REG`` register. This can be done both from ULP code and from the main program.
|
To disable the timer (effectively preventing the ULP program from running again), please clear the ``RTC_CNTL_ULP_CP_SLP_TIMER_EN`` bit in the ``RTC_CNTL_STATE0_REG`` register. This can be done both from the ULP code and from the main program.
|
||||||
|
|
||||||
|
|
||||||
.. _binutils-esp32ulp toolchain: https://github.com/espressif/binutils-esp32ulp
|
.. _binutils-esp32ulp toolchain: https://github.com/espressif/binutils-esp32ulp
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
ULP coprocessor programming
|
ULP Coprocessor programming
|
||||||
===================================
|
=============================
|
||||||
|
|
||||||
:link_to_translation:`zh_CN:[中文]`
|
:link_to_translation:`zh_CN:[中文]`
|
||||||
|
|
||||||
@ -10,23 +10,23 @@ ULP coprocessor programming
|
|||||||
Programming using macros (legacy) <ulp_macros>
|
Programming using macros (legacy) <ulp_macros>
|
||||||
|
|
||||||
|
|
||||||
ULP (Ultra Low Power) coprocessor is a simple FSM which is designed to perform measurements using ADC, temperature sensor, and external I2C sensors, while main processors are in deep sleep mode. ULP coprocessor can access RTC_SLOW_MEM memory region, and registers in RTC_CNTL, RTC_IO, and SARADC peripherals. ULP coprocessor uses fixed-width 32-bit instructions, 32-bit memory addressing, and has 4 general purpose 16-bit registers.
|
The ULP (Ultra Low Power) coprocessor is a simple FSM (Finite State Machine) which is designed to perform measurements using the ADC, temperature sensor, and external I2C sensors, while the main processors are in deep sleep mode. The ULP coprocessor can access the RTC_SLOW_MEM memory region, and registers in RTC_CNTL, RTC_IO, and SARADC peripherals. The ULP coprocessor uses fixed-width 32-bit instructions, 32-bit memory addressing, and has 4 general-purpose 16-bit registers.
|
||||||
|
|
||||||
Installing the toolchain
|
Installing the Toolchain
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
ULP coprocessor code is written in assembly and compiled using the `binutils-esp32ulp toolchain`_.
|
The ULP coprocessor code is written in assembly and compiled using the `binutils-esp32ulp toolchain`_.
|
||||||
|
|
||||||
If you have already set up ESP-IDF with CMake build system according to the :doc:`Getting Started Guide <../../get-started/index>`, then the ULP toolchain is already installed.
|
If you have already set up ESP-IDF with CMake build system according to the :doc:`Getting Started Guide <../../get-started/index>`, then the ULP toolchain will already be installed.
|
||||||
|
|
||||||
If you are using ESP-IDF with the legacy GNU Make based build system, refer to the instructions on this page: :doc:`ulp-legacy`.
|
If you are using ESP-IDF with the legacy GNU Make based build system, refer to the instructions on this page: :doc:`ulp-legacy`.
|
||||||
|
|
||||||
Compiling ULP code
|
Compiling the ULP Code
|
||||||
------------------
|
-----------------------
|
||||||
|
|
||||||
To compile ULP code as part of a component, the following steps must be taken:
|
To compile the ULP code as part of the component, the following steps must be taken:
|
||||||
|
|
||||||
1. ULP code, written in assembly, must be added to one or more files with `.S` extension. These files must be placed into a separate directory inside component directory, for instance `ulp/`.
|
1. The ULP code, written in assembly, must be added to one or more files with `.S` extension. These files must be placed into a separate directory inside the component directory, for instance `ulp/`.
|
||||||
|
|
||||||
.. note: When registering the component (via ``idf_component_register``), this directory should not be added to the ``SRC_DIRS`` argument. The logic behind this is that the ESP-IDF build system will compile files found in ``SRC_DIRS`` based on their extensions. For ``.S`` files, ``xtensa-esp32-elf-as`` assembler is used. This is not desirable for ULP assembly files, so the easiest way to achieve the distinction is by placing ULP assembly files into a separate directory. The ULP assembly source files should also **not** be added to ``SRCS`` for the same reason. See the step below for how to properly add ULP assembly source files.
|
.. note: When registering the component (via ``idf_component_register``), this directory should not be added to the ``SRC_DIRS`` argument. The logic behind this is that the ESP-IDF build system will compile files found in ``SRC_DIRS`` based on their extensions. For ``.S`` files, ``xtensa-esp32-elf-as`` assembler is used. This is not desirable for ULP assembly files, so the easiest way to achieve the distinction is by placing ULP assembly files into a separate directory. The ULP assembly source files should also **not** be added to ``SRCS`` for the same reason. See the step below for how to properly add ULP assembly source files.
|
||||||
|
|
||||||
@ -41,38 +41,38 @@ To compile ULP code as part of a component, the following steps must be taken:
|
|||||||
|
|
||||||
ulp_embed_binary(${ulp_app_name} ${ulp_s_sources} ${ulp_exp_dep_srcs})
|
ulp_embed_binary(${ulp_app_name} ${ulp_s_sources} ${ulp_exp_dep_srcs})
|
||||||
|
|
||||||
The first argument to ``ulp_embed_binary`` specifies the ULP binary name. The name specified here will also be used other generated artifacts
|
The first argument to ``ulp_embed_binary`` specifies the ULP binary name. The name specified here will also be used by other generated artifacts
|
||||||
such as the ELF file, map file, header file and linker export file. The second argument specifies the ULP assembly source files.
|
such as the ELF file, map file, header file and linker export file. The second argument specifies the ULP assembly source files.
|
||||||
Finally, the third argument specifies the list of component source files which include the header file to be generated.
|
Finally, the third argument specifies the list of component source files which include the header file to be generated.
|
||||||
This list is needed to build the dependencies correctly and ensure that the generated header file is created before any of these files are compiled.
|
This list is needed to build the dependencies correctly and ensure that the generated header file will be created before any of these files are compiled.
|
||||||
See section below explaining the concept of generated header files for ULP applications.
|
See section below for the concept of generated header files for ULP applications.
|
||||||
|
|
||||||
3. Build the application as usual (e.g. `idf.py app`)
|
3. Build the application as usual (e.g. `idf.py app`)
|
||||||
|
|
||||||
Inside, the build system will take the following steps to build ULP program:
|
Inside, the build system will take the following steps to build ULP program:
|
||||||
|
|
||||||
1. **Run each assembly file (foo.S) through C preprocessor.** This step generates the preprocessed assembly files (foo.ulp.S) in the component build directory. This step also generates dependency files (foo.ulp.d).
|
1. **Run each assembly file (foo.S) through the C preprocessor.** This step generates the preprocessed assembly files (foo.ulp.S) in the component build directory. This step also generates dependency files (foo.ulp.d).
|
||||||
|
|
||||||
2. **Run preprocessed assembly sources through assembler.** This produces objects (foo.ulp.o) and listing (foo.ulp.lst) files. Listing files are generated for debugging purposes and are not used at later stages of build process.
|
2. **Run preprocessed assembly sources through the assembler.** This produces object (foo.ulp.o) and listing (foo.ulp.lst) files. Listing files are generated for debugging purposes and are not used at later stages of the build process.
|
||||||
|
|
||||||
3. **Run linker script template through C preprocessor.** The template is located in components/ulp/ld directory.
|
3. **Run the linker script template through the C preprocessor.** The template is located in ``components/ulp/ld`` directory.
|
||||||
|
|
||||||
4. **Link object files into an output ELF file** (ulp_app_name.elf). Map file (ulp_app_name.map) generated at this stage may be useful for debugging purposes.
|
4. **Link the object files into an output ELF file** (``ulp_app_name.elf``). The Map file (``ulp_app_name.map``) generated at this stage may be useful for debugging purposes.
|
||||||
|
|
||||||
5. **Dump contents of the ELF file into binary** (ulp_app_name.bin) for embedding into the application.
|
5. **Dump the contents of the ELF file into a binary** (``ulp_app_name.bin``) which can then be embedded into the application.
|
||||||
|
|
||||||
6. **Generate list of global symbols** (ulp_app_name.sym) in the ELF file using esp32ulp-elf-nm.
|
6. **Generate a list of global symbols** (``ulp_app_name.sym``) in the ELF file using ``esp32ulp-elf-nm``.
|
||||||
|
|
||||||
7. **Create LD export script and header file** (ulp_app_name.ld and ulp_app_name.h) containing the symbols from ulp_app_name.sym. This is done using esp32ulp_mapgen.py utility.
|
7. **Create an LD export script and header file** (``ulp_app_name.ld`` and ``ulp_app_name.h``) containing the symbols from ``ulp_app_name.sym``. This is done using the ``esp32ulp_mapgen.py`` utility.
|
||||||
|
|
||||||
8. **Add the generated binary to the list of binary files** to be emedded into the application.
|
8. **Add the generated binary to the list of binary files** to be embedded into the application.
|
||||||
|
|
||||||
Accessing ULP program variables
|
Accessing the ULP Program Variables
|
||||||
-------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
Global symbols defined in the ULP program may be used inside the main program.
|
Global symbols defined in the ULP program may be used inside the main program.
|
||||||
|
|
||||||
For example, ULP program may define a variable ``measurement_count`` which will define the number of ADC measurements the program needs to make before waking up the chip from deep sleep::
|
For example, the ULP program may define a variable ``measurement_count`` which will define the number of ADC measurements the program needs to make before waking up the chip from deep sleep::
|
||||||
|
|
||||||
.global measurement_count
|
.global measurement_count
|
||||||
measurement_count: .long 0
|
measurement_count: .long 0
|
||||||
@ -81,19 +81,19 @@ For example, ULP program may define a variable ``measurement_count`` which will
|
|||||||
move r3, measurement_count
|
move r3, measurement_count
|
||||||
ld r3, r3, 0
|
ld r3, r3, 0
|
||||||
|
|
||||||
Main program needs to initialize this variable before ULP program is started. Build system makes this possible by generating a ``${ULP_APP_NAME}.h`` and ``${ULP_APP_NAME}.ld`` files which define global symbols present in the ULP program. This files include each global symbol defined in the ULP program, prefixed with ``ulp_``.
|
The main program needs to initialize this variable before the ULP program is started. The build system makes this possible by generating the ``${ULP_APP_NAME}.h`` and ``${ULP_APP_NAME}.ld`` files which define the global symbols present in the ULP program. Each global symbol defined in the ULP program is included in these files and are prefixed with ``ulp_``.
|
||||||
|
|
||||||
The header file contains declaration of the symbol::
|
The header file contains the declaration of the symbol::
|
||||||
|
|
||||||
extern uint32_t ulp_measurement_count;
|
extern uint32_t ulp_measurement_count;
|
||||||
|
|
||||||
Note that all symbols (variables, arrays, functions) are declared as ``uint32_t``. For functions and arrays, take address of the symbol and cast to the appropriate type.
|
Note that all symbols (variables, arrays, functions) are declared as ``uint32_t``. For functions and arrays, take the address of the symbol and cast it to the appropriate type.
|
||||||
|
|
||||||
The generated linker script file defines locations of symbols in RTC_SLOW_MEM::
|
The generated linker script file defines the locations of symbols in RTC_SLOW_MEM::
|
||||||
|
|
||||||
PROVIDE ( ulp_measurement_count = 0x50000060 );
|
PROVIDE ( ulp_measurement_count = 0x50000060 );
|
||||||
|
|
||||||
To access ULP program variables from the main program, include the generated header file and use variables as one normally would::
|
To access the ULP program variables from the main program, the generated header file should be included using an ``include`` statement. This will allow the ULP program variables to be accessed as regular variables::
|
||||||
|
|
||||||
#include "ulp_app_name.h"
|
#include "ulp_app_name.h"
|
||||||
|
|
||||||
@ -102,20 +102,20 @@ To access ULP program variables from the main program, include the generated hea
|
|||||||
ulp_measurement_count = 64;
|
ulp_measurement_count = 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
Note that ULP program can only use lower 16 bits of each 32-bit word in RTC memory, because the registers are 16-bit, and there is no instruction to load from high part of the word.
|
Note that the ULP program can only use lower 16 bits of each 32-bit word in RTC memory, because the registers are 16-bit, and there is no instruction to load from the high part of the word.
|
||||||
|
|
||||||
Likewise, ULP store instruction writes register value into the lower 16 bit part of the 32-bit word. Upper 16 bits are written with a value which depends on the address of the store instruction, so when reading variables written by the ULP, main application needs to mask upper 16 bits, e.g.::
|
Likewise, the ULP store instruction writes register value into the lower 16 bits part of the 32-bit word. The upper 16 bits are written with a value which depends on the address of the store instruction, thus when reading variables written by the ULP, the main application needs to mask the upper 16 bits, e.g.::
|
||||||
|
|
||||||
printf("Last measurement value: %d\n", ulp_last_measurement & UINT16_MAX);
|
printf("Last measurement value: %d\n", ulp_last_measurement & UINT16_MAX);
|
||||||
|
|
||||||
Starting the ULP program
|
Starting the ULP Program
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
To run a ULP program, main application needs to load the ULP program into RTC memory using ``ulp_load_binary`` function, and then start it using ``ulp_run`` function.
|
To run a ULP program, the main application needs to load the ULP program into RTC memory using the ``ulp_load_binary`` function, and then start it using the ``ulp_run`` function.
|
||||||
|
|
||||||
Note that "Enable Ultra Low Power (ULP) Coprocessor" option must be enabled in menuconfig in order to reserve memory for the ULP. "RTC slow memory reserved for coprocessor" option must be set to a value sufficient to store ULP code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one.
|
Note that "Enable Ultra Low Power (ULP) Coprocessor" option must be enabled in menuconfig to reserve memory for the ULP. "RTC slow memory reserved for coprocessor" option must be set to a value sufficient to store ULP code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one.
|
||||||
|
|
||||||
Each ULP program is embedded into the ESP-IDF application as a binary blob. Application can reference this blob and load it in the following way (suppose ULP_APP_NAME was defined to ``ulp_app_name``::
|
Each ULP program is embedded into the ESP-IDF application as a binary blob. The application can reference this blob and load it in the following way (suppose ULP_APP_NAME was defined to ``ulp_app_name``)::
|
||||||
|
|
||||||
extern const uint8_t bin_start[] asm("_binary_ulp_app_name_bin_start");
|
extern const uint8_t bin_start[] asm("_binary_ulp_app_name_bin_start");
|
||||||
extern const uint8_t bin_end[] asm("_binary_ulp_app_name_bin_end");
|
extern const uint8_t bin_end[] asm("_binary_ulp_app_name_bin_end");
|
||||||
@ -129,13 +129,13 @@ Each ULP program is embedded into the ESP-IDF application as a binary blob. Appl
|
|||||||
|
|
||||||
.. doxygenfunction:: ulp_load_binary
|
.. doxygenfunction:: ulp_load_binary
|
||||||
|
|
||||||
Once the program is loaded into RTC memory, application can start it, passing the address of the entry point to ``ulp_run`` function::
|
Once the program is loaded into RTC memory, the application can start it, passing the address of the entry point to the ``ulp_run`` function::
|
||||||
|
|
||||||
ESP_ERROR_CHECK( ulp_run(&ulp_entry - RTC_SLOW_MEM) );
|
ESP_ERROR_CHECK( ulp_run(&ulp_entry - RTC_SLOW_MEM) );
|
||||||
|
|
||||||
.. doxygenfunction:: ulp_run
|
.. doxygenfunction:: ulp_run
|
||||||
|
|
||||||
Declaration of the entry point symbol comes from the above mentioned generated header file, ``${ULP_APP_NAME}.h``. In assembly source of the ULP application, this symbol must be marked as ``.global``::
|
Declaration of the entry point symbol comes from the generated header file mentioned above, ``${ULP_APP_NAME}.h``. In the assembly source of the ULP application, this symbol must be marked as ``.global``::
|
||||||
|
|
||||||
|
|
||||||
.global entry
|
.global entry
|
||||||
@ -143,20 +143,20 @@ Declaration of the entry point symbol comes from the above mentioned generated h
|
|||||||
/* code starts here */
|
/* code starts here */
|
||||||
|
|
||||||
|
|
||||||
ULP program flow
|
ULP Program Flow
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
ULP coprocessor is started by a timer. The timer is started once ``ulp_run`` is called. The timer counts a number of RTC_SLOW_CLK ticks (by default, produced by an internal 150kHz RC oscillator). The number of ticks is set using ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers (x = 0..4). When starting the ULP for the first time, ``SENS_ULP_CP_SLEEP_CYC0_REG`` will be used to set the number of timer ticks. Later the ULP program can select another ``SENS_ULP_CP_SLEEP_CYCx_REG`` register using ``sleep`` instruction.
|
The ULP coprocessor is started by a timer. The timer is started once ``ulp_run`` is called. The timer counts the number of RTC_SLOW_CLK ticks (by default, produced by an internal 150kHz RC oscillator). The number of ticks is set using ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers (x = 0..4). When starting the ULP for the first time, ``SENS_ULP_CP_SLEEP_CYC0_REG`` will be used to set the number of timer ticks. Later the ULP program can select another ``SENS_ULP_CP_SLEEP_CYCx_REG`` register using the ``sleep`` instruction.
|
||||||
|
|
||||||
The application can set ULP timer period values (SENS_ULP_CP_SLEEP_CYCx_REG, x = 0..4) using ``ulp_set_wakeup_period`` function.
|
The application can set ULP timer period values (SENS_ULP_CP_SLEEP_CYCx_REG, x = 0..4) using the ``ulp_set_wakeup_period`` function.
|
||||||
|
|
||||||
.. doxygenfunction:: ulp_set_wakeup_period
|
.. doxygenfunction:: ulp_set_wakeup_period
|
||||||
|
|
||||||
Once the timer counts the number of ticks set in the selected ``SENS_ULP_CP_SLEEP_CYCx_REG`` register, ULP coprocessor powers up and starts running the program from the entry point set in the call to ``ulp_run``.
|
Once the timer counts the number of ticks set in the selected ``SENS_ULP_CP_SLEEP_CYCx_REG`` register, the ULP coprocessor will power up and start running the program from the entry point set in the call to ``ulp_run``.
|
||||||
|
|
||||||
The program runs until it encounters a ``halt`` instruction or an illegal instruction. Once the program halts, ULP coprocessor powers down, and the timer is started again.
|
The program runs until it encounters a ``halt`` instruction or an illegal instruction. Once the program halts, the ULP coprocessor will power down, and the timer will be started again.
|
||||||
|
|
||||||
To disable the timer (effectively preventing the ULP program from running again), clear the ``RTC_CNTL_ULP_CP_SLP_TIMER_EN`` bit in the ``RTC_CNTL_STATE0_REG`` register. This can be done both from ULP code and from the main program.
|
To disable the timer (effectively preventing the ULP program from running again), please clear the ``RTC_CNTL_ULP_CP_SLP_TIMER_EN`` bit in the ``RTC_CNTL_STATE0_REG`` register. This can be done both from the ULP code and from the main program.
|
||||||
|
|
||||||
|
|
||||||
.. _binutils-esp32ulp toolchain: https://github.com/espressif/binutils-esp32ulp
|
.. _binutils-esp32ulp toolchain: https://github.com/espressif/binutils-esp32ulp
|
||||||
|
@ -4,44 +4,48 @@ Unit Testing (Legacy GNU Make)
|
|||||||
|
|
||||||
.. include:: ../gnu-make-legacy.rst
|
.. include:: ../gnu-make-legacy.rst
|
||||||
|
|
||||||
ESP-IDF comes with a unit test app based on Unity - unit test framework. Unit tests are integrated in the ESP-IDF repository and are placed in ``test`` subdirectory of each component respectively.
|
ESP-IDF comes with a unit test application that is based on the Unity - unit test framework. Unit tests are integrated in the ESP-IDF repository and are placed in the ``test`` subdirectories of each component respectively.
|
||||||
|
|
||||||
Add normal test cases
|
Normal Test Cases
|
||||||
---------------------
|
------------------
|
||||||
|
|
||||||
Unit tests are added in the ``test`` subdirectory of the respective component.
|
Unit tests are located in the ``test`` subdirectory of a component.
|
||||||
Tests are added in C files, a single C file can include multiple test cases.
|
Tests are added in C files, a single C file can include multiple test cases.
|
||||||
Test files start with the word "test".
|
Test files start with the word "test".
|
||||||
|
|
||||||
The test file should include unity.h and the header for the C module to be tested.
|
Each test file should include the ``unity.h`` header and the header for the C module to be tested.
|
||||||
|
|
||||||
Tests are added in a function in the C file as follows::
|
Tests are added in a function in the C file as follows:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
TEST_CASE("test name", "[module name]"
|
TEST_CASE("test name", "[module name]"
|
||||||
{
|
{
|
||||||
// Add test here
|
// Add test here
|
||||||
}
|
}
|
||||||
|
|
||||||
First argument is a descriptive name for the test, second argument is an identifier in square brackets.
|
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.
|
Identifiers are used to group related test, or tests with specific properties.
|
||||||
|
|
||||||
There is no need to add a main function with ``UNITY_BEGIN()`` and ``UNITY_END()`` in each test case.
|
.. note::
|
||||||
``unity_platform.c`` will run ``UNITY_BEGIN()``, run the tests cases, and then call ``UNITY_END()``.
|
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()``.
|
||||||
|
|
||||||
Each `test` subdirectory needs to include component.mk file with at least the following line of code::
|
Each ``test`` subdirectory needs to include a ``component.mk`` file with the following line of code::
|
||||||
|
|
||||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
||||||
|
|
||||||
See http://www.throwtheswitch.org/unity for more information about writing tests in Unity.
|
See http://www.throwtheswitch.org/unity for more information about writing tests in Unity.
|
||||||
|
|
||||||
|
|
||||||
Add multiple devices test cases
|
Multi-device Test Cases
|
||||||
-------------------------------
|
------------------------
|
||||||
|
|
||||||
The normal test cases will be executed on one DUT (Device Under Test). Components need to communicate with each other (like GPIO, SPI ...) can't be tested with normal 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.
|
||||||
Multiple devices test cases support writing and running test with multiple DUTs.
|
Multi-device test cases involve writing multiple test functions, and running them on multiple DUTs.
|
||||||
|
|
||||||
Here's an example of multiple devices test case::
|
The following is an example of a Multi-device test case:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
void gpio_master_test()
|
void gpio_master_test()
|
||||||
{
|
{
|
||||||
@ -68,12 +72,12 @@ Here's an example of multiple devices test case::
|
|||||||
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);
|
||||||
|
|
||||||
|
|
||||||
The macro ``TEST_CASE_MULTIPLE_DEVICES`` is used to declare multiple devices test cases.
|
The macro ``TEST_CASE_MULTIPLE_DEVICES`` is used to declare a multi-device test case.
|
||||||
First argument is test case name, second argument is test case description.
|
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.
|
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.
|
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 secnario in the above example, slave should get GPIO level after master set level. DUT UART console will prompt and requires user interaction:
|
As the scenario in the above example, the slave should get GPIO level after master set level. DUT UART console will prompt and requires user interaction:
|
||||||
|
|
||||||
DUT1 (master) console::
|
DUT1 (master) console::
|
||||||
|
|
||||||
@ -98,15 +102,15 @@ DUT2 console::
|
|||||||
|
|
||||||
Send signal: [dut2 mac address][10:20:30:40:50:60]!
|
Send signal: [dut2 mac address][10:20:30:40:50:60]!
|
||||||
|
|
||||||
Once the signal is sent from DUT2, you need to input ``10:20:30:40:50:60`` on DUT1 and press "Enter". Then DUT1 will get the MAC address string of DUT2 and unblocks from ``unity_wait_for_signal_param``, start to connect to DUT2.
|
Once the signal is sent from DUT2, you need to input ``10:20:30:40:50:60`` on DUT1 and press "Enter". Then DUT1 will get the MAC address string of DUT2 and unblock from ``unity_wait_for_signal_param``, then start to connect to DUT2.
|
||||||
|
|
||||||
|
|
||||||
Add multiple stages test cases
|
Multi-stage Test Cases
|
||||||
-------------------------------
|
-----------------------
|
||||||
|
|
||||||
The normal test cases are expected to finish without reset (or only need to check if reset happens). Sometimes we want to run some specific test after certain kinds of reset.
|
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 want to test if reset reason is correct after wakeup from deep sleep. We need to create deep sleep reset first and then check the reset reason.
|
For example, we expect to test if 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 multiple stages test case, to group a set of test functions together::
|
To support this, we can define multi-stage test cases, to group a set of test functions::
|
||||||
|
|
||||||
static void trigger_deepsleep(void)
|
static void trigger_deepsleep(void)
|
||||||
{
|
{
|
||||||
@ -122,22 +126,22 @@ To support this, we can define multiple stages test case, to group a set of test
|
|||||||
|
|
||||||
TEST_CASE_MULTIPLE_STAGES("reset reason check for deepsleep", "[esp32]", trigger_deepsleep, check_deepsleep_reset_reason);
|
TEST_CASE_MULTIPLE_STAGES("reset reason check for deepsleep", "[esp32]", trigger_deepsleep, check_deepsleep_reset_reason);
|
||||||
|
|
||||||
Multiple stages test cases present a group of test functions to users. It need user interactions (select case and select different stages) to run the case.
|
Multi-stage test cases present a group of test functions to users. It need user interactions (select cases and select different stages) to run the case.
|
||||||
|
|
||||||
|
|
||||||
Building unit test app
|
Building Unit Test App
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
Follow the setup instructions in the top-level esp-idf README.
|
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.
|
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:
|
Change into ``tools/unit-test-app`` directory to configure and build it:
|
||||||
|
|
||||||
* `make menuconfig` - configure unit test app.
|
* ``make menuconfig`` - configure unit test app.
|
||||||
|
|
||||||
* `make TESTS_ALL=1` - build unit test app with tests for each component having tests in the ``test`` subdirectory.
|
* ``make TESTS_ALL=1`` - build unit test app with tests for each component having tests in the ``test`` subdirectory.
|
||||||
* `make TEST_COMPONENTS='xxx'` - build unit test app with tests for specific components.
|
* ``make TEST_COMPONENTS='xxx'`` - build unit test app with tests for specific components.
|
||||||
* `make TESTS_ALL=1 TEST_EXCLUDE_COMPONENTS='xxx'` - build unit test app with all unit tests, except for unit tests of some components. (For instance: `make TESTS_ALL=1 TEST_EXCLUDE_COMPONENTS='ulp mbedtls'` - build all unit tests exludes ulp and mbedtls components).
|
* ``make TESTS_ALL=1 TEST_EXCLUDE_COMPONENTS='xxx'`` - build unit test app with all unit tests, except for unit tests of some components. (For instance: ``make TESTS_ALL=1 TEST_EXCLUDE_COMPONENTS='ulp mbedtls'`` - build all unit tests exludes ``ulp`` and ``mbedtls`` components).
|
||||||
|
|
||||||
When the build finishes, it will print instructions for flashing the chip. You can simply run ``make flash`` to flash all build output.
|
When the build finishes, it will print instructions for flashing the chip. You can simply run ``make flash`` to flash all build output.
|
||||||
|
|
||||||
@ -145,7 +149,7 @@ You can also run ``make flash TESTS_ALL=1`` or ``make TEST_COMPONENTS='xxx'`` to
|
|||||||
|
|
||||||
Use menuconfig to set the serial port for flashing.
|
Use menuconfig to set the serial port for flashing.
|
||||||
|
|
||||||
Running unit tests
|
Running Unit Tests
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
After flashing reset the ESP32 and it will boot the unit test app.
|
After flashing reset the ESP32 and it will boot the unit test app.
|
||||||
@ -178,7 +182,7 @@ When unit test app is idle, press "Enter" will make it print test menu with all
|
|||||||
(1) "trigger_deepsleep"
|
(1) "trigger_deepsleep"
|
||||||
(2) "check_deepsleep_reset_reason"
|
(2) "check_deepsleep_reset_reason"
|
||||||
|
|
||||||
Normal case will print the case name and description. Master slave cases will also print the sub-menu (the registered test function names).
|
The normal case will print the case name and description. Master-slave cases will also print the sub-menu (the registered test function names).
|
||||||
|
|
||||||
Test cases can be run by inputting one of the following:
|
Test cases can be run by inputting one of the following:
|
||||||
|
|
||||||
@ -190,25 +194,25 @@ Test cases can be run by inputting one of the following:
|
|||||||
|
|
||||||
- An asterisk (``*``) to run all test cases
|
- 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 test case.
|
``[multi_device]`` and ``[multi_stage]`` tags tell the test runner whether a test case is a multi-device or multi-stage test case.
|
||||||
These tags are automatically added by ```TEST_CASE_MULTIPLE_STAGES`` and ``TEST_CASE_MULTIPLE_DEVICES`` macros.
|
These tags are automatically added by ```TEST_CASE_MULTIPLE_STAGES`` and ``TEST_CASE_MULTIPLE_DEVICES`` macros.
|
||||||
|
|
||||||
After you select a multiple devices test case, it will print sub menu::
|
After you select a multi-device test case, it will print sub menu::
|
||||||
|
|
||||||
Running gpio master/slave test example...
|
Running gpio master/slave test example...
|
||||||
gpio master/slave test example
|
gpio master/slave test example
|
||||||
(1) "gpio_master_test"
|
(1) "gpio_master_test"
|
||||||
(2) "gpio_slave_test"
|
(2) "gpio_slave_test"
|
||||||
|
|
||||||
You need to input number to select the test running on the DUT.
|
You need to input a number to select the test running on the DUT.
|
||||||
|
|
||||||
Similar to multiple devices test cases, multiple stages test cases will also print sub menu::
|
Similar to multi-device test cases, multi-stage test cases will also print sub-menu::
|
||||||
|
|
||||||
Running reset reason check for deepsleep...
|
Running reset reason check for deepsleep...
|
||||||
reset reason check for deepsleep
|
reset reason check for deepsleep
|
||||||
(1) "trigger_deepsleep"
|
(1) "trigger_deepsleep"
|
||||||
(2) "check_deepsleep_reset_reason"
|
(2) "check_deepsleep_reset_reason"
|
||||||
|
|
||||||
First time you execute this case, input ``1`` to run first stage (trigger deepsleep).
|
For the first time you execute this case, please input ``1`` to run the first stage (trigger deep-sleep).
|
||||||
After DUT is rebooted and able to run test cases, select this case again and input ``2`` to run the second stage.
|
After DUT is rebooted and test cases are available to run, 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.
|
The case will only pass if the last stage passes and all previous stages trigger reset.
|
||||||
|
@ -2,36 +2,35 @@ Unit Testing in ESP32
|
|||||||
=============================
|
=============================
|
||||||
:link_to_translation:`zh_CN:[中文]`
|
:link_to_translation:`zh_CN:[中文]`
|
||||||
|
|
||||||
ESP-IDF comes with a unit test app based on Unity - unit test framework. Unit tests are integrated in the ESP-IDF repository and are placed in ``test`` subdirectory of each component respectively.
|
ESP-IDF comes with a unit test application that is based on the Unity - unit test framework. Unit tests are integrated in the ESP-IDF repository and are placed in the ``test`` subdirectories of each component respectively.
|
||||||
|
|
||||||
Add normal test cases
|
Normal Test Cases
|
||||||
---------------------
|
------------------
|
||||||
|
|
||||||
Unit tests are added in the ``test`` subdirectory of the respective component.
|
Unit tests are located in the ``test`` subdirectory of a component.
|
||||||
Tests are added in C files, a single C file can include multiple test cases.
|
Tests are written in C, and a single C source file can contain multiple test cases.
|
||||||
Test files start with the word "test".
|
Test files start with the word "test".
|
||||||
|
|
||||||
The test file should include unity.h and the header for the C module to be tested.
|
Each test file should include the ``unity.h`` header and the header for the C module to be tested.
|
||||||
|
|
||||||
Tests are added in a function in the C file as follows::
|
Tests are added in a function in the C file as follows:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
TEST_CASE("test name", "[module name]"
|
TEST_CASE("test name", "[module name]"
|
||||||
{
|
{
|
||||||
// Add test here
|
// Add test here
|
||||||
}
|
}
|
||||||
|
|
||||||
First argument is a descriptive name for the test, second argument is an identifier in square brackets.
|
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.
|
Identifiers are used to group related test, or tests with specific properties.
|
||||||
|
|
||||||
There is no need to add a main function with ``UNITY_BEGIN()`` and ``UNITY_END()`` in each test case.
|
.. note::
|
||||||
``unity_platform.c`` will run ``UNITY_BEGIN()``, run the tests cases, and then call ``UNITY_END()``.
|
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()``.
|
||||||
|
|
||||||
The ``test`` subdirectory should contain a :ref:`component CMakeLists.txt <component-directories>`, since they are themselves,
|
The ``test`` subdirectory should contain a :ref:`component CMakeLists.txt <component-directories>`, since they are themselves, components. ESP-IDF uses the ``unity`` test framework and should be specified as a requirement for the component. Normally, components :ref:`should list their sources manually <cmake-file-globbing>`; for component tests however, this requirement is relaxed and the use of the ``SRC_DIRS`` argument in ``idf_component_register`` is advised.
|
||||||
components. ESP-IDF uses the test framework ``unity`` and should be specified as a requirement for the component. Normally, components
|
|
||||||
:ref:`should list their sources manually <cmake-file-globbing>`; for component tests however, this requirement is relaxed and the
|
|
||||||
use of ``SRC_DIRS`` argument to ``idf_component_register`` is advised.
|
|
||||||
|
|
||||||
Overall, the minimal ``test`` subdirectory CMakeLists.txt file may look like as follows:
|
Overall, the minimal ``test`` subdirectory ``CMakeLists.txt`` file should contain the following:
|
||||||
|
|
||||||
.. code:: cmake
|
.. code:: cmake
|
||||||
|
|
||||||
@ -42,13 +41,15 @@ Overall, the minimal ``test`` subdirectory CMakeLists.txt file may look like as
|
|||||||
See http://www.throwtheswitch.org/unity for more information about writing tests in Unity.
|
See http://www.throwtheswitch.org/unity for more information about writing tests in Unity.
|
||||||
|
|
||||||
|
|
||||||
Add multiple devices test cases
|
Multi-device Test Cases
|
||||||
-------------------------------
|
-------------------------
|
||||||
|
|
||||||
The normal test cases will be executed on one DUT (Device Under Test). Components need to communicate with each other (like GPIO, SPI ...) can't be tested with normal 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.
|
||||||
Multiple devices test cases support writing and running test with multiple DUTs.
|
Multi-device test cases involve writing multiple test functions, and running them on multiple DUTs.
|
||||||
|
|
||||||
Here's an example of multiple devices test case::
|
The following is an example of a multi-device test case:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
void gpio_master_test()
|
void gpio_master_test()
|
||||||
{
|
{
|
||||||
@ -75,12 +76,12 @@ Here's an example of multiple devices test case::
|
|||||||
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);
|
||||||
|
|
||||||
|
|
||||||
The macro ``TEST_CASE_MULTIPLE_DEVICES`` is used to declare multiple devices test cases.
|
The macro ``TEST_CASE_MULTIPLE_DEVICES`` is used to declare a multi-device test case.
|
||||||
First argument is test case name, second argument is test case description.
|
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.
|
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.
|
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 secnario in the above example, slave should get GPIO level after master set level. DUT UART console will prompt and requires user interaction:
|
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::
|
DUT1 (master) console::
|
||||||
|
|
||||||
@ -91,15 +92,15 @@ DUT2 (slave) console::
|
|||||||
|
|
||||||
Send signal: [output high level]!
|
Send signal: [output high level]!
|
||||||
|
|
||||||
Once the signal is set from DUT2, you need to press "Enter" on DUT1, then DUT1 unblocks from ``unity_wait_for_signal`` and starts to change GPIO level.
|
Once the signal is sent from DUT2, you need to press "Enter" on DUT1, then DUT1 unblocks from ``unity_wait_for_signal`` and starts to change GPIO level.
|
||||||
|
|
||||||
|
|
||||||
Add multiple stages test cases
|
Multi-stage Test Cases
|
||||||
-------------------------------
|
-----------------------
|
||||||
|
|
||||||
The normal test cases are expected to finish without reset (or only need to check if reset happens). Sometimes we want to run some specific test after certain kinds of reset.
|
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 want to test if reset reason is correct after wakeup from deep sleep. We need to create deep sleep reset first and then check the reset reason.
|
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 multiple stages test case, to group a set of test functions together::
|
To support this, we can define multi-stage test cases, to group a set of test functions::
|
||||||
|
|
||||||
static void trigger_deepsleep(void)
|
static void trigger_deepsleep(void)
|
||||||
{
|
{
|
||||||
@ -115,22 +116,22 @@ To support this, we can define multiple stages test case, to group a set of test
|
|||||||
|
|
||||||
TEST_CASE_MULTIPLE_STAGES("reset reason check for deepsleep", "[esp32]", trigger_deepsleep, check_deepsleep_reset_reason);
|
TEST_CASE_MULTIPLE_STAGES("reset reason check for deepsleep", "[esp32]", trigger_deepsleep, check_deepsleep_reset_reason);
|
||||||
|
|
||||||
Multiple stages test cases present a group of test functions to users. It need user interactions (select case and select different stages) to run the case.
|
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.
|
||||||
|
|
||||||
|
|
||||||
Building unit test app
|
Building Unit Test App
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
Follow the setup instructions in the top-level esp-idf README.
|
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.
|
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:
|
Change into ``tools/unit-test-app`` directory to configure and build it:
|
||||||
|
|
||||||
* `idf.py menuconfig` - configure unit test app.
|
* ``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 all build`` - build unit test app with tests for each component having tests in the ``test`` subdirectory.
|
||||||
* `idf.py -T xxx build` - build unit test app with tests for specific components.
|
* ``idf.py -T xxx build`` - build unit test app with tests for specific components.
|
||||||
* `idf.py -T all -E xxxbuild` - 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).
|
* ``idf.py -T all -E xxxbuild`` - 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).
|
||||||
|
|
||||||
When the build finishes, it will print instructions for flashing the chip. You can simply run ``idf.py flash`` to flash all build output.
|
When the build finishes, it will print instructions for flashing the chip. You can simply run ``idf.py flash`` to flash all build output.
|
||||||
|
|
||||||
@ -138,7 +139,7 @@ You can also run ``idf.py -T all flash`` or ``idf.py -T xxx flash`` to build and
|
|||||||
|
|
||||||
Use menuconfig to set the serial port for flashing.
|
Use menuconfig to set the serial port for flashing.
|
||||||
|
|
||||||
Running unit tests
|
Running Unit Tests
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
After flashing reset the ESP32 and it will boot the unit test app.
|
After flashing reset the ESP32 and it will boot the unit test app.
|
||||||
@ -171,7 +172,7 @@ When unit test app is idle, press "Enter" will make it print test menu with all
|
|||||||
(1) "trigger_deepsleep"
|
(1) "trigger_deepsleep"
|
||||||
(2) "check_deepsleep_reset_reason"
|
(2) "check_deepsleep_reset_reason"
|
||||||
|
|
||||||
Normal case will print the case name and description. Master slave cases will also print the sub-menu (the registered test function names).
|
The normal case will print the case name and description. Master-slave cases will also print the sub-menu (the registered test function names).
|
||||||
|
|
||||||
Test cases can be run by inputting one of the following:
|
Test cases can be run by inputting one of the following:
|
||||||
|
|
||||||
@ -183,25 +184,26 @@ Test cases can be run by inputting one of the following:
|
|||||||
|
|
||||||
- An asterisk to run all test cases
|
- 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 test case.
|
``[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.
|
These tags are automatically added by ```TEST_CASE_MULTIPLE_STAGES`` and ``TEST_CASE_MULTIPLE_DEVICES`` macros.
|
||||||
|
|
||||||
After you select a multiple devices test case, it will print sub menu::
|
After you select a multi-device test case, it will print sub-menu::
|
||||||
|
|
||||||
Running gpio master/slave test example...
|
Running gpio master/slave test example...
|
||||||
gpio master/slave test example
|
gpio master/slave test example
|
||||||
(1) "gpio_master_test"
|
(1) "gpio_master_test"
|
||||||
(2) "gpio_slave_test"
|
(2) "gpio_slave_test"
|
||||||
|
|
||||||
You need to input number to select the test running on the DUT.
|
You need to input a number to select the test running on the DUT.
|
||||||
|
|
||||||
Similar to multiple devices test cases, multiple stages test cases will also print sub menu::
|
Similar to multi-device test cases, multi-stage test cases will also print sub-menu::
|
||||||
|
|
||||||
Running reset reason check for deepsleep...
|
Running reset reason check for deepsleep...
|
||||||
reset reason check for deepsleep
|
reset reason check for deepsleep
|
||||||
(1) "trigger_deepsleep"
|
(1) "trigger_deepsleep"
|
||||||
(2) "check_deepsleep_reset_reason"
|
(2) "check_deepsleep_reset_reason"
|
||||||
|
|
||||||
|
|
||||||
First time you execute this case, input ``1`` to run first stage (trigger deepsleep).
|
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.
|
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.
|
The case only passes if the last stage passes and all previous stages trigger reset.
|
||||||
@ -239,3 +241,4 @@ of time spent on waiting for code/data in case of a cache miss, then subtract th
|
|||||||
|
|
||||||
One limitation of the cache compensated timer is that the task that benchmarked functions should be pinned to a core. This is due to each core having its own event counters that are independent of each other. For example, if ``ccomp_timer_start`` gets called on one core, put to sleep by the scheduler, wakes up, and gets rescheduled on the other core, then the corresponding ``ccomp_timer_stop`` will be invalid.
|
One limitation of the cache compensated timer is that the task that benchmarked functions should be pinned to a core. This is due to each core having its own event counters that are independent of each other. For example, if ``ccomp_timer_start`` gets called on one core, put to sleep by the scheduler, wakes up, and gets rescheduled on the other core, then the corresponding ``ccomp_timer_stop`` will be invalid.
|
||||||
invalid.
|
invalid.
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ ESP32 具有两个强大的 Xtensa 内核,支持多种程序架构。ESP-IDF
|
|||||||
|
|
||||||
如果你使用的是 :doc:`ESP-WROVER-KIT 开发板 <../../hw-reference/modules-and-boards>`,得益于板载的 FT232H 芯片,PC 和 ESP32 的连接仅仅需要一根 USB 线即可完成。FT232H 提供了两路 USB 通道,一路连接到 JTAG,另一路连接到 UART。
|
如果你使用的是 :doc:`ESP-WROVER-KIT 开发板 <../../hw-reference/modules-and-boards>`,得益于板载的 FT232H 芯片,PC 和 ESP32 的连接仅仅需要一根 USB 线即可完成。FT232H 提供了两路 USB 通道,一路连接到 JTAG,另一路连接到 UART。
|
||||||
|
|
||||||
根据用户的喜好,除了使用 Eclipse 集成开发环境,上述的调试工具和构建工具还可以直接在命令行终端运行。
|
根据用户的喜好,除了使用 Eclipse 集成开发环境,还可以直接在命令行终端运行 `debugger` 和 `idf.py build`。
|
||||||
|
|
||||||
.. _jtag-debugging-selecting-jtag-adapter:
|
.. _jtag-debugging-selecting-jtag-adapter:
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ ESP-IDF 有一些针对 OpenOCD 调试功能的选项可以在编译时进行设
|
|||||||
* :ref:`CONFIG_ESP32_DEBUG_OCDAWARE` 默认会被使能。如果程序抛出了不可修复或者未处理的异常,并且此时已经连接上了 JTAG 调试器(即 OpenOCD 正在运行),那么 ESP-IDF 将会进入调试器工作模式。
|
* :ref:`CONFIG_ESP32_DEBUG_OCDAWARE` 默认会被使能。如果程序抛出了不可修复或者未处理的异常,并且此时已经连接上了 JTAG 调试器(即 OpenOCD 正在运行),那么 ESP-IDF 将会进入调试器工作模式。
|
||||||
* :ref:`CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK` 默认没有使能。在所有任务堆栈的末尾设置观察点,从 1 号开始索引。这是调试任务堆栈溢出的最准确的方式。
|
* :ref:`CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK` 默认没有使能。在所有任务堆栈的末尾设置观察点,从 1 号开始索引。这是调试任务堆栈溢出的最准确的方式。
|
||||||
|
|
||||||
更多有关设置编译时的选项的信息,请参阅 :ref:`idf.py menuconfig <get-started-configure>`。
|
更多有关设置编译时的选项的信息,请参阅 :ref:`项目配置菜单 <get-started-configure>`。
|
||||||
|
|
||||||
.. _jtag-debugging-tip-freertos-support:
|
.. _jtag-debugging-tip-freertos-support:
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
分区表中的每个条目都包括以下几个部分:Name(标签)、Type(app、data 等)、SubType 以及在 flash 中的偏移量(分区的加载地址)。
|
分区表中的每个条目都包括以下几个部分:Name(标签)、Type(app、data 等)、SubType 以及在 flash 中的偏移量(分区的加载地址)。
|
||||||
|
|
||||||
在使用分区表时,最简单的方法就是用 `idf.py menuconfig` 选择一张预定义的分区表:
|
在使用分区表时,最简单的方法就是打开项目配置菜单(``idf.py menuconfig``),并在 :ref:`CONFIG_PARTITION_TABLE_TYPE` 下选择一张预定义的分区表:
|
||||||
|
|
||||||
- "Single factory app, no OTA"
|
- "Single factory app, no OTA"
|
||||||
- "Factory app, two OTA definitions"
|
- "Factory app, two OTA definitions"
|
||||||
@ -102,7 +102,7 @@ SubType 字段长度为 8 bit,内容与具体 Type 有关。目前,esp-idf
|
|||||||
- phy (1) 分区用于存放 PHY 初始化数据,从而保证可以为每个设备单独配置 PHY,而非必须采用固件中的统一 PHY 初始化数据。
|
- phy (1) 分区用于存放 PHY 初始化数据,从而保证可以为每个设备单独配置 PHY,而非必须采用固件中的统一 PHY 初始化数据。
|
||||||
|
|
||||||
- 默认配置下,phy 分区并不启用,而是直接将 phy 初始化数据编译至应用程序中,从而节省分区表空间(直接将此分区删掉)。
|
- 默认配置下,phy 分区并不启用,而是直接将 phy 初始化数据编译至应用程序中,从而节省分区表空间(直接将此分区删掉)。
|
||||||
- 如果需要从此分区加载 phy 初始化数据,请运行 ``idf.py menuconfig``,并且使能 :ref:`CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION` 选项。此时,您还需要手动将 phy 初始化数据烧至设备 flash(esp-idf 编译系统并不会自动完成该操作)。
|
- 如果需要从此分区加载 phy 初始化数据,请打开项目配置菜单(``idf.py menuconfig``),并且使能 :ref:`CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION` 选项。此时,您还需要手动将 phy 初始化数据烧至设备 flash(esp-idf 编译系统并不会自动完成该操作)。
|
||||||
- nvs (2) 是专门给 :doc:`非易失性存储 (NVS) API <../api-reference/storage/nvs_flash>` 使用的分区。
|
- nvs (2) 是专门给 :doc:`非易失性存储 (NVS) API <../api-reference/storage/nvs_flash>` 使用的分区。
|
||||||
|
|
||||||
- 用于存储每台设备的 PHY 校准数据(注意,并不是 PHY 初始化数据)。
|
- 用于存储每台设备的 PHY 校准数据(注意,并不是 PHY 初始化数据)。
|
||||||
@ -142,7 +142,7 @@ Flags 字段
|
|||||||
|
|
||||||
烧写到 ESP32 中的分区表采用二进制格式,而不是 CSV 文件本身。此时,:component_file:`partition_table/gen_esp32part.py` 工具可以实现 CSV 和二进制文件之间的转换。
|
烧写到 ESP32 中的分区表采用二进制格式,而不是 CSV 文件本身。此时,:component_file:`partition_table/gen_esp32part.py` 工具可以实现 CSV 和二进制文件之间的转换。
|
||||||
|
|
||||||
如果您在 ``idf.py menuconfig`` 指定了分区表 CSV 文件的名称,然后执行 ``idf.py partition_table``。这时,转换将在编译过程中自动完成。
|
如果您在项目配置菜单(``idf.py menuconfig``)中设置了分区表 CSV 文件的名称,然后构建项目获执行 ``idf.py partition_table``。这时,转换将在编译过程中自动完成。
|
||||||
|
|
||||||
手动将 CSV 文件转换为二进制文件:
|
手动将 CSV 文件转换为二进制文件:
|
||||||
|
|
||||||
|
@ -1 +1,165 @@
|
|||||||
.. include:: ../../en/api-guides/ulp.rst
|
ULP 协处理器 (传统的 GNU Make)
|
||||||
|
=================================
|
||||||
|
|
||||||
|
:link_to_translation:`en:[English]`
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
指令集参考 <ulp_instruction_set>
|
||||||
|
使用宏进行编程(遗留)<ulp_macros>
|
||||||
|
|
||||||
|
.. include:: ../gnu-make-legacy.rst
|
||||||
|
|
||||||
|
ULP(Ultra Low Power 超低功耗)协处理器是一种简单的有限状态机 (FSM),可以在主处理器处于深度睡眠模式时,使用 ADC、温度传感器和外部 I2C 传感器执行测量操作。ULP 协处理器可以访问 RTC_SLOW_MEM 内存区域及 RTC_CNTL、RTC_IO、SARADC 等外设寄存器。ULP 协处理器使用 32 位固定宽度的指令,32 位内存寻址,配备 4 个 16 位通用寄存器。
|
||||||
|
|
||||||
|
安装工具链
|
||||||
|
-----------
|
||||||
|
|
||||||
|
ULP 协处理器代码是用汇编语言编写的,并使用 `binutils-esp32ulp toolchain` 进行编译。
|
||||||
|
|
||||||
|
1. 从 https://github.com/espressif/binutils-esp32ulp/releases 网站下载预编译的最新版本工具链二进制文件。
|
||||||
|
|
||||||
|
2. 将工具链提取至目录中,向工具链的 ``bin/`` 目录中添加路径至 ``PATH`` 环境变量中。
|
||||||
|
|
||||||
|
编译 ULP 代码
|
||||||
|
--------------
|
||||||
|
|
||||||
|
若需要将 ULP 代码编译为某组件的一部分,则必须执行以下步骤:
|
||||||
|
|
||||||
|
1. 用汇编语言编写的 ULP 代码必须导入到一个或多个 `.S` 扩展文件中,且这些文件必须放在组件目录中一个独立的目录中,例如 `ulp/`。
|
||||||
|
|
||||||
|
.. note: 该目录不要添加到 ``COMPONENT_SRCDIRS`` 环境变量中。因为 ESP-IDF 构建系统将基于文件扩展名编译在 ``COMPONENT_SRCDIRS`` 中搜索到的文件。对于 ``.S`` 文件,使用的是 ``xtensa-esp32-elf-as`` 汇编器。但这并不适用于 ULP 程序集文件,因此体现这种区别的最简单方法就是将 ULP 程序集文件放到单独的目录中。
|
||||||
|
|
||||||
|
2. 修改组件 makefile,添加下列参数::
|
||||||
|
|
||||||
|
ULP_APP_NAME ?= ulp_$(COMPONENT_NAME)
|
||||||
|
ULP_S_SOURCES = $(COMPONENT_PATH)/ulp/ulp_source_file.S
|
||||||
|
ULP_EXP_DEP_OBJECTS := main.o
|
||||||
|
include $(IDF_PATH)/components/ulp/component_ulp_common.mk
|
||||||
|
|
||||||
|
代码解释如下:
|
||||||
|
|
||||||
|
ULP_APP_NAME
|
||||||
|
为生成的 ULP 应用程序命名,不带扩展名。此名称用于 ULP 应用程序的构建输出:ELF 文件、map 文件、二进制文件、生成的头文件和链接器导出文件。
|
||||||
|
|
||||||
|
ULP_S_SOURCES
|
||||||
|
设置要传递给 ULP 汇编器的程序集文件列表。列表中的路径须是绝对路径,即以 ``$(COMPONENT_PATH)`` 开头的路径。如果列表中需列出多个文件,可使用函数 ``$(addprefix)``。因为路径与组件构建目录相关,因而不需前置。
|
||||||
|
|
||||||
|
ULP_EXP_DEP_OBJECTS
|
||||||
|
设置组件中目标文件名称的列表,其中包含生成的头文件。此列表用以建立正确的依赖项,并确保在此类文件被编译前生成头文件。ULP 应用程序头文件的定义请见下文。
|
||||||
|
|
||||||
|
include $(IDF_PATH)/components/ulp/component_ulp_common.mk
|
||||||
|
包含常见的 ULP 编译步骤。为 ULP 目标文件、ELF 文件、二进制文件等定义编译目标。
|
||||||
|
|
||||||
|
3. 使用常规方法(例如 ``idf.py build`` 或 ``idf.py app``)编译应用程序
|
||||||
|
|
||||||
|
在编译过程中,构建系统将按照以下步骤编译 ULP 程序:
|
||||||
|
|
||||||
|
1. **通过 C 预处理器运行每个程序集文件 (foo.S)。** 此步骤在组件编译目录中生成预处理的程序集文件 (foo.ulp.pS),同时生成依赖文件 (foo.ulp.d)。
|
||||||
|
|
||||||
|
2. **通过汇编器运行预处理过的汇编源码。** 此步骤会生成目标文件 (foo.ulp.o) 和清单文件 (foo.ulp.lst)。生成的清单文件用于调试,不用于构建过程的后续步骤。
|
||||||
|
|
||||||
|
3. **通过 C 预处理器运行链接器脚本模板。** 模版位于 components/ulp/ld 目录中。
|
||||||
|
|
||||||
|
4. **将目标文件链接到 ELF 输出文件** (ulp_app_name.elf)。此步骤生成的 .map 文件 (ulp_app_name.map) 可能会用于调试。
|
||||||
|
|
||||||
|
5. **将 ELF 文件中的内容转储为二进制文件** (ulp_app_name.bin),以便嵌入到应用程序中。
|
||||||
|
|
||||||
|
6. **使用 esp32ulp-elf-nm 在 ELF 文件中生成全局符号列表** (ulp_app_name.sym)。
|
||||||
|
|
||||||
|
7. **创建 LD 导出脚本和头文件** (ulp_app_name.ld 和 ulp_app_name.h),包含来自 ulp_app_name.sym 的符号。此步骤通过 esp32ulp_mapgen.py 工具来完成。
|
||||||
|
|
||||||
|
8. **将生成的二进制文件添加到要嵌入应用程序的二进制文件列表中。**
|
||||||
|
|
||||||
|
访问 ULP 程序变量
|
||||||
|
------------------
|
||||||
|
|
||||||
|
在 ULP 程序中定义的全局符号也可以在主程序中使用。
|
||||||
|
|
||||||
|
例如,ULP 程序可以定义 ``measurement_count`` 变量,此变量规定了芯片从深度睡眠模式唤醒之前,程序需要进行 ADC 测量的次数::
|
||||||
|
|
||||||
|
.global measurement_count
|
||||||
|
measurement_count: .long 0
|
||||||
|
|
||||||
|
/* later, use measurement_count */
|
||||||
|
move r3, measurement_count
|
||||||
|
ld r3, r3, 0
|
||||||
|
|
||||||
|
主程序需要在启动 ULP 程序之前初始化 ``measurement_count`` 变量,构建系统通过生成定义 ULP 程序中全局符号的 ``$(ULP_APP_NAME).h`` 和 ``$(ULP_APP_NAME).ld`` 文件,可以实现上述操作;这些文件包含了 ULP 程序中所有全局符号,文件名以 ``ulp_`` 开头。
|
||||||
|
|
||||||
|
头文件包含对此类符号的声明::
|
||||||
|
|
||||||
|
extern uint32_t ulp_measurement_count;
|
||||||
|
|
||||||
|
注意,所有符号(包括变量、数组、函数)均被声明为 ``uint32_t``。对于函数和数组,先获取符号地址,然后转换为适当的类型。
|
||||||
|
|
||||||
|
生成的链接器脚本文件定义了符号在 RTC_SLOW_MEM 中的位置::
|
||||||
|
|
||||||
|
PROVIDE ( ulp_measurement_count = 0x50000060 );
|
||||||
|
|
||||||
|
如果要从主程序访问 ULP 程序变量,应先使用 ``include`` 语句包含生成的头文件,这样,就可以像访问常规变量一样访问 ulp 程序变量。操作如下::
|
||||||
|
|
||||||
|
#include "ulp_app_name.h"
|
||||||
|
|
||||||
|
// later
|
||||||
|
void init_ulp_vars() {
|
||||||
|
ulp_measurement_count = 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
注意,ULP 程序只能使用 RTC 内存中 32 位字的低 16 位,因为寄存器是 16 位的,并且不存在从字的高位加载的指令。
|
||||||
|
|
||||||
|
同样,ULP 储存指令将寄存器值写入 32 位字的低 16 位中。写入高 16 位的值取决于存储指令的地址,因此在读取 ULP 写的变量时,主应用程序需要屏蔽高 16 位,例如::
|
||||||
|
|
||||||
|
printf("Last measurement value: %d\n", ulp_last_measurement & UINT16_MAX);
|
||||||
|
|
||||||
|
启动 ULP 程序
|
||||||
|
---------------
|
||||||
|
|
||||||
|
要运行 ULP 程序,主应用程序需要调用 ``ulp_load_binary`` 函数将 ULP 程序加载到 RTC 内存中,然后调用 ``ulp_run`` 函数,启动 ULP 程序。
|
||||||
|
|
||||||
|
注意,在 menuconfig 中必须启用 "Enable Ultra Low Power (ULP) Coprocessor" 选项,以便为 ULP 预留内存。"RTC slow memory reserved for coprocessor" 选项设置的值必须足够储存 ULP 代码和数据。如果应用程序组件包含多个 ULP 程序,则 RTC 内存必须足以容纳最大的程序。
|
||||||
|
|
||||||
|
每个 ULP 程序均以二进制 BLOB 的形式嵌入到 ESP-IDF 应用程序中。应用程序可以引用此 BLOB,并以下面的方式加载此 BLOB (假设 ULP_APP_NAME 已被定义为 ``ulp_app_name``)::
|
||||||
|
|
||||||
|
extern const uint8_t bin_start[] asm("_binary_ulp_app_name_bin_start");
|
||||||
|
extern const uint8_t bin_end[] asm("_binary_ulp_app_name_bin_end");
|
||||||
|
|
||||||
|
void start_ulp_program() {
|
||||||
|
ESP_ERROR_CHECK( ulp_load_binary(
|
||||||
|
0 /* load address, set to 0 when using default linker scripts */,
|
||||||
|
bin_start,
|
||||||
|
(bin_end - bin_start) / sizeof(uint32_t)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
.. doxygenfunction:: ulp_load_binary
|
||||||
|
|
||||||
|
一旦上述程序加载到 RTC 内存后,应用程序即可启动此程序,并将入口点的地址传递给 ``ulp_run`` 函数::
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK( ulp_run(&ulp_entry - RTC_SLOW_MEM) );
|
||||||
|
|
||||||
|
.. doxygenfunction:: ulp_run
|
||||||
|
|
||||||
|
上述生成的头文件 ``$(ULP_APP_NAME).h`` 声明了入口点符号。在 ULP 应用程序的汇编源代码中,此符号必须标记为 ``.global``::
|
||||||
|
|
||||||
|
.global entry
|
||||||
|
entry:
|
||||||
|
/* code starts here */
|
||||||
|
|
||||||
|
ULP 程序流
|
||||||
|
-----------
|
||||||
|
|
||||||
|
ULP 协处理器由定时器启动,而调用 ``ulp_run`` 则可启动此定时器。定时器为 RTC_SLOW_CLK 的 Tick 事件计数(默认情况下,Tick 由内部 150 kHz 晶振器生成)。使用 ``SENS_ULP_CP_SLEEP_CYCx_REG`` 寄存器 (x = 0..4) 设置 Tick 数值。第一次启动 ULP 时,使用 ``SENS_ULP_CP_SLEEP_CYC0_REG`` 设置定时器 Tick 数值,之后,ULP 程序可以使用 ``sleep`` 指令来另外选择 ``SENS_ULP_CP_SLEEP_CYCx_REG`` 寄存器。
|
||||||
|
|
||||||
|
此应用程序可以调用 ``ulp_set_wakeup_period`` 函数来设置 ULP 定时器周期值 (SENS_ULP_CP_SLEEP_CYCx_REG, x = 0..4)。
|
||||||
|
|
||||||
|
.. doxygenfunction:: ulp_set_wakeup_period
|
||||||
|
|
||||||
|
一旦定时器数值达到在所选的 ``SENS_ULP_CP_SLEEP_CYCx_REG`` 寄存器中设置的数值,ULP 协处理器就会启动,并调用 ``ulp_run`` 的入口点,开始运行程序。
|
||||||
|
|
||||||
|
程序保持运行,直到遇到 ``halt`` 指令或非法指令才停止。一旦程序停止,ULP 协处理器电源关闭,定时器再次启动。
|
||||||
|
|
||||||
|
如果想禁用定时器(禁用定时器可有效防止 ULP 程序再次运行),请清除 ``RTC_CNTL_STATE0_REG`` 寄存器中的 ``RTC_CNTL_ULP_CP_SLP_TIMER_EN`` 位,可在 ULP 代码或主程序中进行以上操作。
|
||||||
|
|
||||||
|
|
||||||
|
.. _binutils-esp32ulp toolchain: https://github.com/espressif/binutils-esp32ulp
|
||||||
|
@ -15,11 +15,11 @@ ULP(Ultra Low Power 超低功耗)协处理器是一种简单的有限状态
|
|||||||
安装工具链
|
安装工具链
|
||||||
----------
|
----------
|
||||||
|
|
||||||
ULP 协处理器代码是用汇编语言编写的,并使用 `binutils-esp32ulp 工具链`_ 进行编译。
|
ULP 协处理器代码是用汇编语言编写的,并使用 `binutils-esp32ulp 工具链` 进行编译。
|
||||||
|
|
||||||
如果你已经按照 :doc:`快速入门指南 <../../get-started/index>` 中的介绍安装好了 ESP-IDF 及其 CMake 构建系统,那么 ULP 工具链已经被默认安装到了你的开发环境中。
|
如果你已经按照 :doc:`快速入门指南 <../../get-started/index>` 中的介绍安装好了 ESP-IDF 及其 CMake 构建系统,那么 ULP 工具链已经被默认安装到了你的开发环境中。
|
||||||
|
|
||||||
如果你的 ESP-IDF 仍在使用旧版本的基于 GNU Make 的构建系统,请参考 :doc:`ulp-legacy` 一文中的说明,完成工具链的安装。
|
如果你的 ESP-IDF 仍在使用传统的基于 GNU Make 的构建系统,请参考 :doc:`ulp-legacy` 一文中的说明,完成工具链的安装。
|
||||||
|
|
||||||
|
|
||||||
编译 ULP 代码
|
编译 ULP 代码
|
||||||
@ -27,49 +27,41 @@ ULP 协处理器代码是用汇编语言编写的,并使用 `binutils-esp32ulp
|
|||||||
|
|
||||||
若需要将 ULP 代码编译为某组件的一部分,则必须执行以下步骤:
|
若需要将 ULP 代码编译为某组件的一部分,则必须执行以下步骤:
|
||||||
|
|
||||||
1. 用汇编语言编写的 ULP 代码必须导入到一个或多个 .S 扩展文件中,且这些文件必须放在组件目录中一个独立的目录中,例如 `ulp/`。
|
1. 用汇编语言编写的 ULP 代码必须导入到一个或多个 `.S` 扩展文件中,且这些文件必须放在组件目录中一个独立的目录中,例如 `ulp/`。
|
||||||
|
|
||||||
.. note:
|
.. note: 在注册组件(通过 ``idf_component_register``)时,不应将该目录添加到 ``SRC_DIRS`` 参数中。因为 ESP-IDF 构建系统将基于文件扩展名编译在 ``SRC_DIRS`` 中搜索到的文件。对于 ``.S`` 文件,使用的是 ``xtensa-esp32-elf-as`` 汇编器。但这并不适用于 ULP 程序集文件,因此体现这种区别最简单的方式就是将 ULP 程序集文件放到单独的目录中。同样,ULP 程序集源文件也 **不应该** 添加到 ``SRCS`` 中。请参考如下步骤,查看如何正确添加 ULP 程序集源文件。
|
||||||
该目录不要添加到 ``COMPONENT_SRCDIRS`` 环境变量中。因为 ESP-IDF 构建系统将基于文件扩展名编译在 ``COMPONENT_SRCDIRS`` 中搜索到的文件。对于 ``.S`` 文件,使用的是 ``xtensa-esp32-elf-as`` 汇编器。但这并不适用于 ULP 程序集文件,因此体现这种区别的最简单方法就是将 ULP 程序集文件放到单独的目录中。同样,ULP 程序集源文件也不应该添加到 ``COMPONENT_SRCS`` 中。请参考如下步骤,查看如何正确添加 ULP 程序集源文件。
|
|
||||||
|
|
||||||
2. 修改组件 CMakeLists.txt,添加必要的 ULP CMake 定义,示例如下::
|
|
||||||
|
|
||||||
set(ULP_APP_NAME ulp_${COMPONENT_NAME})
|
2. 注册后从组件 CMakeLists.txt 中调用 ``ulp_embed_binary`` 示例如下::
|
||||||
set(ULP_S_SOURCES ulp/ulp_assembly_source_file.S)
|
|
||||||
set(ULP_EXP_DEP_SRCS "ulp_c_source_file.c")
|
|
||||||
include(${IDF_PATH}/components/ulp/component_ulp_common.cmake)
|
|
||||||
|
|
||||||
代码解释如下:
|
...
|
||||||
|
idf_component_register()
|
||||||
|
|
||||||
``set(ULP_APP_NAME ulp_${COMPONENT_NAME})``
|
set(ulp_app_name ulp_${COMPONENT_NAME})
|
||||||
为生成的 ULP 应用程序设置名称,不带扩展名。此名称用于 ULP 应用程序的构建输出:ELF 文件、.map 文件、二进制文件、生成的头文件和链接器导出文件。
|
set(ulp_s_sources ulp/ulp_assembly_source_file.S)
|
||||||
|
set(ulp_exp_dep_srcs "ulp_c_source_file.c")
|
||||||
|
|
||||||
``set(ULP_S_SOURCES "ulp/ulp_assembly_source_file_1.S ulp/ulp_assembly_source_file_2.S")``
|
ulp_embed_binary(${ulp_app_name} ${ulp_s_sources} ${ulp_exp_dep_srcs})
|
||||||
设置要传递给 ULP 汇编器的程序集文件列表,用空格隔开,路径可以是绝对路径,也可以是组件 CMakeLists.txt 的相对路径。
|
|
||||||
|
|
||||||
``set(ULP_EXP_DEP_SRCS "ulp_c_source_file_1.c ulp_c_source_file_2.c")``
|
上述第一个参数到 ``ulp_embed_binary`` 为 ULP 二进制文件命名。此名称也用于生成的其他文件,如:ELF 文件、.map 文件、头文件和链接器导出文件。第二个参数设置 ULP 程序集源文件。最后,第三个参数设置组件源文件列表,其中包括被生成的头文件。此列表用以建立正确的依赖项,并确保在构建过程会先生成再编译包含头文件的源文件。请参考下文,查看为 ULP 应用程序生成的头文件等相关概念。
|
||||||
设置组件中源文件名称的列表。所有包含被生成的头文件的原文件都必须在列表里。此列表建立正确构建依赖项,并确保在构建过程会先生成才编译包含头文件的原文件。请参考下文,查看为 ULP 应用程序生成的头文件等相关概念。此列表需要用空格隔开,路径可以是组件 CMakeLists.txt 文件的相对路径,也可以是绝对路径。
|
|
||||||
|
|
||||||
``include(${IDF_PATH}/components/ulp/component_ulp_common.cmake)``
|
|
||||||
包含 ULP 编译步骤的通用定义。使用 ULP 工具链为 ULP 目标文件、ELF 文件、二进制文件等设置编译规则。
|
|
||||||
|
|
||||||
3. 使用常规方法(例如 `idf.py app`)编译应用程序
|
3. 使用常规方法(例如 `idf.py app`)编译应用程序
|
||||||
|
|
||||||
在内部,编译系统将按照以下步骤编译 ULP 程序:
|
在内部,构建系统将按照以下步骤编译 ULP 程序:
|
||||||
|
|
||||||
1. **通过 C 预处理器运行每个程序集文件 (foo.S)。** 此步骤在组件编译目录中生成预处理的程序集文件 (foo.ulp.S),同时生成依赖文件 (foo.ulp.d)。
|
1. **通过 C 预处理器运行每个程序集文件 (foo.S)。** 此步骤在组件编译目录中生成预处理的程序集文件 (foo.ulp.S),同时生成依赖文件 (foo.ulp.d)。
|
||||||
|
|
||||||
2. **通过汇编器运行预处理过的汇编源码。** 此步骤会生成目标文件 (foo.ulp.o) 和清单 (foo.ulp.lst)。清单文件仅用于调试,不用于编译进程的后续步骤。
|
2. **通过汇编器运行预处理过的汇编源码。** 此步骤会生成目标文件 (foo.ulp.o) 和清单 (foo.ulp.lst)。清单文件仅用于调试,不用于编译进程的后续步骤。
|
||||||
|
|
||||||
3. **通过 C 预处理器运行链接器脚本模板。** 模板位于 components/ulp/ld 目录中。
|
3. **通过 C 预处理器运行链接器脚本模板。** 模板位于 ``components/ulp/ld`` 目录中。
|
||||||
|
|
||||||
4. **将目标文件链接到 ELF 输出文件** (ulp_app_name.elf)。此步骤生成的.map 文件 (ulp_app_name.map) 默认用于调试。
|
4. **将目标文件链接到 ELF 输出文件** (``ulp_app_name.elf``)。此步骤生成的.map 文件 (``ulp_app_name.map``) 默认用于调试。
|
||||||
|
|
||||||
5. **将 ELF 文件中的内容转储为二进制文件** (ulp_app_name.bin),以便嵌入到应用程序中。
|
5. **将 ELF 文件中的内容转储为二进制文件** (``ulp_app_name.bin``),以便嵌入到应用程序中。
|
||||||
|
|
||||||
6. **使用 esp32ulp-elf-nm 在 ELF 文件中生成全局符号列表** (ulp_app_name.sym)。
|
6. 使用 ``esp32ulp-elf-nm`` 在 ELF 文件中 **生成全局符号列表** (``ulp_app_name.sym``)。
|
||||||
|
|
||||||
7. **创建 LD 导出脚本和头文件** (ulp_app_name.ld 和 ulp_app_name.h),包含来自 ulp_app_name.sym 的符号。此步骤可借助 esp32ulp_mapgen.py 工具来完成。
|
7. **创建 LD 导出脚本和头文件** (``ulp_app_name.ld`` 和 ``ulp_app_name.h``),包含来自 ``ulp_app_name.sym`` 的符号。此步骤可借助 ``esp32ulp_mapgen.py`` 工具来完成。
|
||||||
|
|
||||||
8. **将生成的二进制文件添加到要嵌入应用程序的二进制文件列表中。**
|
8. **将生成的二进制文件添加到要嵌入应用程序的二进制文件列表中。**
|
||||||
|
|
||||||
@ -87,7 +79,7 @@ ULP 协处理器代码是用汇编语言编写的,并使用 `binutils-esp32ulp
|
|||||||
move r3, measurement_count
|
move r3, measurement_count
|
||||||
ld r3, r3, 0
|
ld r3, r3, 0
|
||||||
|
|
||||||
主程序需要在启动 ULP 程序之前初始化 ``measurement_count`` 变量,编译系统生成 ``${ULP_APP_NAME}.h`` 和 ``${ULP_APP_NAME}.ld`` 文件可以实现上述操作,这些文件在 ULP 编程中定义了全局符号,包含了在 ULP 程序中定义的所有全局符号,前缀为 ``ulp_``。
|
主程序需要在启动 ULP 程序之前初始化 ``measurement_count`` 变量,构建系统生成定义 ULP 编程中全局符号的 ``${ULP_APP_NAME}.h`` 和 ``${ULP_APP_NAME}.ld`` 文件,可以实现上述操作,这些文件包含在 ULP 程序中定义的所有全局符号,文件以 ``ulp_`` 开头。
|
||||||
|
|
||||||
头文件包含对此类符号的声明::
|
头文件包含对此类符号的声明::
|
||||||
|
|
||||||
@ -99,7 +91,7 @@ ULP 协处理器代码是用汇编语言编写的,并使用 `binutils-esp32ulp
|
|||||||
|
|
||||||
PROVIDE ( ulp_measurement_count = 0x50000060 );
|
PROVIDE ( ulp_measurement_count = 0x50000060 );
|
||||||
|
|
||||||
如果要从主程序访问 ULP 程序变量,先包含生成的头文件,并使用上述变量,操作如下::
|
如果要从主程序访问 ULP 程序变量,应先使用 ``include`` 语句包含生成的头文件,这样,就可以像访问常规变量一样访问 ulp 程序变量。操作如下::
|
||||||
|
|
||||||
#include "ulp_app_name.h"
|
#include "ulp_app_name.h"
|
||||||
|
|
||||||
@ -158,7 +150,7 @@ ULP 协处理器由定时器启动,而调用 ``ulp_run`` 则可启动此定时
|
|||||||
|
|
||||||
.. doxygenfunction:: ulp_set_wakeup_period
|
.. doxygenfunction:: ulp_set_wakeup_period
|
||||||
|
|
||||||
一旦定时器达到在所选的 ``SENS_ULP_CP_SLEEP_CYCx_REG`` 寄存器中设置的数值,ULP 协处理器就会启动,并调用 ``ulp_run`` 的入口点开始运行程序。
|
一旦定时器为所选的 ``SENS_ULP_CP_SLEEP_CYCx_REG`` 寄存器的 Tick 事件计数,ULP 协处理器就会启动,并调用 ``ulp_run`` 的入口点开始运行程序。
|
||||||
|
|
||||||
程序保持运行,直到遇到 ``halt`` 指令或非法指令。一旦程序停止,ULP 协处理器电源关闭,定时器再次启动。
|
程序保持运行,直到遇到 ``halt`` 指令或非法指令。一旦程序停止,ULP 协处理器电源关闭,定时器再次启动。
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
ESP32 中的单元测试
|
单元测试(传统的 GNU Make)
|
||||||
==================
|
=====================================
|
||||||
:link_to_translation:`en:[English]`
|
:link_to_translation:`en:[English]`
|
||||||
|
|
||||||
.. include:: ../gnu-make-legacy.rst
|
.. include:: ../gnu-make-legacy.rst
|
||||||
@ -26,18 +26,14 @@ C 文件可以包含多个测试用例。测试文件的名字要以 “test”
|
|||||||
// 在这里添加测试用例
|
// 在这里添加测试用例
|
||||||
}
|
}
|
||||||
|
|
||||||
- 第一个参数是字符串,用来描述当前测试。
|
第一个参数是字符串,用来描述当前测试。第二个参数是字符串,用方括号中的标识符来表示,标识符用来对相关测试或具有特定属性的测试进行分组。
|
||||||
|
|
||||||
- 第二个参数是字符串,用方括号中的标识符来表示,标识符用来对相关测试或具有特定属性的测试进行分组。
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
没有必要在每个测试用例中使用 ``UNITY_BEGIN()`` 和 ``UNITY_END()``
|
没有必要在每个测试用例中使用 ``UNITY_BEGIN()`` 和 ``UNITY_END()``
|
||||||
来声明主函数的区域, ``unity_platform.c`` 会自动调用
|
来声明主函数的区域, ``unity_platform.c`` 会自动调用 ``UNITY_BEGIN()``, 然后运行测试用例,最后调用 ``UNITY_END()``。
|
||||||
``UNITY_BEGIN()``\ , 然后运行测试用例,最后调用 ``UNITY_END()``\ 。
|
|
||||||
|
|
||||||
每一个测试子目录下都应该包含一个
|
每一个 `测试` 子目录下都需包含
|
||||||
``component.mk``\ ,并且里面至少要包含如下的一行内容:
|
``component.mk`` 文件,并且其中至少要包含如下的一行代码::
|
||||||
|
|
||||||
.. code:: makefile
|
|
||||||
|
|
||||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
||||||
|
|
||||||
@ -48,10 +44,8 @@ http://www.throwtheswitch.org/unity 。
|
|||||||
添加多设备测试用例
|
添加多设备测试用例
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
常规测试用例会在一个 DUT(Device Under
|
常规测试用例会在一个 DUT(Device Under Test,在试设备)上执行.但是,那些需要互相通信的组件(比如
|
||||||
Test,在试设备)上执行,那些需要互相通信的组件(比如
|
GPIO、SPI)需要与其通信的其他设备,因此不能使用常规测试用例进行测试。多设备测试用例包括写入多个测试函数,并在多个 DUT 进行运行测试。
|
||||||
GPIO,SPI...)不能使用常规测试用例进行测试。多设备测试用例支持使用多个
|
|
||||||
DUT 进行写入和运行测试。
|
|
||||||
|
|
||||||
以下是一个多设备测试用例:
|
以下是一个多设备测试用例:
|
||||||
|
|
||||||
@ -109,11 +103,11 @@ DUT2(slave)终端:
|
|||||||
|
|
||||||
Send signal: [output high level]!
|
Send signal: [output high level]!
|
||||||
|
|
||||||
一旦 DUT2 发送了该信号,您需要在 DUT2 的终端输入回车,然后 DUT1 会从
|
一旦 DUT2 发送了该信号,您需要在 DUT1 按回车键,然后 DUT1 会从
|
||||||
``unity_wait_for_signal`` 函数中解除阻塞,并开始更改 GPIO 的电平。
|
``unity_wait_for_signal`` 函数中解除阻塞,并开始更改 GPIO 的电平。
|
||||||
|
|
||||||
信号也可以用来在不同 DUT 之间传递参数。例如,DUT1 希望能够拿到 DUT2 的 MAC 地址,来进行蓝牙连接。
|
信号也可以用来在不同设备之间传递参数。例如,DUT1 希望得到 DUT2 的 MAC 地址来进行蓝牙连接,
|
||||||
这时,我们可以使用 ``unity_wait_for_signal_param`` 以及 ``unity_send_signal_param``。
|
这时,可使用 ``unity_wait_for_signal_param`` 以及 ``unity_send_signal_param``。
|
||||||
|
|
||||||
DUT1 终端::
|
DUT1 终端::
|
||||||
|
|
||||||
@ -125,7 +119,7 @@ DUT2 终端::
|
|||||||
|
|
||||||
Send signal: [dut2 mac address][10:20:30:40:50:60]!
|
Send signal: [dut2 mac address][10:20:30:40:50:60]!
|
||||||
|
|
||||||
一旦 DUT2 发送信号,您需要在 DUT1 输入 ``10:20:30:40:50:60`` 及回车,然后 DUT1 会从 ``unity_wait_for_signal_param`` 中获取到蓝牙地址的字符串,并解除阻塞开始蓝牙连接。
|
一旦 DUT2 发送信号,您需要在 DUT1 输入 ``10:20:30:40:50:60`` 并按回车键,然后 DUT1 会获取 DUT2 的 MAC 地址字符串,并从 ``unity_wait_for_signal_param`` 函数中解除阻塞,然后蓝牙连接 DUT2。
|
||||||
|
|
||||||
|
|
||||||
添加多阶段测试用例
|
添加多阶段测试用例
|
||||||
@ -168,9 +162,9 @@ DUT2 终端::
|
|||||||
- ``make TEST_COMPONENTS='xxx'`` - 编译单元测试程序,测试指定的组件。
|
- ``make TEST_COMPONENTS='xxx'`` - 编译单元测试程序,测试指定的组件。
|
||||||
|
|
||||||
- ``make TESTS_ALL=1 TEST_EXCLUDE_COMPONENTS='xxx'`` -
|
- ``make TESTS_ALL=1 TEST_EXCLUDE_COMPONENTS='xxx'`` -
|
||||||
编译单元测试程序,测试所有(除开指定)的组件。例如
|
编译单元测试程序,测试所有(除开指定)的组件。(例如
|
||||||
``make TESTS_ALL=1 TEST_EXCLUDE_COMPONENTS='ulp mbedtls'`` -
|
``make TESTS_ALL=1 TEST_EXCLUDE_COMPONENTS='ulp mbedtls'`` -
|
||||||
编译所有的单元测试,不包括 ``ulp`` 和 ``mbedtls``\ 组件。
|
编译所有的单元测试,不包括 ``ulp`` 和 ``mbedtls`` 组件。)
|
||||||
|
|
||||||
当编译完成时,它会打印出烧写芯片的指令。您只需要运行 ``make flash``
|
当编译完成时,它会打印出烧写芯片的指令。您只需要运行 ``make flash``
|
||||||
即可烧写所有编译输出的文件。
|
即可烧写所有编译输出的文件。
|
||||||
@ -225,7 +219,7 @@ DUT2 终端::
|
|||||||
|
|
||||||
- 测试用例的序号(例如 ``1``),运行单个测试用例。
|
- 测试用例的序号(例如 ``1``),运行单个测试用例。
|
||||||
|
|
||||||
- 方括号中的模块名字(例如 ``[cxx]``),运行指定模块所有的测试用例。
|
- 方括号中的模组名称(例如 ``[cxx]``),运行指定模组的所有的测试用例。
|
||||||
|
|
||||||
- 星号 (``*``),运行所有测试用例。
|
- 星号 (``*``),运行所有测试用例。
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ C 文件可以包含多个测试用例。测试文件的名字要以 “test”
|
|||||||
|
|
||||||
测试用例需要通过 C 文件中特定的函数来添加,如下所示:
|
测试用例需要通过 C 文件中特定的函数来添加,如下所示:
|
||||||
|
|
||||||
.. code:: c
|
.. code-block:: c
|
||||||
|
|
||||||
TEST_CASE("test name", "[module name]"
|
TEST_CASE("test name", "[module name]"
|
||||||
{
|
{
|
||||||
@ -28,23 +28,21 @@ C 文件可以包含多个测试用例。测试文件的名字要以 “test”
|
|||||||
|
|
||||||
- 第二个参数是字符串,用方括号中的标识符来表示,标识符用来对相关测试或具有特定属性的测试进行分组。
|
- 第二个参数是字符串,用方括号中的标识符来表示,标识符用来对相关测试或具有特定属性的测试进行分组。
|
||||||
|
|
||||||
|
.. note::
|
||||||
没有必要在每个测试用例中使用 ``UNITY_BEGIN()`` 和 ``UNITY_END()``
|
没有必要在每个测试用例中使用 ``UNITY_BEGIN()`` 和 ``UNITY_END()``
|
||||||
来声明主函数的区域, ``unity_platform.c`` 会自动调用
|
来声明主函数的区域, ``unity_platform.c`` 会自动调用 ``UNITY_BEGIN()``\ , 然后运行测试用例,最后调用 ``UNITY_END()``。
|
||||||
``UNITY_BEGIN()``\ , 然后运行测试用例,最后调用 ``UNITY_END()``\ 。
|
|
||||||
|
|
||||||
``test`` 子目录需要包含 :ref:`组件 CMakeLists.txt <component-directories>`,因为他们本身就是一种组件。ESP-IDF 使用了
|
``test`` 子目录应包含 :ref:`组件 CMakeLists.txt <component-directories>`,因为他们本身就是一种组件。ESP-IDF 使用了
|
||||||
``unity`` 测试框架,需要将其指定为组件的依赖项。通常,组件
|
``unity`` 测试框架,需要将其指定为组件的依赖项。通常,组件
|
||||||
:ref:`需要手动指定待编译的源文件 <cmake-file-globbing>`;但是,对于测试组件来说,这个要求被放宽了,仅仅是建议使用 “COMPONENT_SRCDIRS”。
|
:ref:`需要手动指定待编译的源文件 <cmake-file-globbing>`;但是,对于测试组件来说,这个要求被放宽为仅建议将参数 ``SRC_DIRS`` 用于 ``idf_component_register``。
|
||||||
|
|
||||||
总的来说,``test`` 子目录下最简单的 CMakeLists.txt 文件可能如下所示:
|
总的来说,``test`` 子目录下最小的 CMakeLists.txt 文件可能如下所示:
|
||||||
|
|
||||||
.. code:: cmake
|
.. code:: cmake
|
||||||
|
|
||||||
set(COMPONENT_SRCDIRS ".")
|
idf_component_register(SRC_DIRS "."
|
||||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
INCLUDE_DIRS "."
|
||||||
set(COMPONENT_REQUIRES unity)
|
REQUIRES unity)
|
||||||
|
|
||||||
register_component()
|
|
||||||
|
|
||||||
更多关于如何在 Unity 下编写测试用例的信息,请查阅
|
更多关于如何在 Unity 下编写测试用例的信息,请查阅
|
||||||
http://www.throwtheswitch.org/unity 。
|
http://www.throwtheswitch.org/unity 。
|
||||||
@ -53,10 +51,8 @@ http://www.throwtheswitch.org/unity 。
|
|||||||
添加多设备测试用例
|
添加多设备测试用例
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
常规测试用例会在一个 DUT(Device Under
|
常规测试用例会在一个 DUT(Device Under Test,在试设备)上执行.但是,那些需要互相通信的组件(比如
|
||||||
Test,在试设备)上执行,那些需要互相通信的组件(比如
|
GPIO、SPI)需要与其通信的其他设备,因此不能使用常规测试用例进行测试。多设备测试用例包括写入多个测试函数,并在多个 DUT 进行运行测试。
|
||||||
GPIO,SPI...)不能使用常规测试用例进行测试。多设备测试用例支持使用多个
|
|
||||||
DUT 进行写入和运行测试。
|
|
||||||
|
|
||||||
以下是一个多设备测试用例:
|
以下是一个多设备测试用例:
|
||||||
|
|
||||||
@ -114,7 +110,7 @@ DUT2(slave)终端:
|
|||||||
|
|
||||||
Send signal: [output high level]!
|
Send signal: [output high level]!
|
||||||
|
|
||||||
一旦 DUT2 发送了该信号,您需要在 DUT2 的终端输入回车,然后 DUT1 会从
|
一旦 DUT2 发送了该信号,您需要在 DUT1 的终端按回车键,然后 DUT1 会从
|
||||||
``unity_wait_for_signal`` 函数中解除阻塞,并开始更改 GPIO 的电平。
|
``unity_wait_for_signal`` 函数中解除阻塞,并开始更改 GPIO 的电平。
|
||||||
|
|
||||||
|
|
||||||
@ -158,9 +154,9 @@ DUT2(slave)终端:
|
|||||||
- ``idf.py -T xxx build`` - 编译单元测试程序,测试指定的组件。
|
- ``idf.py -T xxx build`` - 编译单元测试程序,测试指定的组件。
|
||||||
|
|
||||||
- ``idf.py -T all -E xxxbuild`` -
|
- ``idf.py -T all -E xxxbuild`` -
|
||||||
编译单元测试程序,测试所有(除开指定)的组件。例如
|
编译单元测试程序,测试所有(除开指定)的组件。(例如
|
||||||
``idf.py -T all -E ulp mbedtls build`` -
|
``idf.py -T all -E ulp mbedtls build`` -
|
||||||
编译所有的单元测试,不包括 ``ulp`` 和 ``mbedtls``\ 组件。
|
编译所有的单元测试,不包括 ``ulp`` 和 ``mbedtls`` 组件。)
|
||||||
|
|
||||||
当编译完成时,它会打印出烧写芯片的指令。您只需要运行 ``idf.py flash``
|
当编译完成时,它会打印出烧写芯片的指令。您只需要运行 ``idf.py flash``
|
||||||
即可烧写所有编译输出的文件。
|
即可烧写所有编译输出的文件。
|
||||||
|
Loading…
Reference in New Issue
Block a user