Esp32 ulp

Esp32 ulp DEFAULT

ULP coprocessor programming¶

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.

Compiling ULP code¶

To compile ULP code as part of a 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. Modify the component makefile, adding the following:

    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

    Here is each line explained:

    ULP_APP_NAME

    Name of the generated ULP application, without an extension. This name is used for build products of the ULP application: ELF file, map file, binary file, generated header file, and generated linker export file.

    ULP_S_SOURCES

    List of assembly files to be passed to the ULP assembler. These must be absolute paths, i.e. start with . Consider using function if more than one file needs to be listed. Paths are relative to component build directory, so prefixing them is not necessary.

    ULP_EXP_DEP_OBJECTS

    List of object files names within the component which include the generated header file. 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. See section below explaining the concept of generated header files for ULP applications.

    include $(IDF_PATH)/components/ulp/component_ulp_common.mk

    Includes common definitions of ULP build steps. Defines build targets for ULP object files, ELF file, binary file, etc.

  2. Build the application as usual (e.g. make app)

    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).
    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.
    3. Run linker script template through 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.
    5. Dump contents of the ELF file into binary (ulp_app_name.bin) for embedding into the application.
    6. Generate 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.
    8. Add the generated binary to the list of binary files to be emedded into the application.

Accessing ULP program variables¶

Global symbols defined in the ULP program may be used inside the main program.

For example, ULP program may define a variable which will define the number of ADC measurements the program needs to make before waking up the chip from deep sleep:

.globalmeasurement_countmeasurement_count:.long0/*later,usemeasurement_count*/mover3,measurement_countldr3,r3,0

Main program needs to initialize this variable before ULP program is started. Build system makes this possible by generating a and files which define global symbols present in the ULP program. This files include each global symbol defined in the ULP program, prefixed with .

The header file contains declaration of the symbol:

externuint32_tulp_measurement_count;

Note that all symbols (variables, arrays, functions) are declared as . For functions and arrays, take address of the symbol and cast to the appropriate type.

The generated linker script file defines locations of symbols in RTC_SLOW_MEM:

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:

#include "ulp_app_name.h"//latervoidinit_ulp_vars(){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.

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.:

printf("Last measurement value: %d\n",ulp_last_measurement&UINT16_MAX);

Starting the ULP program¶

To run a ULP program, main application needs to load the ULP program into RTC memory using function, and then start it using 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.

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 :

