├── README.md ├── platform.local.txt ├── revisions.md └── src ├── esp32ulp_build_recipe.py ├── esp32ulp_mapgen.py ├── hash.json ├── include └── ulptool │ └── ulptool.h ├── ld └── esp32.ulp.ld ├── ulp_examples ├── ulp_README │ ├── ulp.s │ ├── ulp_README.ino │ └── ulp_main.h ├── ulp_adc │ ├── adc.s │ ├── ulp_adc.ino │ └── ulp_main.h ├── ulp_hall_sensor │ ├── hall_sensor.s │ ├── ulp_hall_sensor.ino │ └── ulp_main.h ├── ulp_i2c_bitbang │ ├── i2c.s │ ├── i2c_dev.s │ ├── stack.s │ ├── ulp_i2c_bitbang.ino │ └── ulp_main.h ├── ulp_rtc_gpio │ ├── rtcio.s │ ├── ulp_main.h │ └── ulp_rtc_gpio.ino ├── ulp_tsens │ ├── tsens.s │ ├── ulp_main.h │ └── ulp_tsens.ino ├── ulp_watering_device │ ├── adc.s │ ├── ulp_main.h │ └── ulp_watering_device.ino └── ulpcc │ ├── ulpcc_adc │ ├── adc.c │ ├── common.h │ ├── ulp_main.h │ └── ulpcc_adc.ino │ └── ulpcc_counter │ ├── ulp_counter.c │ ├── ulp_main.h │ └── ulpcc_counter.ino └── ulpcc ├── CPYRIGHT ├── bin ├── darwin │ ├── bprint │ ├── cpp │ ├── lburg │ ├── lcc │ └── rcc └── linux │ ├── bprint │ ├── cpp │ ├── lburg │ ├── lcc │ └── rcc └── include ├── soc_ulp_c.h └── ulp_c.h /README.md: -------------------------------------------------------------------------------- 1 | ulptool v2.4.2 2 | ================== 3 | 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. 4 | 5 | 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. 6 | 7 | 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 8 | Examples can be found in the ulp_examples/ulpcc folder. 9 | 10 | Note - platform.local.txt version does not follow ulptool version. 11 | 12 | Manual Setup Steps: 13 | ================== 14 | 1. Download the latest release of this repository and unpack-> https://github.com/duff2013/ulptool/releases/latest 15 | delete the release version number so the folder is just called 'ulptool' 16 | 17 | 2. Download and unpack the latest pre-compiled binutils-esp32ulp toolchain for Mac/Linux/Windows: https://github.com/espressif/binutils-esp32ulp/releases/latest 18 | 19 | 3. Find your Arduino-esp32 core directory which Arduino IDE uses: 20 | 21 | Typically (Mac OS) -> ~/Library/Arduino15/packages/esp32 22 | 23 | Typically (Windows) -> C:\Users\\AppData\Local\Arduino15\packages\esp32 24 | 25 | Typically (Linux) -> ~/.arduino15/packages/esp32 26 | 27 | 4. Move the **ulptool** folder you downloaded and unpacked to the tools folder here -> **... /esp32/tools/ulptool/**. 28 | 29 | 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. 30 | 31 | 6. In the **ulptool** folder, move or copy the **... /ulptool/src/ulp_examples** folder to where Arduino saves your sketches. 32 | 33 | 7. Move **esp32ulp-elf-binutils** folder you downloaded and unpacked to -> **... /esp32/tools/ulptool/src/esp32ulp-elf-binutils/**. 34 | 35 | That's it, you now have all the files in place, lets look at very simple example to get you compiling ulp assembly code! 36 | 37 | Assembly Example: 38 | ----------------- 39 | Open a blank Arduino sketch and copy and paste the code below into that sketch. 40 | ``` 41 | #include "esp32/ulp.h"// Must have this!!! 42 | 43 | // include ulp header you will create 44 | #include "ulp_main.h"// Must have this!!! 45 | 46 | // Custom binary loader 47 | #include "ulptool.h"// Must have this!!! 48 | 49 | // Unlike the esp-idf always use these binary blob names 50 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 51 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 52 | 53 | static void init_run_ulp(uint32_t usec); 54 | 55 | void setup() { 56 | Serial.begin(115200); 57 | delay(1000); 58 | init_run_ulp(100 * 1000); // 100 msec 59 | } 60 | 61 | void loop() { 62 | // ulp variables data is the lower 16 bits 63 | Serial.printf("ulp count: %u\n", ulp_count & 0xFFFF); 64 | delay(100); 65 | } 66 | 67 | static void init_run_ulp(uint32_t usec) { 68 | // initialize ulp variable 69 | ulp_count = 0; 70 | ulp_set_wakeup_period(0, usec); 71 | // use this binary loader instead 72 | esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 73 | // ulp coprocessor will run on its own now 74 | err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)); 75 | } 76 | ``` 77 | 78 | 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. 79 | ``` 80 | /* Define variables, which go into .bss section (zero-initialized data) */ 81 | .bss 82 | /* Store count value */ 83 | .global count 84 | count: 85 | .long 0 86 | 87 | /* Code goes into .text section */ 88 | .text 89 | .global entry 90 | entry: 91 | move r3, count 92 | ld r0, r3, 0 93 | add r0, r0, 1 94 | st r0, r3, 0 95 | halt 96 | ``` 97 | 98 | 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**. 99 | ``` 100 | /* 101 | Put your ULP globals here you want visibility 102 | for your sketch. Add "ulp_" to the beginning 103 | of the variable name and must be size 'uint32_t' 104 | */ 105 | #include "Arduino.h" 106 | 107 | extern uint32_t ulp_entry; 108 | extern uint32_t ulp_count; 109 | ``` 110 | 111 | Upload the code then open your serial monitor, you should see the variable 'ulp_count' increment every 100 msecs. 112 | 113 | ULPCC Example: 114 | --------------- 115 | 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:) 116 | ``` 117 | #include "esp32/ulp.h" 118 | // include ulp header you will create 119 | #include "ulp_main.h" 120 | // must include ulptool helper functions also 121 | #include "ulptool.h" 122 | 123 | // Unlike the esp-idf always use these binary blob names 124 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 125 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 126 | 127 | static void init_run_ulp(uint32_t usec); 128 | 129 | void setup() { 130 | Serial.begin(115200); 131 | delay(1000); 132 | init_run_ulp(100 * 1000); // 100 msec 133 | } 134 | 135 | void loop() { 136 | // ulp variables are 32-bit but only the bottom 16-bits hold data 137 | Serial.printf("Count: %i\n", ulp_counter & 0xFFFF); 138 | delay(100); 139 | } 140 | 141 | static void init_run_ulp(uint32_t usec) { 142 | ulp_set_wakeup_period(0, usec); 143 | esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 144 | err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)); 145 | 146 | if (err) Serial.println("Error Starting ULP Coprocessor"); 147 | } 148 | ``` 149 | 150 | Create a new tab named ulp_counter.c, take notice of the ```#ifdef _ULPCC_```, code between these will be compiled by the ulp c compiler. Do not place any code outside of the ```#ifdef _ULPCC_``` 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. 151 | ``` 152 | #ifdef _ULPCC_ // Do not add anything above this def 153 | // must include ulpcc helper functions 154 | #include 155 | 156 | // global variable that the main processor can see 157 | unsigned counter = 0; 158 | 159 | // all ulpcc programs have have this function 160 | void entry() { 161 | // increment counter 162 | counter++; 163 | } 164 | #endif // do not add anything after here 165 | ``` 166 | 167 | 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**. 168 | ``` 169 | /* 170 | Put your ULP globals here you want visibility 171 | for your sketch. Add "ulp_" to the beginning 172 | of the variable name and must be size 'uint32_t' 173 | */ 174 | #include "Arduino.h" 175 | // points to the entry function in counter.c. 176 | extern uint32_t ulp_entry; 177 | // pointer to counter in counter.c 178 | extern uint32_t ulp_counter; 179 | ``` 180 | 181 | Upload the code then open your serial monitor, you should see the variable 'ulp_counter' increment every 100 msecs. 182 | 183 | Under the Hood: 184 | =============== 185 | All the magic happens in the python script called esp32ulp_build_recipe.py along with espressif's esp32ulp_mapgen.py script. 186 | 187 | Limitations: 188 | ============ 189 | 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. 190 | 191 | 1. ulpcc is still experimental, Mac and Linux only so far. 192 | 2. Errors can be non-informative. 193 | 3. Have to use the custom binary loader function now. (ulptool_load_binary) 194 | -------------------------------------------------------------------------------- /platform.local.txt: -------------------------------------------------------------------------------- 1 | version=2.3.0 2 | 3 | ## paths 4 | compiler.ulp.path={runtime.tools.ulptool.path}/esp32ulp-elf-binutils/bin/ 5 | tools.ulptool.path={runtime.tools.ulptool.path}/ 6 | 7 | ## tool name 8 | tools.ulptool.cmd=esp32ulp_build_recipe.py 9 | 10 | ## ulp build tool 11 | compiler.s.cmd=python "{tools.ulptool.path}{tools.ulptool.cmd}" 12 | 13 | ## ulp_main.ld file address 14 | compiler.c.elf.extra_flags="-L{build.path}/sketch/" -T ulp_main.ld "{build.path}/sketch/ulp_main.bin.bin.o" 15 | 16 | ## add ulptool.h to g++ 17 | compiler.cpp.extra_flags="-I{tools.ulptool.path}/include/ulptool" 18 | 19 | ## compile '.s' (ulp) files 20 | recipe.hooks.core.postbuild.01.pattern={compiler.s.cmd} {compiler.cpreprocessor.flags} -b {build.path} -p {runtime.platform.path} -u {compiler.ulp.path} -x {compiler.path} -t {tools.ulptool.path} --DF_CPU={build.f_cpu} --DARDUINO={runtime.ide.version} --DARDUINO_={build.board} --DARDUINO_ARCH_={build.arch} --DARDUINO_BOARD="{build.board}" --DARDUINO_VARIANT="{build.variant}" 21 | 22 | ## run esp32.ld linker script through c preprocessor 23 | #recipe.hooks.core.postbuild.02.pattern="{compiler.path}{compiler.c.elf.cmd}" -E -P -C -xc -o "{compiler.sdk.path}/ld/esp32_out.ld" "-I{compiler.sdk.path}/include/config" "{compiler.sdk.path}/ld/esp32.ld" 24 | -------------------------------------------------------------------------------- /revisions.md: -------------------------------------------------------------------------------- 1 | >Updated (1/8/20 v2.4.2)
2 | * Fix readme.md ulpcc example.
3 | 4 | >Updated (8/20/19 v2.4.1)
5 | * Added lcc copyright file.
6 | * Fix grammar and spelling of README.md.
7 | * Fix lcc examples.
8 | 9 | >Updated (7/14/19 v2.4.0)
10 | * Added support for ulpcc c compiler :)
11 | * Cleaned up code.
12 | 13 | >Updated (2/15/19 v2.3.0)
14 | * Fixed flash memory allocation.
15 | * Add custom binary load function("ulptool_load_binary").
16 | * Use python to run the esp32.ld thru the c preprocessor creating an updated esp32_out.ld
17 | * Print ulp memory usage in stdout now.
18 | 19 | >Updated (2/8/19 v2.2.0)
20 | * Fixed compiling of non ulp projects.
21 | * Changed example file name from README.ino to ulp_README.ino.
22 | * All files versions numbers match the global version now.
23 | 24 | >Updated (2/5/19 v2.1.0)
25 | * Now compiles for archived cores. i.e esp32 cores v1.0.0 and v1.0.1
26 | * Changed install procedure, hopefully easier.
27 | * Now use platform.local.txt, no need for user to edit platform.txt anymore.
28 | * Cleaned up prints in Windows
29 | 30 | >Updated (1/8/19 v2.0.0)
31 | * Updated to use the esp32 core installed by the Arduino board manager now since its the preferred way to install now.
32 | 33 | >Updated (11/20/18 v1.8.2)
34 | Updated platform.txt to newest version.
35 | 36 | >Updated (9/8/18 v1.8.1)
37 | * Fix indents.
38 | 39 | >Updated (9/8/18 v1.8.0)
40 | * Now uses cpreprocessor flags from platform.txt instead of being hardcoded.
41 | * Uses argparse now.
42 | 43 | >Updated (8/17/18 v1.7.0)
44 | * Update platform.txt to arduino-esp32 v1.0.0 and ulp assembly compile
45 | * Add comment to increase ulp memory in examples
46 | 47 | >Updated (8/16/18 v1.6.1)
48 | * Added new example sketch from README.md.
49 | * Fixed binutils-esp32ulp download path.
50 | 51 | >Updated (4/14/18 v1.6.0)
52 | * Fixed Windows issue of needing python called explesivly in esp32ulp_build_recipe.py.
53 | * Tested and works with Linux Ubuntu VR on my Mac.
54 | * Tested and works with Windows 7 VR on my Mac.
55 | 56 | 57 | >Updated (4/11/18 v1.5.0)
58 | * Fixed a few OS dependent paths, hopefully windows and linux are fully supported now.
59 | * Added MIT license.
60 | 61 | >Updated (4/8/18 v1.4.0)
62 | * Now handles multiple assembly files.
63 | * update examples
64 | * update README.md
65 | 66 | >Updated (4/5/18 v1.3.0)
67 | * fixed a bunch of issues with esp32ulp_build_recipe.py
68 | * update README.md
69 | 70 | >Updated (4/1/18 v1.2.1)
71 | * cleaned up readme
72 | 73 | >Updated (3/28/18 v1.2.0)
74 | * python - should be platform independent now, windows/linux should work, needs testing
75 | * python - fixed security issue with popen shell cmd
76 | * python - close open files correctly before exiting script
77 | * python - handle stderr better
78 | * check spelling
79 | 80 | >Updated (3/28/18 v1.1.0)
81 | * python - On the road to a platform independent script using os.path in esp32ulp_build_recipe.py
82 | * Update readme
83 | 84 | >Updated (3/27/18 v1.0.1)
85 | * python - Update python script, no structural changes just added comments
86 | * Update readme
87 | 88 | >Updated (3/26/18 v1.0.0)
89 | * Initial commit
90 | -------------------------------------------------------------------------------- /src/esp32ulp_build_recipe.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018 Colin Duffy (https://github.com/duff2013) 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 4 | # software and associated documentation files (the "Software"), to deal in the Software 5 | # without restriction, including without limitation the rights to use, copy, modify, merge, 6 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 7 | # to whom the Software is furnished to do so, subject to the following conditions: 8 | # 9 | # The above copyright notice and this permission notice shall be included in all copies or 10 | # substantial portions of the Software. 11 | # 12 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 13 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 15 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 16 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 17 | # DEALINGS IN THE SOFTWARE. 18 | 19 | # version 2.4.1 20 | import os 21 | import re 22 | import sys 23 | import glob 24 | import json 25 | import hashlib 26 | import platform 27 | import argparse 28 | import subprocess 29 | 30 | CPREPROCESSOR_FLAGS = [] 31 | 32 | EXTRA_FLAGS = dict() 33 | EXTRA_FLAGS['doitESP32devkitV1'] = os.path.join('variants','doitESP32devkitV1') 34 | EXTRA_FLAGS['esp32'] = os.path.join('cores','esp32') 35 | EXTRA_FLAGS['E'] = '-E' 36 | EXTRA_FLAGS['P'] = '-P' 37 | EXTRA_FLAGS['XC'] = '-xc' 38 | EXTRA_FLAGS['O'] = '-o' 39 | EXTRA_FLAGS['O+'] = '-O' 40 | EXTRA_FLAGS['I'] = '-I' 41 | EXTRA_FLAGS['C'] = '-C' 42 | EXTRA_FLAGS['A'] = '-A' 43 | EXTRA_FLAGS['T'] = '-T' 44 | EXTRA_FLAGS['G'] = '-g' 45 | EXTRA_FLAGS['F'] = '-f' 46 | EXTRA_FLAGS['S'] = '-s' 47 | EXTRA_FLAGS['BINARY'] = 'binary' 48 | EXTRA_FLAGS['D__ASSEMBLER__'] = '-D__ASSEMBLER__' 49 | EXTRA_FLAGS['DESP_PLATFORM'] = '-DESP_PLATFORM' 50 | EXTRA_FLAGS['DMBEDTLS_CONFIG_FILE'] = os.path.join('-DMBEDTLS_CONFIG_FILE=mbedtls','esp_config.h') 51 | EXTRA_FLAGS['DHAVE_CONFIG_H'] = '-DHAVE_CONFIG_H' 52 | EXTRA_FLAGS['MT'] = '-MT' 53 | EXTRA_FLAGS['MMD'] = '-MMD' 54 | EXTRA_FLAGS['MP'] = '-MP' 55 | EXTRA_FLAGS['DWITH_POSIX'] = '-DWITH_POSIX' 56 | EXTRA_FLAGS['INPUT_TARGET'] = '--input-target' 57 | EXTRA_FLAGS['OUTPUT_TARGET'] = '--output-target' 58 | EXTRA_FLAGS['ELF32_XTENSA_LE'] = 'elf32-xtensa-le' 59 | EXTRA_FLAGS['BINARY_ARCH'] = '--binary-architecture' 60 | EXTRA_FLAGS['XTENSA'] = 'xtensa' 61 | EXTRA_FLAGS['RENAME_SECTION'] = '--rename-section' 62 | EXTRA_FLAGS['EMBEDDED'] = '.data=.rodata.embedded' 63 | EXTRA_FLAGS['CRU'] = 'cru' 64 | EXTRA_FLAGS['ELF32'] = 'elf32-esp32ulp' 65 | EXTRA_FLAGS['POSIX'] = 'posix' 66 | 67 | def main(argv): 68 | parser = argparse.ArgumentParser() 69 | parser.add_argument('-b', action='store') 70 | parser.add_argument('-p', action='store') 71 | parser.add_argument('-u', action='store') 72 | parser.add_argument('-x', action='store') 73 | parser.add_argument('-t', action='store') 74 | parser.add_argument('-I', action='append') 75 | args, options = parser.parse_known_args() 76 | 77 | for item in args.I: 78 | CPREPROCESSOR_FLAGS.append('-I') 79 | CPREPROCESSOR_FLAGS.append(item) 80 | 81 | board_options = [] 82 | for item in options: 83 | if item.startswith('--'): 84 | board_options.append(item[1:]) 85 | 86 | PATHS = dict() 87 | PATHS['build'] = args.b 88 | PATHS['core'] = args.p 89 | PATHS['ulptool'] = args.t 90 | PATHS['ucompiler'] = args.u 91 | PATHS['xcompiler'] = args.x 92 | 93 | os.chdir(os.path.join(PATHS['build'], 'sketch')) 94 | 95 | gen_assembly(PATHS) 96 | 97 | ulp_files = glob.glob('*.s') 98 | 99 | if not ulp_files: 100 | sys.stdout.write('No ULP Assembly File(s) Detected...\r') 101 | with open('tmp.s',"w") as ulp: pass 102 | ulp_files.append('tmp.s') 103 | build_ulp(PATHS, ulp_files, board_options, False) 104 | os.remove('tmp.s') 105 | else: 106 | build_ulp(PATHS, ulp_files, board_options, True) 107 | sys.exit(0) 108 | 109 | 110 | def build_ulp(PATHS, ulp_sfiles, board_options, has_s_file): 111 | console_string = '' 112 | if has_s_file: 113 | console_string = 'ULP Assembly File(s) Detected: ' + ', '.join(ulp_sfiles) + '\r' 114 | 115 | cmds = gen_cmds(os.path.join(PATHS['core'], 'tools')) 116 | 117 | flash_msg = '' 118 | ram_msg = '' 119 | 120 | 121 | for file in ulp_sfiles: 122 | file = file.split('.') 123 | file_names = gen_file_names(file[0]) 124 | 125 | ## Run each assembly file (foo.S) through C preprocessor 126 | cmd = gen_xtensa_preprocessor_cmd(PATHS, file, board_options) 127 | proc = subprocess.Popen(cmd[1],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=False) 128 | (out, err) = proc.communicate() 129 | if err: 130 | error_string = cmd[0] + '\r' + err 131 | sys.exit(error_string) 132 | else: 133 | console_string += cmd[0] + '\r' 134 | 135 | ## Run preprocessed assembly sources through assembler 136 | cmd = gen_binutils_as_cmd(PATHS, file) 137 | proc = subprocess.Popen(cmd[1],stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=False) 138 | (out, err) = proc.communicate() 139 | if err: 140 | error_string = cmd[0] + '\r' + err 141 | sys.exit(error_string) 142 | else: 143 | console_string += cmd[0] + '\r' 144 | 145 | ## Run linker script template through C preprocessor 146 | cmd = gen_xtensa_ld_cmd(PATHS, ulp_sfiles, board_options) 147 | proc = subprocess.Popen(cmd[1],stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=False) 148 | (out, err) = proc.communicate() 149 | if err: 150 | error_string = cmd[0] + '\r' + err 151 | sys.exit(error_string) 152 | else: 153 | console_string += cmd[0] + '\r' 154 | 155 | ## Link object files into an output ELF file 156 | cmd = gen_binutils_ld_cmd(PATHS, ulp_sfiles) 157 | proc = subprocess.Popen(cmd[1],stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=False) 158 | (out, err) = proc.communicate() 159 | if err: 160 | error_string = cmd[0] + '\r' + err 161 | sys.exit(error_string) 162 | else: 163 | console_string += cmd[0] + '\r' 164 | 165 | ## Get section memory sizes 166 | cmd = gen_binutils_size_cmd(PATHS) 167 | proc = subprocess.Popen(cmd[1],stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=False) 168 | (out, err) = proc.communicate() 169 | if err: 170 | error_string = cmd[0] + '\r' + err 171 | sys.exit(error_string) 172 | else: 173 | try: 174 | file_path = os.path.join(PATHS['core'], 'tools', 'sdk', 'include', 'config', 'sdkconfig.h' ) 175 | with open(file_path, "r") as file: text = file.read() 176 | 177 | mem = re.findall(r'#define CONFIG_ULP_COPROC_RESERVE_MEM (.*?)\n', text)[0] 178 | SECTIONS = dict(re.findall('^(\.+[0-9a-zA-Z_]+)\s+([0-9]+)', out, re.MULTILINE)) 179 | max = 0.0 180 | text = 0.0 181 | data = 0.0 182 | bss = 0.0 183 | header = 0.0 184 | total = 0.0 185 | 186 | flash_precent = 0.0 187 | ram_precent = 0.0 188 | 189 | try: max = float(mem) 190 | except Exception: pass 191 | 192 | try: text = float(SECTIONS['.text']) 193 | except Exception: pass 194 | 195 | try: data = float(SECTIONS['.data']) 196 | except Exception: pass 197 | 198 | try: bss = float(SECTIONS['.bss']) 199 | except Exception: pass 200 | 201 | try: header = float(SECTIONS['.header']) 202 | except Exception: pass 203 | 204 | try: 205 | flash_precent = text/(max - data - bss - header) * 100 206 | except Exception: 207 | flash_precent = text/((max + 1) - data - bss - header) * 100 208 | 209 | try: 210 | ram_precent = (data + bss)/(max - text - header) * 100 211 | except Exception: 212 | ram_precent = (data + bss)/((max + 1) - text - header) * 100 213 | 214 | total = text + data + bss + header 215 | ram_left = max - total 216 | 217 | flash_msg = "ulp uses %s bytes (%s%%) of program storage space. Maximum is %s bytes.\r" % (int(text), int(flash_precent), int(max - header)) 218 | ram_msg = 'Global variables use %s bytes (%s%%) of dynamic memory, leaving %s bytes for local variables. Maximum is %s bytes.\r' % (int(data+bss), int(ram_precent), int(ram_left), int(max - header)) 219 | except Exception as e: 220 | pass 221 | 222 | console_string += cmd[0] + '\r' 223 | 224 | ## Generate list of global symbols 225 | cmd = gen_binutils_nm_cmd(PATHS) 226 | proc = subprocess.Popen(cmd[1],stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=False) 227 | (out, err) = proc.communicate() 228 | if err: 229 | error_string = cmd[0] + '\r' + err 230 | sys.exit(error_string) 231 | else: 232 | file_names_constant = gen_file_names_constant() 233 | with open(file_names_constant['sym'],"w") as fsym: 234 | fsym.write(out.decode('utf-8')) 235 | console_string += cmd[0] + '\r' 236 | 237 | ## Create LD export script and header file 238 | cmd = gen_mapgen_cmd(PATHS) 239 | proc = subprocess.Popen(cmd[1],stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=False) 240 | (out, err) = proc.communicate() 241 | if err: 242 | error_string = cmd[0] + '\r' + err 243 | sys.exit(error_string) 244 | else: 245 | console_string += cmd[0] + '\r' 246 | 247 | ## Add the generated binary to the list of binary files 248 | cmd = gen_binutils_objcopy_cmd(PATHS) 249 | proc = subprocess.Popen(cmd[1],stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=False) 250 | (out, err) = proc.communicate() 251 | if err: 252 | error_string = cmd[0] + '\r' + err 253 | sys.exit(error_string) 254 | else: 255 | console_string += cmd[0] + '\r' 256 | 257 | ## Add the generated binary to the list of binary files 258 | cmd = gen_xtensa_objcopy_cmd(PATHS) 259 | proc = subprocess.Popen(cmd[1],stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=False) 260 | (out, err) = proc.communicate() 261 | if err: 262 | error_string = cmd[0] + '\r' + err 263 | sys.exit(error_string) 264 | else: 265 | console_string += cmd[0] + '\r' 266 | 267 | ## Check if sdkconfig.h md5 hash has changed indicating the file has changed 268 | sdk_hash = md5(os.path.join(PATHS['core'] , 'tools', 'sdk', 'include', 'config', 'sdkconfig.h')) 269 | dict_hash = dict() 270 | with open(os.path.join(PATHS['ulptool'], 'hash.json'), 'r') as file: 271 | dict_hash = json.load(file) 272 | if sdk_hash != dict_hash['sdkconfig.h']['hash']: 273 | with open(os.path.join(PATHS['ulptool'], 'hash.json'), 'w') as file: 274 | dict_hash['sdkconfig.h']['hash'] = sdk_hash 275 | file.write(json.dumps(dict_hash)) 276 | ## Run esp32.ld thru the c preprocessor generating esp32_out.ld 277 | cmd = gen_xtensa_ld_preprocessor_cmd(PATHS) 278 | proc = subprocess.Popen(cmd[1],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=False) 279 | (out, err) = proc.communicate() 280 | if err: 281 | error_string = cmd[0] + '\r' + err 282 | sys.exit(error_string) 283 | else: 284 | console_string += cmd[0] + '\r' 285 | 286 | ## print outputs or errors to the console 287 | candy = '*********************************************************************************\r' 288 | if has_s_file: 289 | print(console_string + candy + flash_msg + ram_msg + candy) 290 | return 0 291 | 292 | def gen_assembly(PATHS): 293 | c_files = glob.glob('*.c') 294 | ulpcc_files = [] 295 | try: 296 | for file in c_files: 297 | with open(file, "rb") as f: 298 | top = f.readline().strip() 299 | bottom = f.readlines()[-1].strip() 300 | if top.startswith("#ifdef _ULPCC_"): 301 | if bottom.startswith("#endif"): 302 | ulpcc_files.append(file) 303 | 304 | except Exception as e: 305 | print(e) 306 | 307 | for file in ulpcc_files: 308 | cmd = gen_lcc_cmd(PATHS, file) 309 | proc = subprocess.Popen(cmd[1],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=False) 310 | (out, err) = proc.communicate() 311 | if err: 312 | error_string = cmd[0] + '\r' + err 313 | sys.exit(error_string) 314 | else: 315 | if out == "": 316 | print(cmd[0]) 317 | else: 318 | sys.exit(str(out)) 319 | 320 | 321 | def gen_lcc_cmd(PATHS, file): 322 | soc_path = os.path.join(PATHS['core'], 'tools', 'sdk', 'include', 'soc', 'soc') 323 | include_path = os.path.join(PATHS['core'], 'tools', 'sdk', 'include', 'soc') 324 | header_path = os.path.join(PATHS['ulptool'], 'ulpcc', 'include') 325 | if platform.system() == 'Darwin': 326 | lcc_path = os.path.join(PATHS['ulptool'], 'ulpcc', 'bin', 'darwin') 327 | elif platform.system() == 'Linux': 328 | lcc_path = os.path.join(PATHS['ulptool'], 'ulpcc', 'bin', 'linux') 329 | elif platform.system() == 'Windows': 330 | sys.exit("ulpcc is not supported on Windows") 331 | LCC = [] 332 | LCC.append(lcc_path + '/lcc') 333 | LCC.append('-I' + soc_path) 334 | LCC.append('-I' + include_path) 335 | LCC.append('-I' + header_path) 336 | LCC.append('-D_ULPCC_') 337 | LCC.append('-lccdir=' + lcc_path) 338 | LCC.append('-Wf-target=ulp') 339 | LCC.append('-S') 340 | LCC.append(file) 341 | LCC.append("-o") 342 | LCC.append(file[:-1] + 's') 343 | STR_CMD = ' '.join(LCC) 344 | return STR_CMD, LCC 345 | 346 | def gen_xtensa_ld_preprocessor_cmd(PATHS): 347 | cmds = gen_xtensa_cmds(PATHS['xcompiler']) 348 | XTENSA_GCC_PREPROCESSOR = [] 349 | XTENSA_GCC_PREPROCESSOR.append(cmds['XTENSA_GCC']) 350 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['E']) 351 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['P']) 352 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['C']) 353 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['XC']) 354 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['O']) 355 | XTENSA_GCC_PREPROCESSOR.append(os.path.join(PATHS['core'] , 'tools', 'sdk', 'ld', 'esp32_out.ld')) 356 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['I']) 357 | XTENSA_GCC_PREPROCESSOR.append(os.path.join(PATHS['core'] , 'tools', 'sdk', 'include', 'config')) 358 | XTENSA_GCC_PREPROCESSOR.append(os.path.join(PATHS['core'] , 'tools', 'sdk', 'ld', 'esp32.ld')) 359 | STR_CMD = ' '.join(XTENSA_GCC_PREPROCESSOR) 360 | return STR_CMD, XTENSA_GCC_PREPROCESSOR 361 | 362 | def gen_xtensa_preprocessor_cmd(PATHS, file, board_options): 363 | cmds = gen_xtensa_cmds(PATHS['xcompiler']) 364 | file_names = gen_file_names(file[0]) 365 | XTENSA_GCC_PREPROCESSOR = [] 366 | XTENSA_GCC_PREPROCESSOR.append(cmds['XTENSA_GCC']) 367 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['DESP_PLATFORM']) 368 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['MMD']) 369 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['MP']) 370 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['DWITH_POSIX']) 371 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['DMBEDTLS_CONFIG_FILE']) 372 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['DHAVE_CONFIG_H']) 373 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['MT']) 374 | XTENSA_GCC_PREPROCESSOR.append(file_names['o']) 375 | #XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['C']) 376 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['E']) 377 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['P']) 378 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['XC']) 379 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['O']) 380 | XTENSA_GCC_PREPROCESSOR.append(file_names['ps']) 381 | XTENSA_GCC_PREPROCESSOR.extend(CPREPROCESSOR_FLAGS) 382 | XTENSA_GCC_PREPROCESSOR.extend(board_options) 383 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['I']) 384 | XTENSA_GCC_PREPROCESSOR.append(os.path.join(PATHS['build'], 'sketch')) 385 | XTENSA_GCC_PREPROCESSOR.append(EXTRA_FLAGS['D__ASSEMBLER__']) 386 | XTENSA_GCC_PREPROCESSOR.append(file[0] + '.s') 387 | STR_CMD = ' '.join(XTENSA_GCC_PREPROCESSOR) 388 | return STR_CMD, XTENSA_GCC_PREPROCESSOR 389 | 390 | def gen_binutils_as_cmd(PATHS, file): 391 | cmds = gen_binutils_cmds(PATHS['ucompiler']) 392 | file_names = gen_file_names(file[0]) 393 | ULP_AS = [] 394 | ULP_AS.append(cmds['ULP_AS']) 395 | ULP_AS.append('-al=' + file_names['lst']) 396 | ULP_AS.append('-W') 397 | ULP_AS.append(EXTRA_FLAGS['O']) 398 | ULP_AS.append(file_names['o']) 399 | ULP_AS.append(file_names['ps']) 400 | STR_CMD = ' '.join(ULP_AS) 401 | return STR_CMD, ULP_AS 402 | 403 | def gen_xtensa_ld_cmd(PATHS, file, board_options): 404 | cmds = gen_xtensa_cmds(PATHS['xcompiler']) 405 | file_names = gen_file_names_constant() 406 | XTENSA_GCC_LD = [] 407 | XTENSA_GCC_LD.append(cmds['XTENSA_GCC']) 408 | XTENSA_GCC_LD.append(EXTRA_FLAGS['DESP_PLATFORM']) 409 | XTENSA_GCC_LD.append(EXTRA_FLAGS['MMD']) 410 | XTENSA_GCC_LD.append(EXTRA_FLAGS['MP']) 411 | XTENSA_GCC_LD.append(EXTRA_FLAGS['DWITH_POSIX']) 412 | XTENSA_GCC_LD.append(EXTRA_FLAGS['DMBEDTLS_CONFIG_FILE']) 413 | XTENSA_GCC_LD.append(EXTRA_FLAGS['DHAVE_CONFIG_H']) 414 | XTENSA_GCC_LD.append(EXTRA_FLAGS['MT']) 415 | XTENSA_GCC_LD.append(file_names['ld']) 416 | XTENSA_GCC_LD.append(EXTRA_FLAGS['E']) 417 | XTENSA_GCC_LD.append(EXTRA_FLAGS['P']) 418 | XTENSA_GCC_LD.append(EXTRA_FLAGS['XC']) 419 | XTENSA_GCC_LD.append(EXTRA_FLAGS['O']) 420 | XTENSA_GCC_LD.append(file_names['ld']) 421 | XTENSA_GCC_LD.extend(CPREPROCESSOR_FLAGS) 422 | XTENSA_GCC_LD.extend(board_options) 423 | XTENSA_GCC_LD.append(EXTRA_FLAGS['I']) 424 | XTENSA_GCC_LD.append(os.path.join(PATHS['build'], 'sketch')) 425 | XTENSA_GCC_LD.append(EXTRA_FLAGS['D__ASSEMBLER__']) 426 | XTENSA_GCC_LD.append(os.path.join(PATHS['ulptool'], 'ld', 'esp32.ulp.ld')) 427 | STR_CMD = ' '.join(XTENSA_GCC_LD) 428 | return STR_CMD, XTENSA_GCC_LD 429 | 430 | def gen_binutils_ld_cmd(PATHS, file): 431 | cmds = gen_binutils_cmds(PATHS['ucompiler']) 432 | file_names_constant = gen_file_names_constant() 433 | ULP_LD = [] 434 | ULP_LD.append(cmds['ULP_LD']) 435 | ULP_LD.append(EXTRA_FLAGS['O']) 436 | ULP_LD.append(file_names_constant['elf']) 437 | ULP_LD.append(EXTRA_FLAGS['A']) 438 | ULP_LD.append(EXTRA_FLAGS['ELF32']) 439 | ULP_LD.append('-Map=' + file_names_constant['map']) 440 | ULP_LD.append(EXTRA_FLAGS['T']) 441 | ULP_LD.append(file_names_constant['ld']) 442 | for f in file: 443 | f = f.split('.') 444 | file_names = gen_file_names(f[0]) 445 | ULP_LD.append(file_names['o']) 446 | STR_CMD = ' '.join(ULP_LD) 447 | return STR_CMD, ULP_LD 448 | 449 | def gen_binutils_size_cmd(PATHS): 450 | cmds = gen_binutils_cmds(PATHS['ucompiler']) 451 | file_names_constant = gen_file_names_constant() 452 | ULP_LD = [] 453 | ULP_LD.append(cmds['ULP_SIZE']) 454 | ULP_LD.append(EXTRA_FLAGS['A']) 455 | ULP_LD.append(file_names_constant['elf']) 456 | STR_CMD = ' '.join(ULP_LD) 457 | return STR_CMD, ULP_LD 458 | 459 | def gen_binutils_nm_cmd(PATHS): 460 | cmds = gen_binutils_cmds(PATHS['ucompiler']) 461 | file_names_constant = gen_file_names_constant() 462 | ULP_NM = [] 463 | ULP_NM.append(cmds['ULP_NM']) 464 | ULP_NM.append(EXTRA_FLAGS['G']) 465 | ULP_NM.append(EXTRA_FLAGS['F']) 466 | ULP_NM.append(EXTRA_FLAGS['POSIX']) 467 | ULP_NM.append(file_names_constant['elf']) 468 | STR_CMD = ' '.join(ULP_NM) 469 | return STR_CMD, ULP_NM 470 | 471 | def gen_mapgen_cmd(PATHS): 472 | cmds = gen_cmds(PATHS['ulptool']) 473 | file_names_constant = gen_file_names_constant() 474 | ULP_MAPGEN = [] 475 | ULP_MAPGEN.append('python') 476 | ULP_MAPGEN.append(cmds['ULP_MAPGEN']) 477 | ULP_MAPGEN.append(EXTRA_FLAGS['S']) 478 | ULP_MAPGEN.append(file_names_constant['sym']) 479 | ULP_MAPGEN.append(EXTRA_FLAGS['O']) 480 | ULP_MAPGEN.append('ulp_main') 481 | STR_CMD = ' '.join(ULP_MAPGEN) 482 | return STR_CMD, ULP_MAPGEN 483 | 484 | def gen_binutils_objcopy_cmd(PATHS): 485 | cmds = gen_binutils_cmds(PATHS['ucompiler']) 486 | file_names_constant = gen_file_names_constant() 487 | ULP_OBJCOPY = [] 488 | ULP_OBJCOPY.append(cmds['ULP_OBJCPY']) 489 | ULP_OBJCOPY.append(EXTRA_FLAGS['O+']) 490 | ULP_OBJCOPY.append(EXTRA_FLAGS['BINARY']) 491 | ULP_OBJCOPY.append(file_names_constant['elf']) 492 | ULP_OBJCOPY.append(file_names_constant['bin']) 493 | STR_CMD = ' '.join(ULP_OBJCOPY) 494 | return STR_CMD, ULP_OBJCOPY 495 | 496 | def gen_xtensa_objcopy_cmd(PATHS): 497 | cmds = gen_xtensa_cmds(PATHS['xcompiler']) 498 | file_names_constant = gen_file_names_constant() 499 | XTENSA_OBJCOPY = [] 500 | XTENSA_OBJCOPY.append(cmds['XTENSA_OBJCPY']) 501 | XTENSA_OBJCOPY.append(EXTRA_FLAGS['INPUT_TARGET']) 502 | XTENSA_OBJCOPY.append(EXTRA_FLAGS['BINARY']) 503 | XTENSA_OBJCOPY.append(EXTRA_FLAGS['OUTPUT_TARGET'] ) 504 | XTENSA_OBJCOPY.append(EXTRA_FLAGS['ELF32_XTENSA_LE']) 505 | XTENSA_OBJCOPY.append(EXTRA_FLAGS['BINARY_ARCH']) 506 | XTENSA_OBJCOPY.append(EXTRA_FLAGS['XTENSA'] ) 507 | XTENSA_OBJCOPY.append(EXTRA_FLAGS['RENAME_SECTION']) 508 | XTENSA_OBJCOPY.append(EXTRA_FLAGS['EMBEDDED']) 509 | XTENSA_OBJCOPY.append(file_names_constant['bin']) 510 | XTENSA_OBJCOPY.append(file_names_constant['bin_o']) 511 | STR_CMD = ' '.join(XTENSA_OBJCOPY) 512 | return STR_CMD, XTENSA_OBJCOPY 513 | 514 | def gen_file_names(sfile): 515 | file_names = dict() 516 | file_names['o'] = sfile + '.ulp.o' 517 | file_names['ps'] = sfile + '.ulp.pS' 518 | file_names['lst'] = sfile + '.ulp.lst' 519 | return file_names 520 | 521 | def gen_file_names_constant(): 522 | file_names = dict() 523 | file_names['ld'] = 'ulp_main.common.ld' 524 | file_names['elf'] = 'ulp_main.elf' 525 | file_names['map'] = 'ulp_main.map' 526 | file_names['sym'] = 'ulp_main.sym' 527 | file_names['bin'] = 'ulp_main.bin' 528 | file_names['bin_o'] = 'ulp_main.bin.bin.o' 529 | return file_names 530 | 531 | def gen_cmds(path): 532 | cmds = dict() 533 | cmds['ULP_MAPGEN'] = os.path.join(path, 'esp32ulp_mapgen.py') 534 | return cmds 535 | 536 | def gen_xtensa_cmds(path): 537 | cmds = dict() 538 | cmds['XTENSA_GCC'] = os.path.join(path, 'xtensa-esp32-elf-gcc') 539 | cmds['XTENSA_OBJCPY'] = os.path.join(path, 'xtensa-esp32-elf-objcopy') 540 | cmds['XTENSA_AR'] = os.path.join(path, 'xtensa-esp32-elf-ar') 541 | return cmds 542 | 543 | def gen_binutils_cmds(path): 544 | cmds = dict() 545 | cmds['ULP_AS'] = os.path.join(path, 'esp32ulp-elf-as') 546 | cmds['ULP_LD'] = os.path.join(path, 'esp32ulp-elf-ld') 547 | cmds['ULP_NM'] = os.path.join(path, 'esp32ulp-elf-nm') 548 | cmds['ULP_SIZE'] = os.path.join(path, 'esp32ulp-elf-size') 549 | cmds['ULP_OBJCPY'] = os.path.join(path, 'esp32ulp-elf-objcopy') 550 | return cmds 551 | 552 | def md5(fname): 553 | hash_md5 = hashlib.md5() 554 | with open(fname, "rb") as f: 555 | for chunk in iter(lambda: f.read(4096), b""): 556 | hash_md5.update(chunk) 557 | return hash_md5.hexdigest() 558 | 559 | if __name__ == '__main__': 560 | main(sys.argv[1:]) 561 | -------------------------------------------------------------------------------- /src/esp32ulp_mapgen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # esp32ulp_mapgen utility converts a symbol list provided by nm into an export script 3 | # for the linker and a header file. 4 | # 5 | # Copyright (c) 2016-2017 Espressif Systems (Shanghai) PTE LTD. 6 | # Distributed under the terms of Apache License v2.0 found in the top-level LICENSE file. 7 | 8 | from optparse import OptionParser 9 | 10 | BASE_ADDR = 0x50000000 11 | 12 | 13 | def gen_ld_h_from_sym(f_sym, f_ld, f_h): 14 | f_ld.write("/* Variable definitions for ESP32ULP linker\n") 15 | f_ld.write(" * This file is generated automatically by esp32ulp_mapgen.py utility.\n") 16 | f_ld.write(" */\n\n") 17 | f_h.write("// Variable definitions for ESP32ULP\n") 18 | f_h.write("// This file is generated automatically by esp32ulp_mapgen.py utility\n\n") 19 | f_h.write("#pragma once\n\n") 20 | 21 | for line in f_sym: 22 | name, _, addr_str = line.split() 23 | addr = int(addr_str, 16) + BASE_ADDR 24 | f_h.write("extern uint32_t ulp_{0};\n".format(name)) 25 | f_ld.write("PROVIDE ( ulp_{0} = 0x{1:08x} );\n".format(name, addr)) 26 | 27 | 28 | def main(): 29 | description = ("This application generates .h and .ld files for symbols defined in input file. " 30 | "The input symbols file can be generated using nm utility like this: " 31 | "esp32-ulp-nm -g -f posix > ") 32 | 33 | parser = OptionParser(description=description) 34 | parser.add_option("-s", "--symfile", dest="symfile", 35 | help="symbols file name", metavar="SYMFILE") 36 | parser.add_option("-o", "--outputfile", dest="outputfile", 37 | help="destination .h and .ld files name prefix", metavar="OUTFILE") 38 | 39 | (options, args) = parser.parse_args() 40 | if options.symfile is None: 41 | parser.print_help() 42 | return 1 43 | 44 | if options.outputfile is None: 45 | parser.print_help() 46 | return 1 47 | 48 | with open(options.outputfile + ".h", 'w') as f_h, open(options.outputfile + ".ld", 'w') as f_ld, open(options.symfile) as f_sym: 49 | gen_ld_h_from_sym(f_sym, f_ld, f_h) 50 | return 0 51 | 52 | 53 | if __name__ == "__main__": 54 | exit(main()) 55 | -------------------------------------------------------------------------------- /src/hash.json: -------------------------------------------------------------------------------- 1 | {"sdkconfig.h": {"hash": ""}} 2 | -------------------------------------------------------------------------------- /src/include/ulptool/ulptool.h: -------------------------------------------------------------------------------- 1 | /* 2 | fix for when ulp memory in sdkconfig.h is edited 3 | */ 4 | #include "Arduino.h" 5 | #include "sdkconfig.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | typedef struct { 12 | uint32_t magic; 13 | uint16_t text_offset; 14 | uint16_t text_size; 15 | uint16_t data_size; 16 | uint16_t bss_size; 17 | } ulp_binary_header_t; 18 | 19 | #define ULP_BINARY_MAGIC_ESP32 (0x00706c75) 20 | 21 | esp_err_t ulptool_load_binary(uint32_t load_addr, const uint8_t* program_binary, size_t program_size) { 22 | size_t program_size_bytes = program_size * sizeof(uint32_t); 23 | size_t load_addr_bytes = load_addr * sizeof(uint32_t); 24 | 25 | if (program_size_bytes < sizeof(ulp_binary_header_t)) { 26 | return ESP_ERR_INVALID_SIZE; 27 | } 28 | 29 | if (load_addr_bytes > CONFIG_ULP_COPROC_RESERVE_MEM) { 30 | return ESP_ERR_INVALID_ARG; 31 | } 32 | 33 | if (load_addr_bytes + program_size_bytes > CONFIG_ULP_COPROC_RESERVE_MEM) { 34 | return ESP_ERR_INVALID_SIZE; 35 | } 36 | 37 | // Make a copy of a header in case program_binary isn't aligned 38 | ulp_binary_header_t header; 39 | memcpy(&header, program_binary, sizeof(header)); 40 | 41 | if (header.magic != ULP_BINARY_MAGIC_ESP32) { 42 | return ESP_ERR_NOT_SUPPORTED; 43 | } 44 | 45 | size_t total_size = (size_t) header.text_offset + (size_t) header.text_size + 46 | (size_t) header.data_size; 47 | 48 | ESP_LOGD(TAG, "program_size_bytes: %d total_size: %d offset: %d .text: %d, .data: %d, .bss: %d", 49 | program_size_bytes, total_size, header.text_offset, 50 | header.text_size, header.data_size, header.bss_size); 51 | 52 | if (total_size != program_size_bytes) { 53 | return ESP_ERR_INVALID_SIZE; 54 | } 55 | 56 | size_t text_data_size = header.text_size + header.data_size; 57 | uint8_t* base = (uint8_t*) RTC_SLOW_MEM; 58 | 59 | memcpy(base + load_addr_bytes, program_binary + header.text_offset, text_data_size); 60 | memset(base + load_addr_bytes + text_data_size, 0, header.bss_size); 61 | 62 | return ESP_OK; 63 | } 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /src/ld/esp32.ulp.ld: -------------------------------------------------------------------------------- 1 | #include "sdkconfig.h" 2 | 3 | #define ULP_BIN_MAGIC 0x00706c75 4 | #define HEADER_SIZE 12 5 | MEMORY 6 | { 7 | ram(RW) : ORIGIN = 0, LENGTH = CONFIG_ULP_COPROC_RESERVE_MEM 8 | } 9 | 10 | SECTIONS 11 | { 12 | .text : AT(HEADER_SIZE) 13 | { 14 | *(.text) 15 | } >ram 16 | .data : 17 | { 18 | . = ALIGN(4); 19 | *(.data) 20 | } >ram 21 | .bss : 22 | { 23 | . = ALIGN(4); 24 | *(.bss) 25 | } >ram 26 | 27 | .header : AT(0) 28 | { 29 | LONG(ULP_BIN_MAGIC) 30 | SHORT(LOADADDR(.text)) 31 | SHORT(SIZEOF(.text)) 32 | SHORT(SIZEOF(.data)) 33 | SHORT(SIZEOF(.bss)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_README/ulp.s: -------------------------------------------------------------------------------- 1 | /* Define variables, which go into .bss section (zero-initialized data) */ 2 | .data 3 | /* Store count value */ 4 | .global count 5 | count: 6 | .long 0 7 | /* Code goes into .text section */ 8 | .text 9 | .global entry 10 | entry: 11 | move r3, count 12 | ld r0, r3, 0 13 | add r0, r0, 1 14 | st r0, r3, 0 15 | halt 16 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_README/ulp_README.ino: -------------------------------------------------------------------------------- 1 | #include "esp32/ulp.h" 2 | // include ulp header you will create 3 | #include "ulp_main.h" 4 | // include ulptool binary load function 5 | #include "ulptool.h" 6 | 7 | // Unlike the esp-idf always use these binary blob names 8 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 9 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 10 | 11 | static void init_run_ulp(uint32_t usec); 12 | 13 | void setup() { 14 | Serial.begin(115200); 15 | delay(1000); 16 | init_run_ulp(100 * 1000); // 100 msec 17 | } 18 | 19 | void loop() { 20 | // ulp variables data is the lower 16 bits 21 | Serial.printf("ulp count: %u\n", ulp_count & 0xFFFF); 22 | delay(100); 23 | } 24 | //hash: 5c389934265d8016df226704091cd30a 25 | static void init_run_ulp(uint32_t usec) { 26 | // initialize ulp variable 27 | ulp_set_wakeup_period(0, usec); 28 | esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 29 | // ulp coprocessor will run on its own now 30 | ulp_count = 0; 31 | err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)); 32 | if (err) Serial.println("Error Starting ULP Coprocessor"); 33 | } 34 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_README/ulp_main.h: -------------------------------------------------------------------------------- 1 | /* 2 | Put your ULP globals here you want visibility 3 | for your sketch. Add "ulp_" to the beginning 4 | of the variable name and must be size 'uint32_t' 5 | */ 6 | #include "Arduino.h" 7 | 8 | extern uint32_t ulp_entry; 9 | extern uint32_t ulp_count; 10 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_adc/adc.s: -------------------------------------------------------------------------------- 1 | /* ULP Example: using ADC in deep sleep 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | This file contains assembly code which runs on the ULP. 10 | 11 | ULP wakes up to run this code at a certain period, determined by the values 12 | in SENS_ULP_CP_SLEEP_CYCx_REG registers. On each wake up, the program 13 | measures input voltage on the given ADC channel 'adc_oversampling_factor' 14 | times. Measurements are accumulated and average value is calculated. 15 | Average value is compared to the two thresholds: 'low_thr' and 'high_thr'. 16 | If the value is less than 'low_thr' or more than 'high_thr', ULP wakes up 17 | the chip from deep sleep. 18 | */ 19 | 20 | /* ULP assembly files are passed through C preprocessor first, so include directives 21 | and C macros may be used in these files 22 | */ 23 | #include "soc/rtc_cntl_reg.h" 24 | #include "soc/soc_ulp.h" 25 | 26 | /* ADC1 channel 6, GPIO34 */ 27 | .set adc_channel, 6 28 | 29 | /* Configure the number of ADC samples to average on each measurement. 30 | For convenience, make it a power of 2. */ 31 | .set adc_oversampling_factor_log, 2 32 | .set adc_oversampling_factor, (1 << adc_oversampling_factor_log) 33 | 34 | .data /* .data section */ 35 | 36 | .global thermistor_lut 37 | thermistor_lut: 38 | .long 0x0D33 //3379 -5 degree centigrade 39 | .long 0x0D1F //3359 -4 40 | .long 0x0D0C //3340 -3 41 | .long 0x0CF8 //3320 -2 42 | .long 0x0CE4 //3300 -1 43 | .long 0x0CD0 //3280 0 44 | .long 0x0CBB //3259 1 45 | .long 0x0CA6 //3238 2 46 | .long 0x0C90 //3216 3 47 | .long 0x0C7A //3194 4 48 | .long 0x0C63 //3171 5 49 | .long 0x0C4B //3147 6 50 | .long 0x0C32 //3122 7 51 | .long 0x0C19 //3097 8 52 | .long 0x0BFE //3070 9 53 | .long 0x0BE3 //3043 10 54 | .long 0x0BC6 //3014 11 55 | .long 0x0BA9 //2985 12 56 | .long 0x0B8C //2956 13 57 | .long 0x0B6F //2927 14 58 | .long 0x0B51 //2897 15 59 | .long 0x0B33 //2867 16 60 | .long 0x0B14 //2836 17 61 | .long 0x0AF4 //2804 18 62 | .long 0x0AD2 //2770 19 63 | .long 0x0AB0 //2736 20 64 | .long 0x0A8C //2700 21 65 | .long 0x0A68 //2664 22 66 | .long 0x0A43 //2627 23 67 | .long 0x0A1E //2590 24 68 | .long 0x09F8 //2552 25 69 | .long 0x09D4 //2516 26 70 | .long 0x09B0 //2480 27 71 | .long 0x098E //2446 28 72 | .long 0x096D //2413 29 73 | .long 0x0950 //2384 30 74 | .long 0x092D //2349 31 75 | .long 0x0909 //2313 32 76 | .long 0x08E4 //2276 33 77 | .long 0x08BE //2238 34 78 | .long 0x0896 //2198 35 79 | .long 0x086E //2158 36 80 | .long 0x0845 //2117 37 81 | .long 0x081B //2075 38 82 | .long 0x07F2 //2034 39 83 | .long 0x07C9 //1993 40 84 | .long 0x07A0 //1952 41 85 | .long 0x0778 //1912 42 86 | .long 0x0753 //1875 43 87 | .long 0x072F //1839 44 88 | .long 0x070E //1806 45 89 | .long 0x06E8 //1768 46 90 | .long 0x06C3 //1731 47 91 | .long 0x069E //1694 48 92 | .long 0x067B //1659 49 93 | .long 0x0657 //1623 50 94 | .long 0x0635 //1589 51 95 | .long 0x0613 //1555 52 96 | .long 0x05F1 //1521 53 97 | .long 0x05D0 //1488 54 98 | .long 0x05AF //1455 55 99 | .long 0x058E //1422 56 100 | .long 0x056D //1389 57 101 | .long 0x054C //1356 58 102 | .long 0x052A //1322 59 103 | .long 0x0509 //1289 60 104 | .long 0x04EA //1258 61 105 | .long 0x04CB //1227 62 106 | .long 0x04AD //1197 63 107 | .long 0x048F //1167 64 108 | .long 0x0471 //1137 65 109 | .long 0x0454 //1108 66 110 | .long 0x0438 //1080 67 111 | .long 0x041C //1052 68 112 | .long 0x0401 //1025 69 113 | .long 0x03E6 //998 70 114 | .long 0x03CD //973 71 115 | .long 0x03B4 //948 72 116 | .long 0x039B //923 73 117 | .long 0x0384 //900 74 118 | .long 0x036D //877 75 119 | .long 0x0357 //855 76 120 | .long 0x0342 //834 77 121 | .long 0x032E //814 78 122 | .long 0x031A //794 79 123 | .long 0x0306 //774 80 124 | .long 0x02F3 //755 81 125 | .long 0x02E0 //736 82 126 | .long 0x02CE //718 83 127 | .long 0x02BB //699 84 128 | .long 0x02A9 //681 85 129 | .long 0x0297 //663 86 130 | .long 0x0285 //645 87 131 | .long 0x0273 //627 88 132 | .long 0x0262 //610 89 133 | .long 0x0250 //592 90 134 | .long 0x0240 //576 91 135 | .long 0x0230 //560 92 136 | .long 0x0222 //546 93 137 | .long 0x0213 //531 94 138 | .long 0x0205 //517 95 139 | .long 0x01F8 //504 96 140 | .long 0x01EB //491 97 141 | .long 0x01DE //478 98 142 | .long 0x01D2 //466 99 143 | .long 0x01C5 //453 100 144 | .long 0xffff //table end 145 | 146 | /* Define variables, which go into .bss section (zero-initialized data) */ 147 | .bss 148 | 149 | .global last_result 150 | last_result: 151 | .long 0 152 | 153 | .global temperature 154 | temperature: 155 | .long 0 156 | 157 | /* Code goes into .text section */ 158 | .text 159 | .global entry 160 | entry: 161 | /* do measurements using ADC */ 162 | /* r0 will be used as accumulator */ 163 | move r0, 0 164 | /* initialize the loop counter */ 165 | stage_rst 166 | measure: 167 | /* measure and add value to accumulator */ 168 | adc r1, 0, adc_channel + 1 169 | add r0, r0, r1 170 | /* increment loop counter and check exit condition */ 171 | stage_inc 1 172 | jumps measure, adc_oversampling_factor, lt 173 | 174 | /* divide accumulator by adc_oversampling_factor. 175 | Since it is chosen as a power of two, use right shift */ 176 | rsh r0, r0, adc_oversampling_factor_log 177 | /* averaged value is now in r0; store it into last_result */ 178 | move r3, last_result 179 | st r0, r3, 0 180 | 181 | move r3, temperature 182 | ld r1, r3, 0 183 | /* initialize temperature every calculate loop */ 184 | move r1, 0 185 | st r1, r3, 0 186 | /* use r3 as thermistor_lut pointer */ 187 | move r3, thermistor_lut 188 | look_up_table: 189 | /* load table data in R2 */ 190 | ld r2, r3, 0 191 | 192 | /* check if at the table_end */ 193 | sub r1, r2, 0xffff 194 | /* if hit the tail of table, jump to table_end */ 195 | jump out_of_range, eq 196 | 197 | /* thermistor_lut - last_result */ 198 | sub r2, r0, r2 199 | /* adc_value < table_value */ 200 | jump move_step, ov 201 | /* adc_value > table_value, get current value */ 202 | jump wake_up 203 | out_of_range: 204 | move r1, temperature 205 | ld r2, r1, 0 206 | /* Maximum range 100 degree centigrade */ 207 | move r2, 105 208 | st r2, r1, 0 209 | jump wake_up 210 | move_step: 211 | /* move pointer a step */ 212 | add r3, r3, 1 213 | 214 | move r1, temperature 215 | ld r2, r1, 0 216 | /* temperature count 1 */ 217 | add r2, r2, 1 218 | st r2, r1, 0 219 | jump look_up_table 220 | 221 | /* value within range, end the program */ 222 | .global exit 223 | exit: 224 | halt 225 | 226 | .global wake_up 227 | wake_up: 228 | /* Check if the system can be woken up */ 229 | READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) 230 | 231 | and r0, r0, 1 232 | jump exit, eq 233 | 234 | /* Wake up the SoC, end program */ 235 | wake 236 | WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0) 237 | halt 238 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_adc/ulp_adc.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Must allocate more memory for the ulp in 3 | * esp32/tools/sdk/include/sdkconfig.h 4 | * -> #define CONFIG_ULP_COPROC_RESERVE_MEM 5 | * for this sketch to compile. 2048b seems 6 | * good. 7 | */ 8 | #include "esp_sleep.h" 9 | #include "driver/rtc_io.h" 10 | #include "driver/adc.h" 11 | #include "esp32/ulp.h" 12 | #include "ulp_main.h" 13 | #include "ulptool.h" 14 | 15 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 16 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 17 | 18 | /* This function is called once after power-on reset, to load ULP program into 19 | RTC memory and configure the ADC. 20 | */ 21 | static void init_ulp_program(); 22 | 23 | /* This function is called every time before going into deep sleep. 24 | It starts the ULP program and resets measurement counter. 25 | */ 26 | static void start_ulp_program(); 27 | 28 | void setup() { 29 | Serial.begin(115200); 30 | delay(100); 31 | esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); 32 | if (cause != ESP_SLEEP_WAKEUP_ULP) { 33 | Serial.printf("Not ULP wakeup\n"); 34 | init_ulp_program(); 35 | } else { 36 | Serial.printf("Deep sleep wakeup\n"); 37 | /* Count temperature form -5 ℃ , so ulp_temperature minus 5 */ 38 | Serial.printf("Temperature:%d℃\n", (int16_t)ulp_temperature - 5); 39 | } 40 | Serial.printf("Entering deep sleep\n\n"); 41 | start_ulp_program(); 42 | ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() ); 43 | esp_deep_sleep_start(); 44 | } 45 | 46 | void loop() { 47 | 48 | } 49 | 50 | static void init_ulp_program() 51 | { 52 | esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, 53 | (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 54 | ESP_ERROR_CHECK(err); 55 | 56 | /* Configure ADC channel */ 57 | /* Note: when changing channel here, also change 'adc_channel' constant 58 | in adc.S */ 59 | adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11); 60 | adc1_config_width(ADC_WIDTH_BIT_12); 61 | adc1_ulp_enable(); 62 | 63 | /* Set ULP wake up period to 1000ms */ 64 | ulp_set_wakeup_period(0, 1000 * 1000); 65 | 66 | /* Disable pullup on GPIO15, in case it is connected to ground to suppress 67 | boot messages. 68 | */ 69 | rtc_gpio_pullup_dis(GPIO_NUM_15); 70 | rtc_gpio_hold_en(GPIO_NUM_15); 71 | } 72 | 73 | static void start_ulp_program() 74 | { 75 | /* Start the program */ 76 | esp_err_t err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)); 77 | ESP_ERROR_CHECK(err); 78 | } 79 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_adc/ulp_main.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | 3 | extern uint32_t ulp_entry; 4 | extern uint32_t ulp_temperature; 5 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_hall_sensor/hall_sensor.s: -------------------------------------------------------------------------------- 1 | /* ULP Example: Read hall sensor in deep sleep 2 | 3 | For other examples please check: 4 | https://github.com/espressif/esp-iot-solution/tree/master/examples 5 | 6 | This example code is in the Public Domain (or CC0 licensed, at your option.) 7 | 8 | Unless required by applicable law or agreed to in writing, this 9 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 10 | CONDITIONS OF ANY KIND, either express or implied. 11 | */ 12 | 13 | 14 | /* ULP assembly files are passed through C preprocessor first, so include directives 15 | and C macros may be used in these files 16 | */ 17 | 18 | #include "soc/rtc_cntl_reg.h" 19 | #include "soc/rtc_io_reg.h" 20 | #include "soc/soc_ulp.h" 21 | #include "soc/sens_reg.h" 22 | 23 | /* Configure the number of ADC samples to average on each measurement. 24 | For convenience, make it a power of 2. */ 25 | .set adc_oversampling_factor_log, 2 26 | .set adc_oversampling_factor, (1 << adc_oversampling_factor_log) 27 | 28 | /* Define variables, which go into .bss section (zero-initialized data) */ 29 | .bss 30 | .global Sens_Vp0 31 | Sens_Vp0: 32 | .long 0 33 | 34 | .global Sens_Vn0 35 | Sens_Vn0: 36 | .long 0 37 | 38 | .global Sens_Vp1 39 | Sens_Vp1: 40 | .long 0 41 | 42 | .global Sens_Vn1 43 | Sens_Vn1: 44 | .long 0 45 | 46 | /* Code goes into .text section */ 47 | .text 48 | .global entry 49 | entry: 50 | 51 | /* SENS_XPD_HALL_FORCE = 1, hall sensor force enable, XPD HALL is controlled by SW */ 52 | WRITE_RTC_REG(SENS_SAR_TOUCH_CTRL1_REG, SENS_XPD_HALL_FORCE_S, 1, 1) 53 | 54 | /* RTC_IO_XPD_HALL = 1, xpd hall, Power on hall sensor and connect to VP and VN */ 55 | WRITE_RTC_REG(RTC_IO_HALL_SENS_REG, RTC_IO_XPD_HALL_S, 1, 1) 56 | 57 | /* SENS_HALL_PHASE_FORCE = 1, phase force, HALL PHASE is controlled by SW */ 58 | WRITE_RTC_REG(SENS_SAR_TOUCH_CTRL1_REG, SENS_HALL_PHASE_FORCE_S, 1, 1) 59 | 60 | /* RTC_IO_HALL_PHASE = 0, phase of hall sensor */ 61 | WRITE_RTC_REG(RTC_IO_HALL_SENS_REG, RTC_IO_HALL_PHASE_S, 1, 0) 62 | 63 | /* SENS_FORCE_XPD_SAR, Force power up */ 64 | WRITE_RTC_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, 2, SENS_FORCE_XPD_SAR_PU) 65 | 66 | /* do measurements using ADC */ 67 | /* r2, r3 will be used as accumulator */ 68 | move r2, 0 69 | move r3, 0 70 | /* initialize the loop counter */ 71 | stage_rst 72 | measure0: 73 | /* measure Sar_Mux = 1 to get vp0 */ 74 | adc r0, 0, 1 75 | add r2, r2, r0 76 | 77 | /* measure Sar_Mux = 4 to get vn0 */ 78 | adc r1, 0, 4 79 | add r3, r3, r1 80 | 81 | /* increment loop counter and check exit condition */ 82 | stage_inc 1 83 | jumps measure0, adc_oversampling_factor, lt 84 | 85 | /* divide accumulator by adc_oversampling_factor. 86 | Since it is chosen as a power of two, use right shift */ 87 | rsh r2, r2, adc_oversampling_factor_log 88 | 89 | /* averaged value is now in r2; store it into Sens_Vp0 */ 90 | move r0, Sens_Vp0 91 | st r2, r0, 0 92 | 93 | /* r3 divide 4 which means rsh 2 bits */ 94 | rsh r3, r3, adc_oversampling_factor_log 95 | /* averaged value is now in r3; store it into Sens_Vn0 */ 96 | move r1, Sens_Vn0 97 | st r3, r1, 0 98 | 99 | /* RTC_IO_HALL_PHASE = 1, phase of hall sensor */ 100 | WRITE_RTC_REG(RTC_IO_HALL_SENS_REG, RTC_IO_HALL_PHASE_S, 1, 1) 101 | 102 | /* do measurements using ADC */ 103 | /* r2, r3 will be used as accumulator */ 104 | move r2, 0 105 | move r3, 0 106 | /* initialize the loop counter */ 107 | stage_rst 108 | measure1: 109 | /* measure Sar_Mux = 1 to get vp1 */ 110 | adc r0, 0, 1 111 | add r2, r2, r0 112 | 113 | /* measure Sar_Mux = 4 to get vn1 */ 114 | adc r1, 0, 4 115 | add r3, r3, r1 116 | 117 | /* increment loop counter and check exit condition */ 118 | stage_inc 1 119 | jumps measure1, adc_oversampling_factor, lt 120 | 121 | /* divide accumulator by adc_oversampling_factor. 122 | Since it is chosen as a power of two, use right shift */ 123 | rsh r2, r2, adc_oversampling_factor_log 124 | 125 | /* averaged value is now in r2; store it into Sens_Vp1 */ 126 | move r0, Sens_Vp1 127 | st r2, r0, 0 128 | 129 | /* r3 divide 4 which means rsh 2 bits */ 130 | rsh r3, r3, adc_oversampling_factor_log 131 | /* averaged value is now in r3; store it into Sens_Vn1 */ 132 | move r1, Sens_Vn1 133 | st r3, r1, 0 134 | 135 | /* wake up */ 136 | jump wake_up 137 | 138 | /* Get ULP back to sleep */ 139 | .global exit 140 | exit: 141 | halt 142 | 143 | .global wake_up 144 | wake_up: 145 | /* Check if the SoC can be woken up */ 146 | READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1) 147 | and r0, r0, 1 148 | jump exit, eq 149 | 150 | /* Wake up the SoC and stop ULP program */ 151 | wake 152 | /* Stop the wakeup timer so it does not restart ULP */ 153 | WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0) 154 | halt 155 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_hall_sensor/ulp_hall_sensor.ino: -------------------------------------------------------------------------------- 1 | #include "esp_sleep.h" 2 | #include "soc/soc.h" 3 | #include "soc/soc_ulp.h" 4 | #include "driver/adc.h" 5 | #include "esp32/ulp.h" 6 | #include "ulp_main.h" 7 | #include "ulptool.h" 8 | 9 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 10 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 11 | 12 | static void init_ulp_program() 13 | { 14 | esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, 15 | (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 16 | ESP_ERROR_CHECK(err); 17 | 18 | /* The ADC1 channel 0 input voltage will be reduced to about 1/2 */ 19 | adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_6); 20 | /* The ADC1 channel 3 input voltage will be reduced to about 1/2 */ 21 | adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_6); 22 | /* ADC capture 12Bit width */ 23 | adc1_config_width(ADC_WIDTH_BIT_12); 24 | /* enable adc1 */ 25 | adc1_ulp_enable(); 26 | 27 | /* Set ULP wake up period to 3 S */ 28 | ulp_set_wakeup_period(0, 3 * 1000 * 1000); 29 | } 30 | 31 | static void print_hall_sensor() 32 | { 33 | Serial.printf("ulp_hall_sensor:Sens_Vp0:%d,Sens_Vn0:%d,Sens_Vp1:%d,Sens_Vn1:%d\r\n", 34 | (uint16_t)ulp_Sens_Vp0, (uint16_t)ulp_Sens_Vn0, (uint16_t)ulp_Sens_Vp1, (uint16_t)ulp_Sens_Vn1); 35 | Serial.printf("offset:%d\r\n", ((uint16_t)ulp_Sens_Vp0 - (uint16_t)ulp_Sens_Vp1) - ((uint16_t)ulp_Sens_Vn0 - (uint16_t)ulp_Sens_Vn1)); 36 | } 37 | 38 | void setup() { 39 | Serial.begin(115200); 40 | esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); 41 | if (cause != ESP_SLEEP_WAKEUP_ULP) { 42 | Serial.printf("Not ULP wakeup, initializing ULP\n"); 43 | init_ulp_program(); 44 | } else { 45 | Serial.println("--------------------------------------------------------------------------"); 46 | Serial.printf("ULP wakeup, printing hall sensor value\n"); 47 | print_hall_sensor(); 48 | } 49 | 50 | Serial.printf("Entering deep sleep\n\n"); 51 | /* Start the ULP program */ 52 | ESP_ERROR_CHECK( ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t))); 53 | ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() ); 54 | esp_deep_sleep_start(); 55 | } 56 | 57 | void loop() { 58 | // not used 59 | } 60 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_hall_sensor/ulp_main.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | 3 | extern uint32_t ulp_entry; 4 | extern uint32_t ulp_Sens_Vp0; 5 | extern uint32_t ulp_Sens_Vn0; 6 | extern uint32_t ulp_Sens_Vp1; 7 | extern uint32_t ulp_Sens_Vn1; 8 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_i2c_bitbang/i2c.s: -------------------------------------------------------------------------------- 1 | /* ULP Example: Read temperautre in deep sleep 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | This file contains assembly code which runs on the ULP. 10 | 11 | */ 12 | 13 | /* ULP assembly files are passed through C preprocessor first, so include directives 14 | and C macros may be used in these files 15 | */ 16 | 17 | #include "soc/rtc_cntl_reg.h" 18 | #include "soc/rtc_io_reg.h" 19 | #include "soc/soc_ulp.h" 20 | #include "stack.s" 21 | 22 | .bss 23 | i2c_started: 24 | .long 0 25 | i2c_didInit: 26 | .long 0 27 | 28 | .text 29 | .global i2c_start_cond 30 | .global i2c_stop_cond 31 | .global i2c_write_bit 32 | .global i2c_read_bit 33 | .global i2c_write_byte 34 | .global i2c_read_byte 35 | 36 | .macro I2C_delay 37 | wait 50 // if number equ 10 then clock gap is minimal 4.7us 38 | .endm 39 | 40 | .macro read_SCL 41 | READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + 9, 1) 42 | .endm 43 | 44 | .macro read_SDA 45 | READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + 8, 1) 46 | .endm 47 | 48 | .macro set_SCL 49 | WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TC_REG, RTC_GPIO_ENABLE_W1TC_S + 9, 1, 1) 50 | .endm 51 | 52 | .macro clear_SCL 53 | WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TS_REG, RTC_GPIO_ENABLE_W1TS_S + 9, 1, 1) 54 | .endm 55 | 56 | .macro set_SDA 57 | WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TC_REG, RTC_GPIO_ENABLE_W1TC_S + 8, 1, 1) 58 | .endm 59 | 60 | .macro clear_SDA 61 | WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TS_REG, RTC_GPIO_ENABLE_W1TS_S + 8, 1, 1) 62 | .endm 63 | 64 | 65 | i2c_start_cond: 66 | move r1,i2c_didInit 67 | ld r0,r1,0 68 | jumpr didInit,1,ge 69 | move r0,1 70 | st r0,r1,0 71 | WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + 9, 1, 0) 72 | WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + 8, 1, 0) 73 | didInit: 74 | move r2,i2c_started 75 | ld r0,r2,0 76 | jumpr not_started,1,lt // if started, do a restart condition 77 | set_SDA // set SDA to 1 78 | I2C_delay 79 | set_SCL 80 | clock_stretch: // TODO: Add timeout 81 | read_SCL 82 | jumpr clock_stretch,1,lt 83 | I2C_delay // Repeated start setup time, minimum 4.7us 84 | not_started: 85 | clear_SDA // SCL is high, set SDA from 1 to 0. 86 | I2C_delay 87 | clear_SCL 88 | move r0,1 89 | st r0,r2,0 90 | ret 91 | 92 | i2c_stop_cond: 93 | clear_SDA // set SDA to 0 94 | I2C_delay 95 | set_SCL 96 | clock_stretch_stop: 97 | read_SCL 98 | jumpr clock_stretch_stop,1,lt 99 | I2C_delay // Stop bit setup time, minimum 4us 100 | set_SDA // SCL is high, set SDA from 0 to 1 101 | I2C_delay 102 | move r2,i2c_started 103 | move r0,0 104 | st r0,r2,0 105 | ret 106 | 107 | i2c_write_bit: // Write a bit to I2C bus 108 | jumpr bit0,1,lt 109 | set_SDA 110 | jump bit1 111 | bit0: 112 | clear_SDA 113 | bit1: 114 | I2C_delay // SDA change propagation delay 115 | set_SCL 116 | I2C_delay 117 | clock_stretch_write: 118 | read_SCL 119 | jumpr clock_stretch_write,1,lt 120 | clear_SCL 121 | ret 122 | 123 | i2c_read_bit: // Read a bit from I2C bus 124 | set_SDA // Let the slave drive data 125 | I2C_delay 126 | set_SCL 127 | clock_stretch_read: 128 | read_SCL 129 | jumpr clock_stretch_read,1,lt 130 | I2C_delay 131 | read_SDA // SCL is high, read out bit 132 | clear_SCL 133 | ret // bit in r0 134 | 135 | i2c_write_byte: // Return 0 if ack by the slave. 136 | stage_rst 137 | next_bit: 138 | and r0,r2,0x80 139 | psr 140 | jump i2c_write_bit 141 | lsh r2,r2,1 142 | stage_inc 1 143 | jumps next_bit,8,lt 144 | psr 145 | jump i2c_read_bit 146 | ret 147 | 148 | i2c_read_byte: // Read a byte from I2C bus 149 | push r2 150 | move r2,0 151 | stage_rst 152 | next_bit_read: 153 | psr 154 | jump i2c_read_bit 155 | lsh r2,r2,1 156 | or r2,r2,r0 157 | stage_inc 1 158 | jumps next_bit_read,8,lt 159 | pop r0 160 | psr 161 | jump i2c_write_bit 162 | move r0,r2 163 | ret 164 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_i2c_bitbang/i2c_dev.s: -------------------------------------------------------------------------------- 1 | /* ULP Example: using ADC in deep sleep 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | This file contains assembly code which runs on the ULP. 10 | */ 11 | 12 | /* ULP assembly files are passed through C preprocessor first, so include directives 13 | and C macros may be used in these files 14 | */ 15 | 16 | 17 | #include "soc/rtc_cntl_reg.h" 18 | #include "soc/rtc_io_reg.h" 19 | #include "soc/soc_ulp.h" 20 | #include "stack.s" 21 | 22 | .set BH1750_ADDR_W, 0x46 23 | .set BH1750_ADDR_R, 0x47 24 | .set BH1750_ON,0x01 25 | .set BH1750_RSET,0x07 26 | .set BH1750_ONE, 0x20 27 | .set BH1750_ONE_LOW, 0x23 28 | 29 | .bss 30 | .global sample_counter 31 | sample_counter: 32 | .long 0 33 | .global result 34 | result: 35 | .long 0 36 | .global stack 37 | stack: 38 | .skip 100 39 | .global stackEnd 40 | stackEnd: 41 | .long 0 42 | 43 | .text 44 | .global entry 45 | entry: 46 | move r3,stackEnd 47 | psr 48 | jump Task_BH1750 49 | move r1, sample_counter /* Read sample counter */ 50 | ld r0, r1, 0 51 | add r0, r0, 1 /* Increment */ 52 | st r0, r1, 0 /* Save counter in memory */ 53 | jumpr clear, 3, ge 54 | jump exit 55 | clear: 56 | move r1, sample_counter 57 | ld r0, r1, 0 58 | .set zero, 0x00 59 | move r0, zero 60 | st r0, r1, 0 61 | jump wake_up 62 | /* value within range, end the program */ 63 | .global exit 64 | exit: 65 | halt 66 | 67 | .global wake_up 68 | wake_up: 69 | /* Check if the system can be woken up */ 70 | READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1) 71 | and r0, r0, 1 72 | jump exit, eq 73 | /* Wake up the SoC, end program */ 74 | wake 75 | WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0) 76 | halt 77 | 78 | .global Read_BH1750 79 | Read_BH1750: 80 | move r1, BH1750_ADDR_R 81 | push r1 82 | psr 83 | jump i2c_start_cond // i2c Start 84 | ld r2, r3, 4 // Address+Read 85 | psr 86 | jump i2c_write_byte 87 | jumpr popfail, 1, ge 88 | pop r1 89 | move r2,0 90 | psr 91 | jump i2c_read_byte 92 | push r0 93 | move r2,1 // last byte 94 | psr 95 | jump i2c_read_byte 96 | push r0 97 | psr 98 | jump i2c_stop_cond 99 | pop r0 // Low-byte 100 | pop r2 // Hight-byte 101 | lsh r2,r2,8 102 | or r2,r2,r0 103 | move r0,r2 104 | move r1, result 105 | st r0, r1, 0 106 | move r2,0 // OK 107 | ret 108 | 109 | 110 | .global Cmd_Write_BH1750 111 | Cmd_Write_BH1750: 112 | psr 113 | jump i2c_start_cond // i2c Start 114 | ld r2, r3, 12 // Address+Write 115 | psr 116 | jump i2c_write_byte 117 | jumpr popfail,1,ge 118 | ld r2, r3, 8 // Command 119 | psr 120 | jump i2c_write_byte 121 | jumpr popfail, 1, ge 122 | psr 123 | jump i2c_stop_cond // i2c Stop 124 | ret 125 | 126 | .global Start_BH1750 127 | Start_BH1750: 128 | move r1, BH1750_ADDR_W 129 | push r1 130 | move r1, BH1750_ON 131 | push r1 132 | psr 133 | jump Cmd_Write_BH1750 // power on 134 | pop r1 135 | move r1, BH1750_ONE 136 | push r1 137 | psr 138 | jump Cmd_Write_BH1750 // once H 139 | pop r1 140 | pop r1 141 | ret 142 | 143 | .global Task_BH1750 144 | Task_BH1750: 145 | psr 146 | jump Start_BH1750 147 | move r2, 200 // Wait 150ms 148 | psr 149 | jump waitMs 150 | psr 151 | jump Read_BH1750 152 | ret 153 | 154 | popfail: 155 | pop r1 // pop caller return address 156 | move r2,1 157 | ret 158 | 159 | // Wait for r2 milliseconds 160 | .global waitMs 161 | waitMs: 162 | wait 8000 163 | sub r2,r2,1 164 | jump doneWaitMs,eq 165 | jump waitMs 166 | doneWaitMs: 167 | ret 168 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_i2c_bitbang/stack.s: -------------------------------------------------------------------------------- 1 | /* ULP Example: Read temperautre in deep sleep 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | This file contains assembly code which runs on the ULP. 10 | 11 | */ 12 | 13 | /* ULP assembly files are passed through C preprocessor first, so include directives 14 | and C macros may be used in these files 15 | */ 16 | 17 | .macro push rx 18 | st \rx,r3,0 19 | sub r3,r3,1 20 | .endm 21 | 22 | .macro pop rx 23 | add r3,r3,1 24 | ld \rx,r3,0 25 | .endm 26 | 27 | // Prepare subroutine jump 28 | .macro psr 29 | .set addr,(.+16) 30 | move r1,addr 31 | push r1 32 | .endm 33 | 34 | // Return from subroutine 35 | .macro ret 36 | pop r1 37 | jump r1 38 | .endm 39 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_i2c_bitbang/ulp_i2c_bitbang.ino: -------------------------------------------------------------------------------- 1 | /* ULP Example 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | */ 9 | /* 10 | * Must allocate more memory for the ulp in 11 | * esp32/tools/sdk/include/sdkconfig.h 12 | * -> #define CONFIG_ULP_COPROC_RESERVE_MEM 13 | * for this sketch to compile. 2048b seems 14 | * good. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "esp_sleep.h" 20 | #include "soc/rtc_cntl_reg.h" 21 | #include "soc/rtc_io_reg.h" 22 | #include "soc/sens_reg.h" 23 | #include "soc/soc.h" 24 | #include "driver/rtc_io.h" 25 | #include "esp32/ulp.h" 26 | #include "sdkconfig.h" 27 | #include "ulp_main.h" 28 | #include "ulptool.h" 29 | 30 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 31 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 32 | 33 | const gpio_num_t GPIO_SCL = GPIO_NUM_32; 34 | const gpio_num_t GPIO_SDA = GPIO_NUM_33; 35 | 36 | /* This function is called once after power-on reset, to load ULP program into 37 | RTC memory and configure the ADC. 38 | */ 39 | static void init_ulp_program(); 40 | 41 | /* This function is called every time before going into deep sleep. 42 | It starts the ULP program and resets measurement counter. 43 | */ 44 | static void start_ulp_program(); 45 | 46 | uint16_t convert_lux(uint16_t raw_data) 47 | { 48 | uint32_t temp; 49 | temp = (uint32_t) raw_data; 50 | temp *= 10; 51 | temp /= 120; 52 | return temp; 53 | } 54 | 55 | void setup() { 56 | esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); 57 | if (cause != ESP_SLEEP_WAKEUP_ULP) { 58 | Serial.printf("Not ULP wakeup\n"); 59 | init_ulp_program(); 60 | } else { 61 | Serial.printf("Deep sleep wakeup,raw_data: %d ", (uint16_t)ulp_result); 62 | Serial.printf("light: %d lux\n", convert_lux(ulp_result)); 63 | } 64 | Serial.printf("Entering deep sleep\n\n"); 65 | start_ulp_program(); 66 | ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() ); 67 | esp_deep_sleep_start(); 68 | } 69 | 70 | void loop() { 71 | 72 | } 73 | 74 | static void init_ulp_program() 75 | { 76 | esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, 77 | (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 78 | ESP_ERROR_CHECK(err); 79 | 80 | rtc_gpio_init(GPIO_SCL); 81 | rtc_gpio_set_direction(GPIO_SCL, RTC_GPIO_MODE_INPUT_ONLY); 82 | rtc_gpio_init(GPIO_SDA); 83 | rtc_gpio_set_direction(GPIO_SDA, RTC_GPIO_MODE_INPUT_ONLY); 84 | 85 | /* Set ULP wake up period to 1000ms */ 86 | ulp_set_wakeup_period(0, 1000 * 1000); 87 | } 88 | 89 | static void start_ulp_program() 90 | { 91 | /* Start the program */ 92 | esp_err_t err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)); 93 | ESP_ERROR_CHECK(err); 94 | } 95 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_i2c_bitbang/ulp_main.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | 3 | extern uint32_t ulp_entry; 4 | extern uint32_t ulp_result; 5 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_rtc_gpio/rtcio.s: -------------------------------------------------------------------------------- 1 | /* ULP Example: using RTC GPIO in deep sleep 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | This file contains assembly code which runs on the ULP. 10 | 11 | ULP wakes up to run this code at a certain period, determined by the values 12 | in SENS_ULP_CP_SLEEP_CYCx_REG registers. On each wake up, the program 13 | measures input voltage on the given ADC channel 'adc_oversampling_factor' 14 | times. Measurements are accumulated and average value is calculated. 15 | Average value is compared to the two thresholds: 'low_thr' and 'high_thr'. 16 | If the value is less than 'low_thr' or more than 'high_thr', ULP wakes up 17 | the chip from deep sleep. 18 | */ 19 | 20 | /* ULP assembly files are passed through C preprocessor first, so include directives 21 | and C macros may be used in these files 22 | */ 23 | 24 | #include "soc/rtc_cntl_reg.h" 25 | #include "soc/rtc_io_reg.h" 26 | #include "soc/soc_ulp.h" 27 | 28 | /* Define variables, which go into .bss section (zero-initialized data) */ 29 | .bss 30 | 31 | .global toggle_counter 32 | toggle_counter: 33 | .long 0 34 | 35 | /* Number of restarts of ULP to wake up the main program. 36 | See couple of lines below how this value is used */ 37 | .set toggle_cycles_to_wakeup, 7 38 | 39 | /* Code goes into .text section */ 40 | .text 41 | .global entry 42 | entry: 43 | /* Disable hold of RTC_GPIO10 output */ 44 | WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG,RTC_IO_TOUCH_PAD0_HOLD_S,1,0) 45 | /* Set the RTC_GPIO10 output HIGH 46 | to signal that ULP is now up */ 47 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG,RTC_GPIO_OUT_DATA_W1TS_S+10,1,1) 48 | /* Wait some cycles to have visible trace on the scope */ 49 | wait 1000 50 | 51 | /* Read toggle counter */ 52 | move r3, toggle_counter 53 | ld r0, r3, 0 54 | /* Increment */ 55 | add r0, r0, 1 56 | /* Save counter in memory */ 57 | st r0, r3, 0 58 | /* Save counter in r3 to use it later */ 59 | move r3, r0 60 | 61 | /* Disable hold of RTC_GPIO17 output */ 62 | WRITE_RTC_REG(RTC_IO_TOUCH_PAD7_REG,RTC_IO_TOUCH_PAD7_HOLD_S,1,0) 63 | 64 | /* Toggle RTC_GPIO17 output */ 65 | and r0, r0, 0x01 66 | jump toggle_clear, eq 67 | 68 | /* Set the RTC_GPIO17 output HIGH */ 69 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG,RTC_GPIO_OUT_DATA_W1TS_S+17,1,1) 70 | jump toggle_complete 71 | 72 | .global toggle_clear 73 | toggle_clear: 74 | /* Set the RTC_GPIO17 output LOW (clear output) */ 75 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG,RTC_GPIO_OUT_DATA_W1TC_S+17,1,1) 76 | 77 | .global toggle_complete 78 | toggle_complete: 79 | 80 | /* Enable hold on RTC_GPIO17 output */ 81 | WRITE_RTC_REG(RTC_IO_TOUCH_PAD7_REG,RTC_IO_TOUCH_PAD7_HOLD_S,1,1) 82 | 83 | /* Set the RTC_GPIO10 output LOW (clear output) 84 | to signal that ULP is now going down */ 85 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG,RTC_GPIO_OUT_DATA_W1TC_S+10,1,1) 86 | 87 | /* Enable hold on RTC_GPIO10 output */ 88 | WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG,RTC_IO_TOUCH_PAD0_HOLD_S,1,1) 89 | 90 | /* Compare the toggle counter with toggle cycles to wakeup SoC 91 | and wakup SoC if the values match */ 92 | and r0, r3, toggle_cycles_to_wakeup 93 | jump wake_up, eq 94 | 95 | /* Get ULP back to sleep */ 96 | .global exit 97 | exit: 98 | halt 99 | 100 | .global wake_up 101 | wake_up: 102 | /* Check if the SoC can be woken up */ 103 | READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1) 104 | and r0, r0, 1 105 | jump exit, eq 106 | 107 | /* Wake up the SoC and stop ULP program */ 108 | wake 109 | /* Stop the wakeup timer so it does not restart ULP */ 110 | WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0) 111 | halt 112 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_rtc_gpio/ulp_main.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | 3 | extern uint32_t ulp_entry; 4 | extern uint32_t ulp_toggle_clear; 5 | extern uint32_t ulp_toggle_complete; 6 | extern uint32_t ulp_wake_up; 7 | extern uint32_t ulp_toggle_counter; 8 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_rtc_gpio/ulp_rtc_gpio.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "esp_sleep.h" 3 | #include "nvs.h" 4 | #include "nvs_flash.h" 5 | #include "soc/rtc_cntl_reg.h" 6 | #include "soc/rtc_io_reg.h" 7 | #include "soc/sens_reg.h" 8 | #include "soc/soc.h" 9 | #include "driver/gpio.h" 10 | #include "driver/rtc_io.h" 11 | #include "esp32/ulp.h" 12 | #include "ulp_main.h" 13 | #include "ulptool.h" 14 | 15 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 16 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 17 | 18 | 19 | gpio_num_t cpu_up_num = GPIO_NUM_25; 20 | gpio_num_t ulp_up_num = GPIO_NUM_4; 21 | 22 | gpio_num_t cpu_toggle_num = GPIO_NUM_26; 23 | gpio_num_t ulp_toggle_num = GPIO_NUM_27; 24 | 25 | RTC_DATA_ATTR static int cpu_toggle_counter; 26 | 27 | static void init_ulp_program() 28 | { 29 | esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 30 | ESP_ERROR_CHECK(err); 31 | 32 | ulp_toggle_counter = 0; 33 | 34 | rtc_gpio_init(cpu_up_num); 35 | rtc_gpio_set_direction(cpu_up_num, RTC_GPIO_MODE_OUTPUT_ONLY); 36 | rtc_gpio_set_level(cpu_up_num, 1); 37 | 38 | rtc_gpio_init(ulp_up_num); 39 | rtc_gpio_set_direction(ulp_up_num, RTC_GPIO_MODE_OUTPUT_ONLY); 40 | 41 | rtc_gpio_init(cpu_toggle_num); 42 | rtc_gpio_set_direction(cpu_toggle_num, RTC_GPIO_MODE_OUTPUT_ONLY); 43 | 44 | rtc_gpio_init(ulp_toggle_num); 45 | rtc_gpio_set_direction(ulp_toggle_num, RTC_GPIO_MODE_OUTPUT_ONLY); 46 | 47 | /* Set ULP wake up period to 10ms */ 48 | ulp_set_wakeup_period(0, 10 * 10000); 49 | } 50 | 51 | static void print_status() 52 | { 53 | 54 | Serial.printf("CPU / ULP toggle counter \r\n0x%x / 0x%x\n", cpu_toggle_counter, ulp_toggle_counter & UINT16_MAX); 55 | 56 | rtc_gpio_hold_dis(cpu_toggle_num); 57 | if (cpu_toggle_counter % 2 == 0) { 58 | rtc_gpio_set_level(cpu_toggle_num, 0); 59 | } else { 60 | rtc_gpio_set_level(cpu_toggle_num, 1); 61 | } 62 | rtc_gpio_hold_en(cpu_toggle_num); 63 | 64 | cpu_toggle_counter++; 65 | } 66 | 67 | void setup() { 68 | Serial.begin(115200); 69 | esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); 70 | if (cause != ESP_SLEEP_WAKEUP_ULP) { 71 | Serial.printf("Not ULP wakeup, initializing ULP\n"); 72 | init_ulp_program(); 73 | } else { 74 | rtc_gpio_set_level(cpu_up_num, 1); 75 | 76 | Serial.printf("ULP wakeup, printing status\n"); 77 | print_status(); 78 | } 79 | 80 | Serial.printf("Entering deep sleep\n\n"); 81 | /* Start the ULP program */ 82 | ESP_ERROR_CHECK( ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t))); 83 | ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() ); 84 | rtc_gpio_set_level(cpu_up_num, 0); 85 | esp_deep_sleep_start(); 86 | } 87 | 88 | void loop() { 89 | // not used 90 | } 91 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_tsens/tsens.s: -------------------------------------------------------------------------------- 1 | /* ULP Example: Read temperautre in deep sleep 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | This file contains assembly code which runs on the ULP. 10 | 11 | */ 12 | 13 | /* ULP assembly files are passed through C preprocessor first, so include directives 14 | and C macros may be used in these files 15 | */ 16 | 17 | #include "soc/rtc_cntl_reg.h" 18 | #include "soc/soc_ulp.h" 19 | #include "soc/sens_reg.h" 20 | 21 | /* Define variables, which go into .bss section (zero-initialized data) */ 22 | .bss 23 | 24 | .global tsens_value 25 | tsens_value: 26 | .long 0 27 | 28 | /* Code goes into .text section */ 29 | .text 30 | .global entry 31 | entry: 32 | /* Force power up */ 33 | WRITE_RTC_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, 2, SENS_FORCE_XPD_SAR_PU) 34 | 35 | tsens r0, 1000 36 | move r3, tsens_value 37 | st r0, r3, 0 38 | jump wake_up 39 | 40 | /* Get ULP back to sleep */ 41 | .global exit 42 | exit: 43 | halt 44 | 45 | .global wake_up 46 | wake_up: 47 | /* Check if the SoC can be woken up */ 48 | READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1) 49 | and r0, r0, 1 50 | jump exit, eq 51 | 52 | /* Wake up the SoC and stop ULP program */ 53 | wake 54 | /* Stop the wakeup timer so it does not restart ULP */ 55 | WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0) 56 | halt 57 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_tsens/ulp_main.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | 3 | extern uint32_t ulp_entry; 4 | extern uint32_t ulp_tsens_value; 5 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_tsens/ulp_tsens.ino: -------------------------------------------------------------------------------- 1 | #include "esp_sleep.h" 2 | #include "nvs.h" 3 | #include "nvs_flash.h" 4 | #include "soc/rtc_cntl_reg.h" 5 | #include "soc/rtc_io_reg.h" 6 | #include "soc/sens_reg.h" 7 | #include "soc/soc.h" 8 | #include "soc/soc_ulp.h" 9 | #include "driver/gpio.h" 10 | #include "driver/rtc_io.h" 11 | #include "esp32/ulp.h" 12 | #include "ulp_main.h" 13 | #include "ulptool.h" 14 | 15 | 16 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 17 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 18 | 19 | static void init_ulp_program() 20 | { 21 | esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start,(ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 22 | ESP_ERROR_CHECK(err); 23 | 24 | /* Set ULP wake up period to 3s */ 25 | ulp_set_wakeup_period(0, 3*1000*1000); 26 | } 27 | 28 | static void print_tsens() 29 | { 30 | Serial.printf("ulp_tsens_value:%d.\r\n",(uint16_t)ulp_tsens_value); 31 | } 32 | 33 | void setup() { 34 | Serial.begin(115200); 35 | esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); 36 | if (cause != ESP_SLEEP_WAKEUP_ULP) { 37 | Serial.printf("Not ULP wakeup, initializing ULP\n"); 38 | init_ulp_program(); 39 | } else { 40 | Serial.printf("ULP wakeup, printing temperature sensor value\n"); 41 | print_tsens(); 42 | } 43 | 44 | Serial.printf("Entering deep sleep\n\n"); 45 | /* Start the ULP program */ 46 | ESP_ERROR_CHECK( ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t))); 47 | ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() ); 48 | esp_deep_sleep_start(); 49 | } 50 | 51 | void loop() { 52 | // not used 53 | } 54 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_watering_device/adc.s: -------------------------------------------------------------------------------- 1 | /* ULP Example: using ADC in deep sleep 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | This file contains assembly code which runs on the ULP. 10 | 11 | ULP wakes up to run this code at a certain period, determined by the values 12 | in SENS_ULP_CP_SLEEP_CYCx_REG registers. On each wake up, the program 13 | measures input voltage on the given ADC channel 'adc_oversampling_factor' 14 | times. Measurements are accumulated and average value is calculated. 15 | Average value is compared to the two thresholds: 'low_thr' and 'high_thr'. 16 | If the value is less than 'low_thr' or more than 'high_thr', ULP wakes up 17 | the chip from deep sleep. 18 | */ 19 | 20 | /* ULP assembly files are passed through C preprocessor first, so include directives 21 | and C macros may be used in these files 22 | */ 23 | #include "soc/rtc_cntl_reg.h" 24 | #include "soc/rtc_io_reg.h" 25 | #include "soc/soc_ulp.h" 26 | 27 | /* ADC1 channel 6, GPIO34 */ 28 | .set adc_channel, 6 29 | 30 | /* Configure the number of ADC samples to average on each measurement. 31 | For convenience, make it a power of 2. */ 32 | .set adc_oversampling_factor_log, 2 33 | .set adc_oversampling_factor, (1 << adc_oversampling_factor_log) 34 | 35 | /* Define variables, which go into .bss section (zero-initialized data) */ 36 | .bss 37 | 38 | /* Low threshold of ADC reading. 39 | Set by the main program. */ 40 | .global low_thr 41 | low_thr: 42 | .long 0 43 | 44 | /* error threshold of ADC reading. 45 | Set by the main program. */ 46 | .global error_thr 47 | error_thr: 48 | .long 0 49 | 50 | /* Counter of measurements done */ 51 | .global sample_counter 52 | sample_counter: 53 | .long 0 54 | 55 | .global last_result 56 | last_result: 57 | .long 0 58 | 59 | /* Code goes into .text section */ 60 | .text 61 | .global entry 62 | entry: 63 | 64 | /* increment sample counter */ 65 | move r3, sample_counter 66 | ld r2, r3, 0 67 | add r2, r2, 1 68 | st r2, r3, 0 69 | 70 | /* do measurements using ADC */ 71 | /* r0 will be used as accumulator */ 72 | move r0, 0 73 | /* initialize the loop counter */ 74 | stage_rst 75 | measure: 76 | /* measure and add value to accumulator */ 77 | adc r1, 0, adc_channel + 1 78 | add r0, r0, r1 79 | /* increment loop counter and check exit condition */ 80 | stage_inc 1 81 | jumps measure, adc_oversampling_factor, lt 82 | 83 | /* divide accumulator by adc_oversampling_factor. 84 | Since it is chosen as a power of two, use right shift */ 85 | rsh r0, r0, adc_oversampling_factor_log 86 | /* averaged value is now in r0; store it into last_result */ 87 | move r3, last_result 88 | st r0, r3, 0 89 | 90 | /* compare with error_thr; wake up if value > error_thr */ 91 | move r3, error_thr 92 | ld r3, r3, 0 93 | sub r3, r3, r0 94 | jump sensor_err, ov 95 | 96 | /* compare with low_thr; wake up if value < low_thr */ 97 | move r3, low_thr 98 | ld r3, r3, 0 99 | sub r3, r0, r3 100 | jump stop_water, ov /* if value < low_thr jump stop_water */ 101 | 102 | .global start_water 103 | start_water: 104 | /* Disable hold of RTC_GPIO10 output */ 105 | WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG,RTC_IO_TOUCH_PAD0_HOLD_S,1,0) 106 | 107 | /* Set the RTC_GPIO10 output HIGH 108 | to signal that ULP is now up */ 109 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG,RTC_GPIO_OUT_DATA_W1TS_S+10,1,1) 110 | 111 | /* Enable hold on RTC_GPIO10 output */ 112 | WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG,RTC_IO_TOUCH_PAD0_HOLD_S,1,1) 113 | 114 | /* stop water and go sleep */ 115 | jump exit /* start pump and go sleep wait next wake up checking ADC value */ 116 | 117 | .global stop_water 118 | stop_water: 119 | /* Disable hold of RTC_GPIO10 output */ 120 | WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG,RTC_IO_TOUCH_PAD0_HOLD_S,1,0) 121 | 122 | /* Set the RTC_GPIO10 output LOW (clear output) 123 | to signal that ULP is now going down */ 124 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG,RTC_GPIO_OUT_DATA_W1TC_S+10,1,1) 125 | 126 | /* Enable hold on RTC_GPIO10 output */ 127 | WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG,RTC_IO_TOUCH_PAD0_HOLD_S,1,1) 128 | 129 | jump exit /* stop water and go sleep */ 130 | 131 | .global sensor_err 132 | sensor_err: 133 | /* Disable hold of RTC_GPIO10 output */ 134 | WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG,RTC_IO_TOUCH_PAD0_HOLD_S,1,0) 135 | 136 | /* Set the RTC_GPIO10 output LOW (clear output) 137 | to signal that ULP is now going down */ 138 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG,RTC_GPIO_OUT_DATA_W1TC_S+10,1,1) 139 | 140 | /* Enable hold on RTC_GPIO10 output */ 141 | WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG,RTC_IO_TOUCH_PAD0_HOLD_S,1,1) 142 | 143 | jump wake_up /* sensor error waku up */ 144 | 145 | /* value within range, end the program */ 146 | .global exit 147 | exit: 148 | halt 149 | 150 | .global wake_up 151 | wake_up: 152 | /* Check if the system can be woken up */ 153 | READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1) 154 | and r0, r0, 1 155 | jump exit, eq 156 | 157 | /* Wake up the SoC, end program */ 158 | wake 159 | WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0) 160 | halt 161 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_watering_device/ulp_main.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | 3 | extern uint32_t ulp_entry; 4 | extern uint32_t ulp_sample_counter; 5 | extern uint32_t ulp_low_thr; 6 | extern uint32_t ulp_error_thr; 7 | extern uint32_t ulp_last_result; 8 | -------------------------------------------------------------------------------- /src/ulp_examples/ulp_watering_device/ulp_watering_device.ino: -------------------------------------------------------------------------------- 1 | #include "nvs.h" 2 | #include "nvs_flash.h" 3 | #include "soc/rtc_cntl_reg.h" 4 | #include "soc/sens_reg.h" 5 | #include "driver/gpio.h" 6 | #include "driver/rtc_io.h" 7 | #include "driver/adc.h" 8 | #include "driver/dac.h" 9 | #include "esp32/ulp.h" 10 | #include "esp_sleep.h" 11 | #include "ulp_main.h" 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "ulptool.h" 15 | 16 | #define ERROR_INDICATE_LED 16 17 | #define RELAY_CONTROLS_PIN 4 18 | 19 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 20 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 21 | 22 | /* This function is called once after power-on reset, to load ULP program into 23 | RTC memory and configure the ADC. 24 | */ 25 | static void init_ulp_program(); 26 | 27 | /* This function is called every time before going into deep sleep. 28 | It starts the ULP program and resets measurement counter. 29 | */ 30 | static void start_ulp_program(); 31 | 32 | void setup() { 33 | Serial.begin(115200); 34 | while(!Serial.available()); 35 | esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); 36 | if (cause != ESP_SLEEP_WAKEUP_ULP) { 37 | Serial.printf("Not ULP wakeup\n"); 38 | init_ulp_program(); 39 | } else { 40 | Serial.printf("Deep sleep wakeup\n"); 41 | Serial.printf("ULP did %d measurements since last reset\n", ulp_sample_counter & UINT16_MAX); 42 | Serial.printf("Thresholds: low_thr=%d error_thr=%d\n", ulp_low_thr, ulp_error_thr); 43 | ulp_last_result &= UINT16_MAX; 44 | Serial.printf("adc Value=%d \n", ulp_last_result); 45 | 46 | /* if sensor error, LED blink 5 times */ 47 | if (ulp_last_result > 4000) 48 | { 49 | rtc_gpio_hold_dis((gpio_num_t)ERROR_INDICATE_LED); 50 | for (uint32_t i = 0; i < 10; i++) 51 | { 52 | if (i % 2 == 0) { 53 | rtc_gpio_set_level((gpio_num_t)ERROR_INDICATE_LED, 1); 54 | } else { 55 | rtc_gpio_set_level((gpio_num_t)ERROR_INDICATE_LED, 0); 56 | } 57 | vTaskDelay(50); 58 | } 59 | rtc_gpio_hold_en((gpio_num_t)ERROR_INDICATE_LED); 60 | } 61 | } 62 | Serial.printf("Entering deep sleep\n\n"); 63 | start_ulp_program(); 64 | ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() ); 65 | esp_deep_sleep_start(); 66 | } 67 | 68 | void loop() { 69 | // not used 70 | } 71 | 72 | static void init_ulp_program() 73 | { 74 | esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, 75 | (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 76 | ESP_ERROR_CHECK(err); 77 | 78 | /* Configure ADC channel */ 79 | /* Note: when changing channel here, also change 'adc_channel' constant 80 | in adc.S */ 81 | adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11); 82 | adc1_config_width(ADC_WIDTH_BIT_12); 83 | adc1_ulp_enable(); 84 | 85 | /* configure sensor indicate LED */ 86 | rtc_gpio_init((gpio_num_t)ERROR_INDICATE_LED); 87 | rtc_gpio_set_direction((gpio_num_t)ERROR_INDICATE_LED, RTC_GPIO_MODE_OUTPUT_ONLY); 88 | rtc_gpio_set_level((gpio_num_t)ERROR_INDICATE_LED, 0); 89 | 90 | /* configure relay control pin */ 91 | rtc_gpio_init((gpio_num_t)RELAY_CONTROLS_PIN); 92 | rtc_gpio_set_direction((gpio_num_t)RELAY_CONTROLS_PIN, RTC_GPIO_MODE_OUTPUT_ONLY); 93 | 94 | /* Set low and high thresholds, approx. 1.35V - 1.75V*/ 95 | ulp_low_thr = 2000; 96 | ulp_error_thr = 4000; 97 | 98 | /* Set ULP wake up period to 3s */ 99 | ulp_set_wakeup_period(0, 3 * 100 * 1000); 100 | 101 | } 102 | 103 | static void start_ulp_program() 104 | { 105 | /* Reset sample counter */ 106 | ulp_sample_counter = 0; 107 | 108 | /* Start the program */ 109 | esp_err_t err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)); 110 | ESP_ERROR_CHECK(err); 111 | } 112 | -------------------------------------------------------------------------------- /src/ulp_examples/ulpcc/ulpcc_adc/adc.c: -------------------------------------------------------------------------------- 1 | #ifdef _ULPCC_ // do not add code above this line 2 | // must include ulpcc helper functions 3 | #include 4 | #include "common.h" 5 | 6 | // ADC1 channel 6, GPIO34 7 | #define adc_channel 6 8 | 9 | // threshold of ADC reading. Set by the main program. 10 | unsigned threshold; 11 | /* 12 | * esp32 main processor can reach this variable. 13 | * It is used to signal that a result is ready 14 | * and when the main processor read the value. 15 | */ 16 | unsigned mutex = 0; 17 | 18 | // # of samples 19 | unsigned sample_counter = 0; 20 | /* 21 | * ulp registers are 32 bits but can 22 | * only hold 16 bits of data. We use 23 | * sum_hi (upper 16-bits) and sum_lo 24 | * (lower 16-bits) to hold 32 bits 25 | * of data. The averaged data is computed 26 | * from this proto 32-bit number. 27 | */ 28 | unsigned sum_lo = 0; 29 | unsigned sum_hi = 0; 30 | unsigned adc_avg_result = 0; 31 | 32 | void entry() { 33 | unsigned tmp = 0, i = 0; 34 | sum_hi = 0; 35 | sum_lo = 0; 36 | // avg routine 37 | for (i = 0; i < adc_oversampling_factor; i++) { 38 | // get adc reading 39 | tmp = adc(0, adc_channel + 1); 40 | //check if sum_lo + tmp will overflow 41 | if (tmp > (0xFFFF - sum_lo)) { 42 | // update the high 16 bits 43 | sum_hi += 1; 44 | } 45 | // update low 16 bits 46 | sum_lo += tmp; 47 | } 48 | // calculate get average from the proto 32 bit number 49 | adc_avg_result = (sum_hi << 10) + (sum_lo >> 6); 50 | 51 | // if adc_avg_result is greater than threshold signal main processor 52 | if (adc_avg_result > threshold) { 53 | sample_counter++; 54 | mutex = 1; 55 | wake(); 56 | // wait until main processor has read the result 57 | while (mutex); 58 | } 59 | } 60 | #endif // do not add code after here 61 | -------------------------------------------------------------------------------- /src/ulp_examples/ulpcc/ulpcc_adc/common.h: -------------------------------------------------------------------------------- 1 | // Number of ADC samples to average on each measurement. 2 | #define adc_oversampling_factor 128 3 | -------------------------------------------------------------------------------- /src/ulp_examples/ulpcc/ulpcc_adc/ulp_main.h: -------------------------------------------------------------------------------- 1 | /* 2 | Put your ULP globals here you want visibility 3 | for your sketch. Add "ulp_" to the beginning 4 | of the variable name and must be size 'uint32_t' 5 | */ 6 | #include "Arduino.h" 7 | 8 | extern uint32_t ulp_entry; 9 | extern uint32_t ulp_adc_avg_result; 10 | extern uint32_t ulp_sample_counter; 11 | extern uint32_t ulp_threshold; 12 | extern uint32_t ulp_mutex; 13 | -------------------------------------------------------------------------------- /src/ulp_examples/ulpcc/ulpcc_adc/ulpcc_adc.ino: -------------------------------------------------------------------------------- 1 | #include "esp_sleep.h" 2 | #include "driver/rtc_io.h" 3 | #include "driver/adc.h" 4 | #include "esp32/ulp.h" 5 | #include "ulp_main.h" 6 | #include "ulptool.h" 7 | #include "common.h" 8 | 9 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 10 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 11 | 12 | static void init_ulp_program(); 13 | 14 | void setup() { 15 | Serial.begin(115200); 16 | delay(2000); 17 | Serial.println("ULP Light Sleep adc threshold wakeup example"); 18 | delay(100); 19 | // wake ulp every 10 msec 20 | init_run_ulp(1000 * 10); 21 | ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() ); 22 | } 23 | 24 | void loop() { 25 | // light sleep until adc thershold is exceeded 26 | esp_light_sleep_start(); 27 | // check for the ulp is waiting for the main processor to read the adc result 28 | if (ulp_mutex) { 29 | Serial.printf("counter: %4i | oversample factor: %3i | adc avg: %i\n", 30 | ulp_sample_counter & 0xffff, 31 | adc_oversampling_factor, 32 | ulp_adc_avg_result & 0xffff 33 | ); 34 | Serial.flush(); 35 | // tell ulp we are done reading result 36 | ulp_mutex = false; 37 | } 38 | } 39 | 40 | static void init_run_ulp(uint32_t usec) { 41 | adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_0); 42 | adc1_config_width(ADC_WIDTH_BIT_12); 43 | adc1_ulp_enable(); 44 | //rtc_gpio_pullup_dis(GPIO_NUM_15); 45 | //rtc_gpio_hold_en(GPIO_NUM_15); 46 | ulp_set_wakeup_period(0, usec); 47 | esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 48 | // all shared ulp variables have to be intialized after ulptool_load_binary for the ulp to see it. 49 | // Set the high threshold reading you want the ulp to trigger a main processor wakeup. 50 | ulp_threshold = 0x20; 51 | err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)); 52 | if (err) Serial.println("Error Starting ULP Coprocessor"); 53 | } 54 | -------------------------------------------------------------------------------- /src/ulp_examples/ulpcc/ulpcc_counter/ulp_counter.c: -------------------------------------------------------------------------------- 1 | #ifdef _ULPCC_ // do not add code above this line 2 | // must include ulpcc helper functions 3 | #include 4 | 5 | // global variable that the main processor can see 6 | unsigned counter = 0; 7 | 8 | // all ulpcc programs have have this function 9 | void entry() { 10 | // increment counter 11 | counter++; 12 | } 13 | #endif // do not add code after here 14 | -------------------------------------------------------------------------------- /src/ulp_examples/ulpcc/ulpcc_counter/ulp_main.h: -------------------------------------------------------------------------------- 1 | /* 2 | Put your ULP globals here you want visibility 3 | for your sketch. Add "ulp_" to the beginning 4 | of the variable name and must be size 'uint32_t' 5 | */ 6 | #include "Arduino.h" 7 | 8 | extern uint32_t ulp_entry; 9 | extern unsigned ulp_counter; 10 | -------------------------------------------------------------------------------- /src/ulp_examples/ulpcc/ulpcc_counter/ulpcc_counter.ino: -------------------------------------------------------------------------------- 1 | #include "esp32/ulp.h" 2 | // include ulp header you will create 3 | #include "ulp_main.h" 4 | // must include ulptool helper functions also 5 | #include "ulptool.h" 6 | 7 | // Unlike the esp-idf always use these binary blob names 8 | extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); 9 | extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); 10 | 11 | static void init_run_ulp(uint32_t usec); 12 | 13 | void setup() { 14 | Serial.begin(115200); 15 | delay(1000); 16 | init_run_ulp(100 * 1000); // 100 msec 17 | } 18 | 19 | void loop() { 20 | // ulp variables are 32-bit but only the bottom 16-bits hold data 21 | Serial.printf("Count: %i\n", ulp_counter & 0xFFFF); 22 | delay(100); 23 | } 24 | 25 | static void init_run_ulp(uint32_t usec) { 26 | ulp_set_wakeup_period(0, usec); 27 | esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); 28 | err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)); 29 | 30 | if (err) Serial.println("Error Starting ULP Coprocessor"); 31 | } 32 | -------------------------------------------------------------------------------- /src/ulpcc/CPYRIGHT: -------------------------------------------------------------------------------- 1 | The authors of this software are Christopher W. Fraser and 2 | David R. Hanson. 3 | 4 | Copyright (c) 1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002 5 | by AT&T, Christopher W. Fraser, and David R. Hanson. All Rights Reserved. 6 | 7 | Permission to use, copy, modify, and distribute this software for any 8 | purpose, subject to the provisions described below, without fee is 9 | hereby granted, provided that this entire notice is included in all 10 | copies of any software that is or includes a copy or modification of 11 | this software and in all copies of the supporting documentation for 12 | such software. 13 | 14 | THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED 15 | WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY 16 | REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY 17 | OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. 18 | 19 | lcc is not public-domain software, shareware, and it is not protected 20 | by a `copyleft' agreement, like the code from the Free Software 21 | Foundation. 22 | 23 | lcc is available free for your personal research and instructional use 24 | under the `fair use' provisions of the copyright law. You may, however, 25 | redistribute lcc in whole or in part provided you acknowledge its 26 | source and include this CPYRIGHT file. You may, for example, include 27 | the distribution in a CDROM of free software, provided you charge only 28 | for the media, or mirror the distribution files at your site. 29 | 30 | You may not sell lcc or any product derived from it in which it is a 31 | significant part of the value of the product. Using the lcc front end 32 | to build a C syntax checker is an example of this kind of product. 33 | 34 | You may use parts of lcc in products as long as you charge for only 35 | those components that are entirely your own and you acknowledge the use 36 | of lcc clearly in all product documentation and distribution media. You 37 | must state clearly that your product uses or is based on parts of lcc 38 | and that lcc is available free of charge. You must also request that 39 | bug reports on your product be reported to you. Using the lcc front 40 | end to build a C compiler for the Motorola 88000 chip and charging for 41 | and distributing only the 88000 code generator is an example of this 42 | kind of product. 43 | 44 | Using parts of lcc in other products is more problematic. For example, 45 | using parts of lcc in a C++ compiler could save substantial time and 46 | effort and therefore contribute significantly to the profitability of 47 | the product. This kind of use, or any use where others stand to make a 48 | profit from what is primarily our work, requires a license agreement 49 | with Addison-Wesley. Per-copy and unlimited use licenses are 50 | available; for more information, contact 51 | 52 | Kim Boedigheimer 53 | Addison Wesley 54 | 75 Arlington St., Suite 300 55 | Boston, MA 02116 56 | 617/848-6559 kim.boedigheimer@pearsoned.com 57 | ----- 58 | Chris Fraser / cwf@aya.yale.edu 59 | David Hanson / drh@drhanson.net 60 | $Revision$ $Date$ 61 | -------------------------------------------------------------------------------- /src/ulpcc/bin/darwin/bprint: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duff2013/ulptool/dd9def8146dbbe6a3748725bb01c9e8af33ea9c6/src/ulpcc/bin/darwin/bprint -------------------------------------------------------------------------------- /src/ulpcc/bin/darwin/cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duff2013/ulptool/dd9def8146dbbe6a3748725bb01c9e8af33ea9c6/src/ulpcc/bin/darwin/cpp -------------------------------------------------------------------------------- /src/ulpcc/bin/darwin/lburg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duff2013/ulptool/dd9def8146dbbe6a3748725bb01c9e8af33ea9c6/src/ulpcc/bin/darwin/lburg -------------------------------------------------------------------------------- /src/ulpcc/bin/darwin/lcc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duff2013/ulptool/dd9def8146dbbe6a3748725bb01c9e8af33ea9c6/src/ulpcc/bin/darwin/lcc -------------------------------------------------------------------------------- /src/ulpcc/bin/darwin/rcc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duff2013/ulptool/dd9def8146dbbe6a3748725bb01c9e8af33ea9c6/src/ulpcc/bin/darwin/rcc -------------------------------------------------------------------------------- /src/ulpcc/bin/linux/bprint: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duff2013/ulptool/dd9def8146dbbe6a3748725bb01c9e8af33ea9c6/src/ulpcc/bin/linux/bprint -------------------------------------------------------------------------------- /src/ulpcc/bin/linux/cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duff2013/ulptool/dd9def8146dbbe6a3748725bb01c9e8af33ea9c6/src/ulpcc/bin/linux/cpp -------------------------------------------------------------------------------- /src/ulpcc/bin/linux/lburg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duff2013/ulptool/dd9def8146dbbe6a3748725bb01c9e8af33ea9c6/src/ulpcc/bin/linux/lburg -------------------------------------------------------------------------------- /src/ulpcc/bin/linux/lcc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duff2013/ulptool/dd9def8146dbbe6a3748725bb01c9e8af33ea9c6/src/ulpcc/bin/linux/lcc -------------------------------------------------------------------------------- /src/ulpcc/bin/linux/rcc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duff2013/ulptool/dd9def8146dbbe6a3748725bb01c9e8af33ea9c6/src/ulpcc/bin/linux/rcc -------------------------------------------------------------------------------- /src/ulpcc/include/soc_ulp_c.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #pragma once 15 | 16 | // This file contains various convenience macros to be used in ULP programs. 17 | 18 | // Helper macros to calculate bit field width from mask, using the preprocessor. 19 | // Used later in READ_RTC_FIELD and WRITE_RTC_FIELD. 20 | #define IS_BIT_SET(m, i) (((m) >> (i)) & 1) 21 | #define MASK_TO_WIDTH_HELPER1(m, i) IS_BIT_SET(m, i) 22 | #define MASK_TO_WIDTH_HELPER2(m, i) (MASK_TO_WIDTH_HELPER1(m, i) + MASK_TO_WIDTH_HELPER1(m, i + 1)) 23 | #define MASK_TO_WIDTH_HELPER4(m, i) (MASK_TO_WIDTH_HELPER2(m, i) + MASK_TO_WIDTH_HELPER2(m, i + 2)) 24 | #define MASK_TO_WIDTH_HELPER8(m, i) (MASK_TO_WIDTH_HELPER4(m, i) + MASK_TO_WIDTH_HELPER4(m, i + 4)) 25 | #define MASK_TO_WIDTH_HELPER16(m, i) (MASK_TO_WIDTH_HELPER8(m, i) + MASK_TO_WIDTH_HELPER8(m, i + 8)) 26 | #define MASK_TO_WIDTH_HELPER32(m, i) (MASK_TO_WIDTH_HELPER16(m, i) + MASK_TO_WIDTH_HELPER16(m, i + 16)) 27 | 28 | // Peripheral register access macros, build around REG_RD and REG_WR instructions. 29 | // Registers defined in rtc_cntl_reg.h, rtc_io_reg.h, sens_reg.h, and rtc_i2c_reg.h are usable with these macros. 30 | 31 | // Read from rtc_reg[low_bit + bit_width - 1 : low_bit] into R0, bit_width <= 16 32 | #define READ_RTC_REG(rtc_reg, low_bit, bit_width) \ 33 | reg_rd((((rtc_reg) - DR_REG_RTCCNTL_BASE) / 4), ((low_bit) + (bit_width) - 1), (low_bit)) 34 | 35 | // Write immediate value into rtc_reg[low_bit + bit_width - 1 : low_bit], bit_width <= 8 36 | #define WRITE_RTC_REG(rtc_reg, low_bit, bit_width, value) \ 37 | reg_wr((((rtc_reg) - DR_REG_RTCCNTL_BASE) / 4), ((low_bit) + (bit_width) - 1), (low_bit), ((value) & 0xff)) 38 | 39 | // Read from a field in rtc_reg into R0, up to 16 bits 40 | #define READ_RTC_FIELD(rtc_reg, field) \ 41 | READ_RTC_REG(rtc_reg, field ## _S, MASK_TO_WIDTH_HELPER16(field ## _V, 0)) 42 | 43 | // Write immediate value into a field in rtc_reg, up to 8 bits 44 | #define WRITE_RTC_FIELD(rtc_reg, field, value) \ 45 | WRITE_RTC_REG(rtc_reg, field ## _S, MASK_TO_WIDTH_HELPER8(field ## _V, 0), ((value) & field ## _V)) 46 | -------------------------------------------------------------------------------- /src/ulpcc/include/ulp_c.h: -------------------------------------------------------------------------------- 1 | /* Header file for pseudo-functions that the compiler will convert to assembler instructions */ 2 | 3 | /* This simplifies what the following headers pull in */ 4 | #define __ASSEMBLER__ 5 | 6 | #include "soc/rtc_cntl_reg.h" 7 | #include "soc/rtc_io_reg.h" 8 | #include "soc_ulp_c.h" 9 | 10 | unsigned adc(unsigned sar_sel, unsigned mux); 11 | void halt(); 12 | void i2c_rd(unsigned sub_addr, unsigned high, unsigned low, unsigned slave_sel); 13 | unsigned i2c_wr(unsigned sub_addr, unsigned value, unsigned high, unsigned low, unsigned slave_sel); 14 | unsigned reg_rd(unsigned addr, unsigned high, unsigned low); 15 | void reg_wr(unsigned addr, unsigned high, unsigned low, unsigned data); 16 | void sleep(unsigned sleep_reg); 17 | unsigned tsens(unsigned wait_delay); 18 | void wait(unsigned cycles); 19 | void wake(); 20 | #define wake_when_ready() do {while (0 == (READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) & 1)); wake(); halt();} while(0) 21 | 22 | --------------------------------------------------------------------------------