externconstuint8_tbin_start[]asm("_binary_ulp_app_name_bin_start");externconstuint8_tbin_end[]asm("_binary_ulp_app_name_bin_end");voidstart_ulp_program(){ESP_ERROR_CHECK(ulp_load_binary(0/*loadaddress,setto0whenusingdefaultlinkerscripts*/,bin_start,(bin_end-bin_start)/sizeof(uint32_t)));}
esp_err_t (uint32_t load_addr, const uint8_t *program_binary, size_t program_size

Load ULP program binary into RTC memory.

ULP program binary should have the following format (all values little-endian):

  1. MAGIC, (value 0x00706c75, 4 bytes)
  2. TEXT_OFFSET, offset of .text section from binary start (2 bytes)
  3. TEXT_SIZE, size of .text section (2 bytes)
  4. DATA_SIZE, size of .data section (2 bytes)
  5. BSS_SIZE, size of .bss section (2 bytes)
  6. (TEXT_OFFSET - 16) bytes of arbitrary data (will not be loaded into RTC memory)
  7. .text section
  8. .data section

Linker script in components/ulp/ld/esp32.ulp.ld produces ELF files which correspond to this format. This linker script produces binaries with load_addr == 0.

Return
  • ESP_OK on success
  • ESP_ERR_INVALID_ARG if load_addr is out of range
  • ESP_ERR_INVALID_SIZE if program_size doesn’t match (TEXT_OFFSET + TEXT_SIZE + DATA_SIZE)
  • ESP_ERR_NOT_SUPPORTED if the magic number is incorrect
Parameters
  • : address where the program should be loaded, expressed in 32-bit words
  • : pointer to program binary
  • : size of the program binary

Once the program is loaded into RTC memory, application can start it, passing the address of the entry point to function:

ESP_ERROR_CHECK(ulp_run((&ulp_entry-RTC_SLOW_MEM)/sizeof(uint32_t)));
esp_err_t (uint32_t entry_point

Run the program loaded into RTC memory.

Return
ESP_OK on success
Parameters
  • : entry point, expressed in 32-bit words

Declaration of the entry point symbol comes from the above mentioned generated header file, . In assembly source of the ULP application, this symbol must be marked as :

.globalentryentry:/*codestartshere*/

ULP program flow¶

ULP coprocessor is started by a timer. The timer is started once 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 registers (x = 0..4). When starting the ULP for the first time, will be used to set the number of timer ticks. Later the ULP program can select another register using instruction.

The application can set ULP timer period values (SENS_ULP_CP_SLEEP_CYCx_REG, x = 0..4) using function.

esp_err_t (size_t period_index, uint32_t period_us

Set one of ULP wakeup period values.

ULP coprocessor starts running the program when the wakeup timer counts up to a given value (called period). There are 5 period values which can be programmed into SENS_ULP_CP_SLEEP_CYCx_REG registers, x = 0..4. By default, wakeup timer will use the period set into SENS_ULP_CP_SLEEP_CYC0_REG, i.e. period number 0. ULP program code can use SLEEP instruction to select which of the SENS_ULP_CP_SLEEP_CYCx_REG should be used for subsequent wakeups.

Return
  • ESP_OK on success
  • ESP_ERR_INVALID_ARG if period_index is out of range
Parameters
  • : wakeup period setting number (0 - 4)
  • : wakeup period, us

Once the timer counts the number of ticks set in the selected register, ULP coprocessor powers up and starts running the program from the entry point set in the call to .

The program runs until it encounters a instruction or an illegal instruction. Once the program halts, ULP coprocessor powers down, and the timer is started again.

To disable the timer (effectively preventing the ULP program from running again), clear the bit in the register. This can be done both from ULP code and from the main program.


© Copyright 2016 - 2017, Espressif. Revision .

Built with Sphinx using a theme provided by Read the Docs.
Sours: http://demo-dijiudu.readthedocs.io/en/latest/api-guides/ulp.html

The ULP is a low-power coprocessor (Ultra Low Power) integrated into the ESP32 MCU. It’s a very small processor that can run independently from the main cores and that has access to the GPIOs, some peripheral and an I²C controller.

The ULP is also able to run when the ESP32 is is deep-sleep mode. In this mode, nearly the whole MCU is powered-off, except the RTC domain, in which the ULP is built. It is also able to wake the ESP32 up.

A possible application of the ULP is to acquire temperature while the ESP32 is in deep-sleep and to wake it up once it reaches a specified threshold.

Programming the ULP seems very interesting, but even if the documentation from Espressif is very complete, I couldn’t find a simple and easy example to learn how to use it.

Here is then in details an ultra simple exemple : the traditional blink, which blinks 2 LEDs on the WROVER-KIT V3.

The example I’ll explain here consists in blinking 2 LEDs (the blue and the green ones from the ESP32-WROVER-KIT V3). The 1st one is driven by the main core, the second one by the ULP.

Installing the ULP toolchain

The installation is documented in the official documentation.

The project & CMake

Oh, by the way : you program the ULP in its assembly language. There’s obviously no C compiler available!

We’ll start by creating a file with the extension .S in a subdirectory of the project. I called it ulp/main-ulp-blink.S.

Next, we’ll edit the file CMakeLists.txt of the main component to add the code for the ULP:

The “application” program

This program is voluntarily very simple. It starts by initializing the GPIOs : GPIO4 for the blue LED, GPIO2/RTC_GPIO12 for the green LED. Not that we use rtc method for the green LED, as it’ll be driven by the ULP.

Next, it programs the ULP and start it, and then, enters in an infinite loop. This loop powers-ON/OFF the blue LED every 1 second. In the meantime, the ULP takes care of the green LED.

The ULP program

For informations, here is the documentation of the assembly language. This document is very useful if you want to understand and write your own programs.

Wow, that’s a lot of WAITs… I’ll come to it later on 🙂

The program, written in ULP assembly, start by declaring an empty BSS segment (it contains declaration of variable, but this program is so simple it doesn’t even need one). Next comes the TEXT segment which contains the code.

This code writes a 1 on GPIO2/RTC_GPIO12, waits, wait, waits some more, than writes a 0 on this GPIO, waits again, and start again.

Why so much WAITs? The mnemonic WAIT accepts as operand a 16bits (0..65535) value. It’s the number of cycles (@8MHZ) we want to wait until the next instruction. If you specify a greater value, the assembler drops them, and only uses the 16 lower bits, and waits for less than you would have expected.

In order to keep the example as simple as possible, I’ve decided to not write a true loop to wait, and simply add multiple WAITs so that we can see the blinking with our eyes.

The source code of this example is available on my GitHub.

Sours: https://codingfield.com/en/2019/01/15/ulp-esp32-un-exemple-simple/
  1. Tall urns for plants
  2. Medications for myoclonic seizures
  3. City of loveland golf
  4. Viridian laser hellcat

ULP Coprocessor programming¶

[中文]

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.

Compiling the ULP Code¶

To compile the ULP code as part of the component, the following steps must be taken:

  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/.

  1. Call from the component CMakeLists.txt after registration. For example:

    ... idf_component_register() set(ulp_app_name ulp_${COMPONENT_NAME}) set(ulp_s_sources ulp/ulp_assembly_source_file.S) set(ulp_exp_dep_srcs "ulp_c_source_file.c") ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}")

The first argument to 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. 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 will be created before any of these files are compiled. See section below for the concept of generated header files for ULP applications.

  1. Build the application as usual (e.g. idf.py app)

    Inside, the build system will take the following steps to build ULP program:

    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 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 the linker script template through the C preprocessor. The template is located in directory.

    4. Link the object files into an output ELF file (). The Map file () generated at this stage may be useful for debugging purposes.

    5. Dump the contents of the ELF file into a binary () which can then be embedded into the application.

    6. Generate a list of global symbols () in the ELF file using .

    7. Create an LD export script and header file ( and ) containing the symbols from . This is done using the utility.

    8. Add the generated binary to the list of binary files to be embedded into the application.

Accessing the ULP Program Variables¶

Global symbols defined in the ULP program may be used inside the main program.

For example, the ULP program may define a variable which will define the number of ADC measurements the program needs to make before waking up the chip from deep sleep:

.globalmeasurement_countmeasurement_count:.long0/*later,usemeasurement_count*/mover3,measurement_countldr3,r3,0

The main program needs to initialize this variable before the ULP program is started. The build system makes this possible by generating the and 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 .

The header file contains the declaration of the symbol:

externuint32_tulp_measurement_count;

Note that all symbols (variables, arrays, functions) are declared as . For functions and arrays, take the address of the symbol and cast it to the appropriate type.

The generated linker script file defines the locations of symbols in RTC_SLOW_MEM:

PROVIDE(ulp_measurement_count=0x50000060);

To access the ULP program variables from the main program, the generated header file should be included using an statement. This will allow the ULP program variables to be accessed as regular variables:

#include "ulp_app_name.h"//latervoidinit_ulp_vars(){ulp_measurement_count=64;}

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, 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);

Starting the ULP Program¶

To run a ULP program, the main application needs to load the ULP program into RTC memory using the function, and then start it using the function.

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. The application can reference this blob and load it in the following way (suppose ULP_APP_NAME was defined to ):

externconstuint8_tbin_start[]asm("_binary_ulp_app_name_bin_start");externconstuint8_tbin_end[]asm("_binary_ulp_app_name_bin_end");voidstart_ulp_program(){ESP_ERROR_CHECK(ulp_load_binary(0/*loadaddress,setto0whenusingdefaultlinkerscripts*/,bin_start,(bin_end-bin_start)/sizeof(uint32_t)));}
esp_err_t(uint32_t load_addr, const uint8_t *program_binary, size_t program_size

Load ULP program binary into RTC memory.

ULP program binary should have the following format (all values little-endian):

  1. MAGIC, (value 0x00706c75, 4 bytes)

  2. TEXT_OFFSET, offset of .text section from binary start (2 bytes)

  3. TEXT_SIZE, size of .text section (2 bytes)

  4. DATA_SIZE, size of .data section (2 bytes)

  5. BSS_SIZE, size of .bss section (2 bytes)

  6. (TEXT_OFFSET - 12) bytes of arbitrary data (will not be loaded into RTC memory)

  7. .text section

  8. .data section

Linker script in components/ulp/ld/esp32.ulp.ld produces ELF files which correspond to this format. This linker script produces binaries with load_addr == 0.

Return
  • ESP_OK on success

  • ESP_ERR_INVALID_ARG if load_addr is out of range

  • ESP_ERR_INVALID_SIZE if program_size doesn’t match (TEXT_OFFSET + TEXT_SIZE + DATA_SIZE)

  • ESP_ERR_NOT_SUPPORTED if the magic number is incorrect

Parameters
  • : address where the program should be loaded, expressed in 32-bit words

  • : pointer to program binary

  • : size of the program binary

Once the program is loaded into RTC memory, the application can start it, passing the address of the entry point to the function:

ESP_ERROR_CHECK(ulp_run(&ulp_entry-RTC_SLOW_MEM));
esp_err_t(uint32_t entry_point

Run the program loaded into RTC memory.

Return

ESP_OK on success

Parameters
  • : entry point, expressed in 32-bit words

Declaration of the entry point symbol comes from the generated header file mentioned above, . In the assembly source of the ULP application, this symbol must be marked as :

.globalentryentry:/*codestartshere*/

ESP32 ULP program flow¶

ESP32 ULP coprocessor is started by a timer. The timer is started once is called. The timer counts a number of RTC_SLOW_CLK ticks (by default, produced by an internal 150 kHz RC oscillator). The number of ticks is set using registers (x = 0..4). When starting the ULP for the first time, will be used to set the number of timer ticks. Later the ULP program can select another register using instruction.

The application can set ULP timer period values (SENS_ULP_CP_SLEEP_CYCx_REG, x = 0..4) using function.

esp_err_t(size_t period_index, uint32_t period_us

Set one of ULP wakeup period values.

ULP coprocessor starts running the program when the wakeup timer counts up to a given value (called period). There are 5 period values which can be programmed into SENS_ULP_CP_SLEEP_CYCx_REG registers, x = 0..4 for ESP32, and one period value which can be programmed into RTC_CNTL_ULP_CP_TIMER_1_REG register for ESP32-S2. By default, for ESP32, wakeup timer will use the period set into SENS_ULP_CP_SLEEP_CYC0_REG, i.e. period number 0. ULP program code can use SLEEP instruction to select which of the SENS_ULP_CP_SLEEP_CYCx_REG should be used for subsequent wakeups.

However, please note that SLEEP instruction issued (from ULP program) while the system is in deep sleep mode does not have effect, and sleep cycle count 0 is used.

For ESP32-s2 the SLEEP instruction not exist. Instead a WAKE instruction will be used.

Note

The ULP FSM requires two clock cycles to wakeup before being able to run the program. Then additional 16 cycles are reserved after wakeup waiting until the 8M clock is stable. The FSM also requires two more clock cycles to go to sleep after the program execution is halted. The minimum wakeup period that may be set up for the ULP is equal to the total number of cycles spent on the above internal tasks. For a default configuration of the ULP running at 150kHz it makes about 133us.

Return
  • ESP_OK on success

  • ESP_ERR_INVALID_ARG if period_index is out of range

Parameters
  • : wakeup period setting number (0 - 4)

  • : wakeup period, us

Once the timer counts the number of ticks set in the selected register, ULP coprocessor powers up and starts running the program from the entry point set in the call to .

The program runs until it encounters a instruction or an illegal instruction. Once the program halts, ULP coprocessor powers down, and the timer is started again.

To disable the timer (effectively preventing the ULP program from running again), clear the bit in the register. This can be done both from ULP code and from the main program.


© Copyright 2016 - 2021, Espressif Systems (Shanghai) Co., Ltd

Sours: https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/ulp.html
Deep sleep: Use RTC memory to store data (ESP32 + Arduino series)

ulptool v2.4.2

Now Arduino can program the ULP coprocessor for your esp32 projects. The guide below assumes you installed the esp32 core with the preferred method using the board manager.

Typically in Arduino you can compile assembly files using the '.S' extension. Using the ESP32 Arduino core framework these files would correspond to the Xtensa processors whose toolchain is incompatible with the ULP coprocessor. Luckily, Arduino provides a fairly easy series of recipes for building the ULP assembly files by using the '.s' extension which Arduino will let you create. Take note, the extension is a lower case s. In tring to keep the ulp build process the same as the esp-idf framework only a few small modifications are needed to the esp32 Arduino installation.

A new experimental c compiler (lcc) for the ulp implemented by Jason Fuller is included now. Currently only Mac and Linux have been built but hopefully I'll have Windows soon:) There are many limitations and those can found at his github page here: https://github.com/jasonful/lcc Examples can be found in the ulp_examples/ulpcc folder.

Note - platform.local.txt version does not follow ulptool version.

  1. Download the latest release of this repository and unpack-> https://github.com/duff2013/ulptool/releases/latest delete the release version number so the folder is just called 'ulptool'

  2. Download and unpack the latest pre-compiled binutils-esp32ulp toolchain for Mac/Linux/Windows: https://github.com/espressif/binutils-esp32ulp/releases/latest

  3. Find your Arduino-esp32 core directory which Arduino IDE uses:

  4. Move the ulptool folder you downloaded and unpacked to the tools folder here -> ... /esp32/tools/ulptool/.

  5. Copy the 'platform.local.txt' file to ... /esp32/hardware/esp32/1.0.0/. Remember 1.0.0 has to match your esp32 core version.

  6. In the ulptool folder, move or copy the ... /ulptool/src/ulp_examples folder to where Arduino saves your sketches.

  7. Move esp32ulp-elf-binutils folder you downloaded and unpacked to -> ... /esp32/tools/ulptool/src/esp32ulp-elf-binutils/.

That's it, you now have all the files in place, lets look at very simple example to get you compiling ulp assembly code!

Assembly Example:

Open a blank Arduino sketch and copy and paste the code below into that sketch.

Create a new tab named ulp.s, take notice that the extension is a lower case s. Copy the code below into the ulp assembly file you just created.

Create a new tab named ulp_main.h. This header allows your sketch to see global variables whose memory is allocated in your ulp assembly file. This memory is from the SLOW RTC section. Copy the code below into the header file. As with the esp-idf you have to add 'ulp_' to the front of the variable name. Unlike esp-idf the name of this header is always ulp_main.h.

Upload the code then open your serial monitor, you should see the variable 'ulp_count' increment every 100 msecs.

ULPCC Example:

Open a blank Arduino sketch and copy and paste the code below into the that sketch. This is basically the same as the example above but written in c:)

Create a new tab named ulp_counter.c, take notice of the , code between these will be compiled by the ulp c compiler. Do not place any code outside of the or your sketch will fail to build. For further information about the limitations see Jason Fullers github site which is developed for the esp-idf not Arduino.

Create a new tab named ulp_main.h. This header allows your sketch to see global variables whose memory is allocated in your ulp assembly file. This memory is from the SLOW RTC section. Copy the code below into the header file. As with the esp-idf you have to add 'ulp_' to the front of the variable name. Unlike esp-idf the name of this header is always ulp_main.h.

Upload the code then open your serial monitor, you should see the variable 'ulp_counter' increment every 100 msecs.

All the magic happens in the python script called esp32ulp_build_recipe.py along with espressif's esp32ulp_mapgen.py script.

While almost a complete solution to programing the ULP coprocessor in assembly, there are currently a few limitations. Once I fix these, I'll remove them from this list.

  1. ulpcc is still experimental, Mac and Linux only so far.
  2. Errors can be non-informative.
  3. Have to use the custom binary loader function now. (ulptool_load_binary)
Sours: https://github.com/duff2013/ulptool

Ulp esp32

ESP32 ULP coprocessor instruction set¶

This document provides details about the instructions used by ESP32 ULP coprocessor assembler.

ULP coprocessor has 4 16-bit general purpose registers, labeled R0, R1, R2, R3. It also has an 8-bit counter register (stage_cnt) which can be used to implement loops. Stage count register is accessed using special instructions.

ULP coprocessor can access 8k bytes of RTC_SLOW_MEM memory region. Memory is addressed in 32-bit word units. It can also access peripheral registers in RTC_CNTL, RTC_IO, and SENS peripherals.

All instructions are 32-bit. Jump instructions, ALU instructions, peripheral register and memory access instructions are executed in 1 cycle. Instructions which work with peripherals (TSENS, ADC, I2C) take variable number of cycles, depending on peripheral operation.

The instruction syntax is case insensitive. Upper and lower case letters can be used and intermixed arbitrarily. This is true both for register names and instruction names.

Note about addressing¶

ESP32 ULP coprocessor’s JUMP, ST, LD instructions which take register as an argument (jump address, store/load base address) expect the argument to be expressed in 32-bit words.

Consider the following example program:

entry:NOPNOPNOPNOPloop:MOVER1,loopJUMPR1

When this program is assembled and linked, address of label will be equal to 16 (expressed in bytes). However JUMP instruction expects the address stored in register to be expressed in 32-bit words. To account for this common use case, assembler will convert the address of label loop from bytes to words, when generating instruction, so the code generated code will be equivalent to:

0000NOP0004NOP0008NOP000cNOP0010MOVER1,40014JUMPR1

The other case is when the argument of instruction is not a label but a constant. In this case assembler will use the value as is, without any conversion:

.setval,0x10MOVER1,val

In this case, value loaded into R1 will be .

Similar considerations apply to and instructions. Consider the following code:

.globalarrayarray:.long0.long0.long0.long0MOVER1,arrayMOVER2,0x1234STR2,R1,0//writevalueofR2intothefirstarrayelement,//i.e.array[0]STR2,R1,4//writevalueofR2intothesecondarrayelement//(4byteoffset),i.e.array[1]ADDR1,R1,2//thisincrementsaddressby2words(8bytes)STR2,R1,0//writevalueofR2intothethirdarrayelement,//i.e.array[2]

Note about instruction execution time¶

ULP coprocessor is clocked from RTC_FAST_CLK, which is normally derived from the internal 8MHz oscillator. Applications which need to know exact ULP clock frequency can calibrate it against the main XTAL clock:

#include "soc/rtc.h"//calibrate8M/256clockagainstXTAL,get8M/256clockperioduint32_trtc_8md256_period=rtc_clk_cal(RTC_CAL_8MD256,100);uint32_trtc_fast_freq_hz=1000000ULL*(1<<RTC_CLK_CAL_FRACT)*256/rtc_8md256_period;

ULP coprocessor needs certain number of clock cycles to fetch each instruction, plus certain number of cycles to execute it, depending on the instruction. See description of each instruction below for details on the execution time.

Instruction fetch time is:

  • 2 clock cycles — for instructions following ALU and branch instructions.

  • 4 clock cycles — in other cases.

Note that when accessing RTC memories and RTC registers, ULP coprocessor has lower priority than the main CPUs. This means that ULP coprocessor execution may be suspended while the main CPUs access same memory region as the ULP.

NOP - no operation¶

Syntax

NOP

Operands

None

Cycles

2 cycle to execute, 4 cycles to fetch next instruction

Description

No operation is performed. Only the PC is incremented.

Example:

ADD - Add to register¶

Syntax

ADDRdst, Rsrc1, Rsrc2

ADDRdst, Rsrc1, imm

Operands
  • Rdst - Register R[0..3]

  • Rsrc1 - Register R[0..3]

  • Rsrc2 - Register R[0..3]

  • Imm - 16-bit signed value

Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction adds source register to another source register or to a 16-bit signed value and stores result to the destination register.

Examples:

1:ADDR1,R2,R3//R1=R2+R32:AddR1,R2,0x1234//R1=R2+0x12343:.setvalue1,0x03//constantvalue1=0x03AddR1,R2,value1//R1=R2+value14:.globallabel//declarationofvariablelabelAddR1,R2,label//R1=R2+label...label:nop//definitionofvariablelabel

SUB - Subtract from register¶

Syntax

SUBRdst, Rsrc1, Rsrc2

SUBRdst, Rsrc1, imm

Operands
  • Rdst - Register R[0..3]

  • Rsrc1 - Register R[0..3]

  • Rsrc2 - Register R[0..3]

  • Imm - 16-bit signed value

Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction subtracts the source register from another source register or subtracts 16-bit signed value from a source register, and stores result to the destination register.

Examples:

1:SUBR1,R2,R3//R1=R2-R32:subR1,R2,0x1234//R1=R2-0x12343:.setvalue1,0x03//constantvalue1=0x03SUBR1,R2,value1//R1=R2-value14:.globallabel//declarationofvariablelabelSUBR1,R2,label//R1=R2-label....label:nop//definitionofvariablelabel

AND - Logical AND of two operands¶

Syntax

ANDRdst, Rsrc1, Rsrc2

ANDRdst, Rsrc1, imm

Operands
  • Rdst - Register R[0..3]

  • Rsrc1 - Register R[0..3]

  • Rsrc2 - Register R[0..3]

  • Imm - 16-bit signed value

Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction does logical AND of a source register and another source register or 16-bit signed value and stores result to the destination register.

Examples:

1:ANDR1,R2,R3//R1=R2&R32:ANDR1,R2,0x1234//R1=R2&0x12343:.setvalue1,0x03//constantvalue1=0x03ANDR1,R2,value1//R1=R2&value14:.globallabel//declarationofvariablelabelANDR1,R2,label//R1=R2&label...label:nop//definitionofvariablelabel

OR - Logical OR of two operands¶

Syntax

ORRdst, Rsrc1, Rsrc2

ORRdst, Rsrc1, imm

Operands
  • Rdst - Register R[0..3]

  • Rsrc1 - Register R[0..3]

  • Rsrc2 - Register R[0..3]

  • Imm - 16-bit signed value

Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction does logical OR of a source register and another source register or 16-bit signed value and stores result to the destination register.

Examples:

1:ORR1,R2,R3//R1=R2 \|R32:ORR1,R2,0x1234//R1=R2 \|0x12343:.setvalue1,0x03//constantvalue1=0x03ORR1,R2,value1//R1=R2 \|value14:.globallabel//declarationofvariablelabelORR1,R2,label//R1=R2 \|label...label:nop//definitionofvariablelabel

LSH - Logical Shift Left¶

Syntax

LSHRdst, Rsrc1, Rsrc2

LSHRdst, Rsrc1, imm

Operands
  • Rdst - Register R[0..3]

  • Rsrc1 - Register R[0..3]

  • Rsrc2 - Register R[0..3]

  • Imm - 16-bit signed value

Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction does logical shift to left of source register to number of bits from another source register or 16-bit signed value and store result to the destination register.

Examples:

1:LSHR1,R2,R3//R1=R2<<R32:LSHR1,R2,0x03//R1=R2<<0x033:.setvalue1,0x03//constantvalue1=0x03LSHR1,R2,value1//R1=R2<<value14:.globallabel//declarationofvariablelabelLSHR1,R2,label//R1=R2<<label...label:nop//definitionofvariablelabel

RSH - Logical Shift Right¶

Syntax

RSHRdst, Rsrc1, Rsrc2

RSHRdst, Rsrc1, imm

Operands

Rdst - Register R[0..3] Rsrc1 - Register R[0..3] Rsrc2 - Register R[0..3] Imm - 16-bit signed value

Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction does logical shift to right of source register to number of bits from another source register or 16-bit signed value and store result to the destination register.

Examples:

1:RSHR1,R2,R3//R1=R2>>R32:RSHR1,R2,0x03//R1=R2>>0x033:.setvalue1,0x03//constantvalue1=0x03RSHR1,R2,value1//R1=R2>>value14:.globallabel//declarationofvariablelabelRSHR1,R2,label//R1=R2>>labellabel:nop//definitionofvariablelabel

MOVE – Move to register¶

Syntax

MOVERdst, Rsrc

MOVERdst, imm

Operands
  • Rdst – Register R[0..3]

  • Rsrc – Register R[0..3]

  • Imm – 16-bit signed value

Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction move to destination register value from source register or 16-bit signed value.

Note that when a label is used as an immediate, the address of the label will be converted from bytes to words. This is because LD, ST, and JUMP instructions expect the address register value to be expressed in words rather than bytes. To avoid using an extra instruction

Examples:

1:MOVER1,R2//R1=R22:MOVER1,0x03//R1=0x033:.setvalue1,0x03//constantvalue1=0x03MOVER1,value1//R1=value14:.globallabel//declarationoflabelMOVER1,label//R1=address_of(label)/4...label:nop//definitionoflabel

ST – Store data to the memory¶

Syntax

STRsrc, Rdst, offset

Operands
  • Rsrc – Register R[0..3], holds the 16-bit value to store

  • Rdst – Register R[0..3], address of the destination, in 32-bit words

  • Offset – 13-bit signed value, offset in bytes

Cycles

4 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction stores the 16-bit value of Rsrc to the lower half-word of memory with address Rdst+offset. The upper half-word is written with the current program counter (PC) (expressed in words, shifted left by 5 bits) OR’d with Rdst (0..3):

Mem[Rdst+offset/4]{31:0}={PC[10:0],3'b0, Rdst, Rsrc[15:0]}

The application can use higher 16 bits to determine which instruction in the ULP program has written any particular word into memory.

Examples:

1:STR1,R2,0x12//MEM[R2+0x12]=R12:.data//DatasectiondefinitionAddr1:.word123//DefinelabelAddr116bit.setoffs,0x00//Defineconstantoffs.text//TextsectiondefinitionMOVER1,1//R1=1MOVER2,Addr1//R2=Addr1STR1,R2,offs//MEM[R2+0]=R1//MEM[Addr1+0]willbe32'h600001

LD – Load data from the memory¶

Syntax

LDRdst, Rsrc, offset

Operands

Rdst – Register R[0..3], destination

Rsrc – Register R[0..3], holds address of destination, in 32-bit words

Offset – 13-bit signed value, offset in bytes

Cycles

4 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction loads lower 16-bit half-word from memory with address Rsrc+offset into the destination register Rdst:

Rdst[15:0]=Mem[Rsrc+offset/4][15:0]

Examples:

1:LDR1,R2,0x12//R1=MEM[R2+0x12]2:.data//DatasectiondefinitionAddr1:.word123//DefinelabelAddr116bit.setoffs,0x00//Defineconstantoffs.text//TextsectiondefinitionMOVER1,1//R1=1MOVER2,Addr1//R2=Addr1/4(addressoflabelisconvertedintowords)LDR1,R2,offs//R1=MEM[R2+0]//R1willbe123

JUMP – Jump to an absolute address¶

Syntax

JUMPRdst

JUMPImmAddr

JUMPRdst, Condition

JUMPImmAddr, Condition

Operands
  • Rdst – Register R[0..3] containing address to jump to (expressed in 32-bit words)

  • ImmAddr – 13 bits address (expressed in bytes), aligned to 4 bytes

  • Condition:
    • EQ – jump if last ALU operation result was zero

    • OV – jump if last ALU has set overflow flag

Cycles

2 cycles to execute, 2 cycles to fetch next instruction

Description

The instruction makes jump to the specified address. Jump can be either unconditional or based on an ALU flag.

Examples:

1:JUMPR1//JumptoaddressinR1(addressinR1isin32-bitwords)2:JUMP0x120,EQ//Jumptoaddress0x120(inbytes)ifALUresultiszero3:JUMPlabel//Jumptolabel...label:nop//Definitionoflabel4:.globallabel//DeclarationofgloballabelMOVER1,label//R1=label(valueloadedintoR1isinwords)JUMPR1//Jumptolabel...label:nop//Definitionoflabel

JUMPR – Jump to a relative offset (condition based on R0)¶

Syntax

JUMPRStep, Threshold, Condition

Operands
  • Step – relative shift from current position, in bytes

  • Threshold – threshold value for branch condition

  • Condition:
    • EQ (equal) – jump if value in R0 == threshold

    • LT (less than) – jump if value in R0 < threshold

    • LE (less or equal) – jump if value in R0 <= threshold

    • GT (greater than) – jump if value in R0 > threshold

    • GE (greater or equal) – jump if value in R0 >= threshold

Cycles

Conditions LT, GE, LE and GT: 2 cycles to execute, 2 cycles to fetch next instruction

Conditions LE and GT are implemented in the assembler using one JUMPR instructions:

//JUMPRtarget,threshold,GTisimplementedas:JUMPRtarget,threshold+1,GE//JUMPRtarget,threshold,LEisimplementedas:JUMPRtarget,threshold+1,LT

Conditions EQ is implemented in the assembler using two JUMPR instructions:

//JUMPRtarget,threshold,EQisimplementedas:JUMPRnext,threshold+1,GEJUMPRtarget,threshold,GEnext:

Therefore the execution time will depend on the branches taken: either 2 cycles to execute + 2 cycles to fetch, or 4 cycles to execute + 4 cycles to fetch.

Description

The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of R0 register value and the threshold value.

Examples:

1:pos:JUMPR16,20,GE//Jumptoaddress(position+16bytes)ifvalueinR0>=202://DowncountingloopusingR0registerMOVER0,16//load16intoR0label:SUBR0,R0,1//R0--NOP//dosomethingJUMPRlabel,1,GE//jumptolabelifR0>=1

JUMPS – Jump to a relative address (condition based on stage count)¶

Syntax

JUMPSStep, Threshold, Condition

Operands
  • Step – relative shift from current position, in bytes

  • Threshold – threshold value for branch condition

  • Condition:
    • EQ (equal) – jump if value in stage_cnt == threshold

    • LT (less than) – jump if value in stage_cnt < threshold

    • LE (less or equal) - jump if value in stage_cnt <= threshold

    • GT (greater than) – jump if value in stage_cnt > threshold

    • GE (greater or equal) — jump if value in stage_cnt >= threshold

Cycles

Conditions LE, LT, GE: 2 cycles to execute, 2 cycles to fetch next instruction

Conditions EQ, GT are implemented in the assembler using two JUMPS instructions:

//JUMPStarget,threshold,EQisimplementedas:JUMPSnext,threshold,LTJUMPStarget,threshold,LEnext://JUMPStarget,threshold,GTisimplementedas:JUMPSnext,threshold,LEJUMPStarget,threshold,GEnext:

Therefore the execution time will depend on the branches taken: either 2 cycles to execute + 2 cycles to fetch, or 4 cycles to execute + 4 cycles to fetch.

Description

The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of count register value and threshold value.

Examples:

1:pos:JUMPS16,20,EQ//Jumpto(position+16bytes)ifstage_cnt==202://UpcountingloopusingstagecountregisterSTAGE_RST//setstage_cntto0label:STAGE_INC1//stage_cnt++NOP//dosomethingJUMPSlabel,16,LT//jumptolabelifstage_cnt<16

STAGE_RST – Reset stage count register¶

Syntax

STAGE_RST

Operands

No operands

Description

The instruction sets the stage count register to 0

Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Examples:

1:STAGE_RST//Resetstagecountregister

STAGE_INC – Increment stage count register¶

Syntax

STAGE_INCValue

Operands
Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction increments stage count register by given value.

Examples:

1:STAGE_INC10//stage_cnt+=102://Upcountingloopexample:STAGE_RST//setstage_cntto0label:STAGE_INC1//stage_cnt++NOP//dosomethingJUMPSlabel,16,LT//jumptolabelifstage_cnt<16

STAGE_DEC – Decrement stage count register¶

Syntax

STAGE_DECValue

Operands
Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction decrements stage count register by given value.

Examples:

1:STAGE_DEC10//stage_cnt-=10;2://DowncountingloopexampleSTAGE_RST//setstage_cntto0STAGE_INC16//incrementstage_cntto16label:STAGE_DEC1//stage_cnt--;NOP//dosomethingJUMPSlabel,0,GT//jumptolabelifstage_cnt>0

HALT – End the program¶

Syntax

HALT

Operands

No operands

Cycles

2 cycles to execute

Description

The instruction halts the ULP coprocessor and restarts ULP wakeup timer, if it is enabled.

Examples:

1:HALT//Haltthecoprocessor

WAKE – Wake up the chip¶

Syntax

WAKE

Operands

No operands

Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction sends an interrupt from ULP to RTC controller.

  • If the SoC is in deep sleep mode, and ULP wakeup is enabled, this causes the SoC to wake up.

  • If the SoC is not in deep sleep mode, and ULP interrupt bit (RTC_CNTL_ULP_CP_INT_ENA) is set in RTC_CNTL_INT_ENA_REG register, RTC interrupt will be triggered.

Note that before using WAKE instruction, ULP program may needs to wait until RTC controller is ready to wake up the main CPU. This is indicated using RTC_CNTL_RDY_FOR_WAKEUP bit of RTC_CNTL_LOW_POWER_ST_REG register. If WAKE instruction is executed while RTC_CNTL_RDY_FOR_WAKEUP is zero, it has no effect (wake up does not occur).

Examples:

1:is_rdy_for_wakeup://ReadRTC_CNTL_RDY_FOR_WAKEUPbitREAD_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG,RTC_CNTL_RDY_FOR_WAKEUP)ANDr0,r0,1JUMPis_rdy_for_wakeup,eq//RetryuntilthebitissetWAKE//TriggerwakeupREG_WR0x006,24,24,0//StopULPtimer(clearRTC_CNTL_ULP_CP_SLP_TIMER_EN)HALT//StoptheULPprogram//Aftertheseinstructions,SoCwillwakeup,//andULPwillnotrunagainuntilstartedbythemainprogram.

SLEEP – set ULP wakeup timer period¶

Syntax

SLEEPsleep_reg

Operands
  • sleep_reg – 0..4, selects one of registers.

Cycles

2 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction selects which of the (x = 0..4) register values is to be used by the ULP wakeup timer as wakeup period. By default, the value from is used.

Examples:

1:SLEEP1//UseperiodsetinSENS_ULP_CP_SLEEP_CYC1_REG2:.setsleep_reg,4//SetconstantSLEEPsleep_reg//UseperiodsetinSENS_ULP_CP_SLEEP_CYC4_REG

WAIT – wait some number of cycles¶

Syntax

WAITCycles

Operands
  • Cycles – number of cycles for wait

Cycles

2 + Cycles cycles to execute, 4 cycles to fetch next instruction

Description

The instruction delays for given number of cycles.

Examples:

1:WAIT10//Donothingfor10cycles2:.setwait_cnt,10//SetaconstantWAITwait_cnt//waitfor10cycles

TSENS – do measurement with temperature sensor¶

Syntax
Operands
  • Rdst – Destination Register R[0..3], result will be stored to this register

  • Wait_Delay – number of cycles used to perform the measurement

Cycles

2 + Wait_Delay + 3 * TSENS_CLK to execute, 4 cycles to fetch next instruction

Description

The instruction performs measurement using TSENS and stores the result into a general purpose register.

Examples:

1:TSENSR1,1000//Measuretemperaturesensorfor1000cycles,//andstoreresulttoR1

ADC – do measurement with ADC¶

Syntax
  • ADCRdst, Sar_sel, Mux

  • ADCRdst, Sar_sel, Mux, 0 — deprecated form

Operands
  • Rdst – Destination Register R[0..3], result will be stored to this register

  • Sar_sel – Select ADC: 0 = SARADC1, 1 = SARADC2

  • Mux - Enable ADC channel. Channel number is [Mux-1]. If the user passes Mux value 1, then ADC channel 0 gets used.

Cycles

cycles to execute, 4 cycles to fetch next instruction

Description

The instruction makes measurements from ADC.

Examples:

1:ADCR1,0,1//MeasurevalueusingADC1channel0andstoreresultintoR1

I2C_RD - read single byte from I2C slave¶

Syntax
  • I2C_RDSub_addr, High, Low, Slave_sel

Operands
  • Sub_addr – Address within the I2C slave to read.

  • High, Low — Define range of bits to read. Bits outside of [High, Low] range are masked.

  • Slave_sel - Index of I2C slave address to use.

Cycles

Execution time mostly depends on I2C communication time. 4 cycles to fetch next instruction.

Description

instruction reads one byte from I2C slave with index . Slave address (in 7-bit format) has to be set in advance into SENS_I2C_SLAVE_ADDRx register field, where . 8 bits of read result is stored into R0 register.

Examples:

1:I2C_RD0x10,7,0,0//Readbytefromsub-address0x10ofslavewithaddresssetinSENS_I2C_SLAVE_ADDR0

I2C_WR - write single byte to I2C slave¶

Syntax
  • I2C_WRSub_addr, Value, High, Low, Slave_sel

Operands
  • Sub_addr – Address within the I2C slave to write.

  • Value – 8-bit value to be written.

  • High, Low — Define range of bits to write. Bits outside of [High, Low] range are masked.

  • Slave_sel - Index of I2C slave address to use.

Cycles

Execution time mostly depends on I2C communication time. 4 cycles to fetch next instruction.

Description

instruction writes one byte to I2C slave with index . Slave address (in 7-bit format) has to be set in advance into SENS_I2C_SLAVE_ADDRx register field, where .

Examples:

1:I2C_WR0x20,0x33,7,0,1//Writebyte0x33tosub-address0x20ofslavewithaddresssetinSENS_I2C_SLAVE_ADDR1.

REG_RD – read from peripheral register¶

Syntax

REG_RDAddr, High, Low

Operands
  • Addr – Register address, in 32-bit words

  • High – Register end bit number

  • Low – Register start bit number

Cycles

4 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction reads up to 16 bits from a peripheral register into a general purpose register: .

This instruction can access registers in RTC_CNTL, RTC_IO, SENS, and RTC_I2C peripherals. Address of the the register, as seen from the ULP, can be calculated from the address of the same register on the DPORT bus as follows:

addr_ulp=(addr_dport-DR_REG_RTCCNTL_BASE)/4

Examples:

1:REG_RD0x120,7,4//load4bits:R0={12'b0, REG[0x120][7:4]}

REG_WR – write to peripheral register¶

Syntax

REG_WRAddr, High, Low, Data

Operands
  • Addr – Register address, in 32-bit words.

  • High – Register end bit number

  • Low – Register start bit number

  • Data – Value to write, 8 bits

Cycles

8 cycles to execute, 4 cycles to fetch next instruction

Description

The instruction writes up to 8 bits from an immediate data value into a peripheral register: .

This instruction can access registers in RTC_CNTL, RTC_IO, SENS, and RTC_I2C peripherals. Address of the the register, as seen from the ULP, can be calculated from the address of the same register on the DPORT bus as follows:

addr_ulp=(addr_dport-DR_REG_RTCCNTL_BASE)/4

Examples:

1:REG_WR0x120,7,0,0x10//set8bits:REG[0x120][7:0]=0x10

Convenience macros for peripheral registers access¶

ULP source files are passed through C preprocessor before the assembler. This allows certain macros to be used to facilitate access to peripheral registers.

Some existing macros are defined in header file. These macros allow access to the fields of peripheral registers by their names. Peripheral registers names which can be used with these macros are the ones defined in , , , and .

READ_RTC_REG(rtc_reg, low_bit, bit_width)

Read up to 16 bits from rtc_reg[low_bit + bit_width - 1 : low_bit] into R0. For example:

#include "soc/soc_ulp.h"#include "soc/rtc_cntl_reg.h"/*Read16lowerbitsofRTC_CNTL_TIME0_REGintoR0*/READ_RTC_REG(RTC_CNTL_TIME0_REG,0,16)
READ_RTC_FIELD(rtc_reg, field)

Read from a field in rtc_reg into R0, up to 16 bits. For example:

#include "soc/soc_ulp.h"#include "soc/sens_reg.h"/*Read8-bitSENS_TSENS_OUTfieldofSENS_SAR_SLAVE_ADDR3_REGintoR0*/READ_RTC_FIELD(SENS_SAR_SLAVE_ADDR3_REG,SENS_TSENS_OUT)
WRITE_RTC_REG(rtc_reg, low_bit, bit_width, value)

Write immediate value into rtc_reg[low_bit + bit_width - 1 : low_bit], bit_width <= 8. For example:

#include "soc/soc_ulp.h"#include "soc/rtc_io_reg.h"/*SetBIT(2)ofRTC_GPIO_OUT_DATA_W1TSfieldinRTC_GPIO_OUT_W1TS_REG*/WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG,RTC_GPIO_OUT_DATA_W1TS_S+2,1,1)
WRITE_RTC_FIELD(rtc_reg, field, value)

Write immediate value into a field in rtc_reg, up to 8 bits. For example:

#include "soc/soc_ulp.h"#include "soc/rtc_cntl_reg.h"/*SetRTC_CNTL_ULP_CP_SLP_TIMER_ENfieldofRTC_CNTL_STATE0_REGto0*/WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG,RTC_CNTL_ULP_CP_SLP_TIMER_EN,0)

© Copyright 2016 - 2021, Espressif Systems (Shanghai) Co., Ltd

Sours: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/ulp_instruction_set.html
Coding a Sound Driver on the ESP32 ULP

HULP

HULP is a helper library for the ESP32's Ultra Low Power Co-Processor (ULP).


Features

Memory Access

Easily access variables to store and retrieve data, or share between the ULP and SoC

GPIO

Functions to configure GPIOs in preparation for the ULP

Countless macros to simplify ULP GPIO operations

As well as advanced functionality, including interrupts

Timing

Macros for basic delays

HULP also introduces a concept that makes use of the RTC slow clock for complex, asynchronous timing operations, useful for very power efficient operation and multitasking without the need for blocking delays. See example.

Peripherals

Helpers and drivers for internal peripherals as well as communication protocols, including:

  • Capacitive Touch

  • Hall Effect

  • Temperature

  • Hardware I2C

  • Software (bitbanged) I2C

  • APA RGB LED

  • UART

  • HX711

Debugging

  • Insert breakpoints, track and manipulate ULP program flow, inspect or alter register contents via the SoC

  • Combine the UART driver with included ULP PRINTF subroutines to communicate debugging information independent of the SoC (even in deep sleep!)

And much more...

Check out the examples for some programs demonstrating the possibilities of the ULP with HULP.

There's a lot more to HULP than what is mentioned above - in lieu of better documentation, please see relevant header files for more features and information.


Compatibility

HULP uses the C macro (legacy) programming method (https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/ulp_macros.html), however you are free to copy and convert any parts for use with the ULP binary toolchain.

ESP-IDF >=4.2.0 is required, and there is partial support for Arduino-ESP32 >=2.0.0.

Only ESP32 is currently supported.

Sours: https://github.com/boarchuz/HULP

You will also be interested:

.



1192 1193 1194 1195 1196