mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2026-01-31 14:51:02 +03:00
Merge branch 'jomjol:master' into master
This commit is contained in:
@@ -11,6 +11,22 @@
|
||||
|
||||
____
|
||||
|
||||
#### #27 Use Homie Spec for Mqtt binding
|
||||
|
||||
* Use the standardized Home Protocol for the Mqtt binding
|
||||
* https://homieiot.github.io/
|
||||
|
||||
#### #26 Changes behaviour for "N" replacement
|
||||
|
||||
* in case the higher digits has already increased by minium 1 - don't set the "N" to the last value, but to "0"
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/792
|
||||
|
||||
|
||||
#### #25 Trigger Measurement via MQTT
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/727
|
||||
|
||||
|
||||
#### #24 Show Mqtt state directly in Webserver
|
||||
|
||||
* Show MQTT log in Web page. E.g. connection established or failed to connect...
|
||||
@@ -48,18 +64,15 @@ ____
|
||||
|
||||
|
||||
|
||||
#### #18 Document WLAN-strength in web page
|
||||
#### ~~#18 Document WLAN-strength in web page~~
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/563
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/563~~
|
||||
|
||||
|
||||
|
||||
#### #17 Direct InfluxDB connection
|
||||
#### ~~#17 Direct InfluxDB connection~~
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/534
|
||||
* Direct interface to a InfluxDB data base
|
||||
* Integrate InfluxDB interface in firmware
|
||||
* Adapt html web page for configuration
|
||||
* ~~Done in v10.6.0~~
|
||||
|
||||
|
||||
#### #16 Serial Communication
|
||||
@@ -101,9 +114,9 @@ ____
|
||||
|
||||
|
||||
|
||||
#### #12 Less reboots due to memory leakage
|
||||
#### ~~#12 Less reboots due to memory leakage~~
|
||||
|
||||
* Issue: #414 & #425 #430
|
||||
* ~~Issue: #414 & #425 #430~~
|
||||
|
||||
|
||||
|
||||
@@ -222,4 +235,4 @@ ____
|
||||
|
||||
* ~~Implementation of a software module for external light source (e.g. WS8132 LED controller, ...)~~
|
||||
* ~~Update of the camera module to use the external light instead of the internal flash light~~
|
||||
* ~~Adopt the configuration algorithm with a configurable light source~~
|
||||
* ~~Adopt the configuration algorithm with a configurable light source~~
|
||||
|
||||
24
README.md
24
README.md
@@ -52,9 +52,31 @@ In other cases you can contact the developer via email: <img src="https://raw.gi
|
||||
|
||||
|
||||
|
||||
##### 10.6.0 - Stability Increase (2022-07-17)
|
||||
|
||||
- IndluxDB: direct injection into InfluxDB - thanks to **[wetneb](https://github.com/wetneb)**
|
||||
|
||||
- MQTT: implemented "Retain Flag" and extend with absolute Change (in addition to rate)
|
||||
|
||||
- `config.ini`: removal of modelsize (readout from tflite)
|
||||
|
||||
- Updated analog neural network file (`ana1000s2.tflite`) & digital neural network file (`dig1400s2q.tflite`)
|
||||
|
||||
- TFMicro/Lite: Update (espressif Version 20220716)
|
||||
|
||||
- Updated esp32cam (v20220716)
|
||||
|
||||
- ESP-IDF: Update to 4.4
|
||||
|
||||
- Internal update (CNN algorithm optimizations, reparation for new neural network type)
|
||||
|
||||
- Bug Fix: no time with fixed IP, Postprocessing, MQTT
|
||||
|
||||
|
||||
|
||||
##### 10.5.2 - Stability Increase (2022-02-22)
|
||||
|
||||
- **NEW 10.5.2:** Bug Fix: wrong `firmware.bin` (no rate update)
|
||||
- NEW 10.5.2: Bug Fix: wrong `firmware.bin` (no rate update)
|
||||
- NEW 10.5.1: Bug Fix: wrong return value, rate value & PreValue status, HTML: SSID & IP were not displayed
|
||||
- MQTT: changed wifi naming to "wifiRSSI"
|
||||
- HTML: check selectable values for consistency
|
||||
|
||||
57
code/components/esp-nn/.gitignore
vendored
Normal file
57
code/components/esp-nn/.gitignore
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
.config
|
||||
*.o
|
||||
*.i
|
||||
*.s
|
||||
*.orig
|
||||
*.pyc
|
||||
|
||||
# gtags
|
||||
GTAGS
|
||||
GRTAGS
|
||||
GPATH
|
||||
|
||||
# emacs
|
||||
.dir-locals.el
|
||||
|
||||
# emacs temp file suffixes
|
||||
*~
|
||||
.#*
|
||||
\#*#
|
||||
|
||||
# eclipse setting
|
||||
.settings
|
||||
|
||||
# MacOS directory files
|
||||
.DS_Store
|
||||
|
||||
# Example project files
|
||||
examples/**/sdkconfig
|
||||
examples/**/sdkconfig.old
|
||||
examples/**/build
|
||||
|
||||
# Test app files
|
||||
test_app/build
|
||||
test_app/sdkconfig
|
||||
test_app/sdkconfig.old
|
||||
|
||||
# Doc build artifacts
|
||||
docs/_build/
|
||||
docs/doxygen-warning-log.txt
|
||||
docs/sphinx-warning-log.txt
|
||||
docs/sphinx-warning-log-sanitized.txt
|
||||
docs/xml/
|
||||
docs/xml_in/
|
||||
docs/man/
|
||||
docs/doxygen_sqlite3.db
|
||||
|
||||
TEST_LOGS
|
||||
|
||||
|
||||
# gcov coverage reports
|
||||
*.gcda
|
||||
*.gcno
|
||||
coverage.info
|
||||
coverage_report/
|
||||
|
||||
# VS Code Settings
|
||||
.vscode/
|
||||
55
code/components/esp-nn/.gitlab-ci.yml
Normal file
55
code/components/esp-nn/.gitlab-ci.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
stages:
|
||||
- build
|
||||
|
||||
variables:
|
||||
BATCH_BUILD: "1"
|
||||
V: "0"
|
||||
MAKEFLAGS: "-j8 --no-keep-going"
|
||||
IDF_PATH: "$CI_PROJECT_DIR/esp-idf"
|
||||
LOG_PATH: "$CI_PROJECT_DIR"
|
||||
|
||||
.set_git_config: &set_git_config
|
||||
# Set git config
|
||||
- git config user.email "test@espressif.com"
|
||||
- git config user.name "Espressif"
|
||||
|
||||
.add_ssh_key: &add_ssh_key
|
||||
# Add gitlab ssh key
|
||||
- mkdir -p ~/.ssh
|
||||
- chmod 700 ~/.ssh
|
||||
- echo -n $GITLAB_KEY > ~/.ssh/id_rsa_base64
|
||||
- base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa
|
||||
- chmod 600 ~/.ssh/id_rsa
|
||||
- echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
|
||||
|
||||
before_script:
|
||||
# Add gitlab ssh key
|
||||
- *add_ssh_key
|
||||
# Set git config
|
||||
- *set_git_config
|
||||
|
||||
.build_esp32s3: &build_esp32s3
|
||||
- idf.py set-target esp32s3 build
|
||||
|
||||
.build_esp32: &build_esp32
|
||||
- idf.py set-target esp32 build
|
||||
|
||||
build_demo:
|
||||
stage: build
|
||||
image: $CI_DOCKER_REGISTRY/esp32-ci-env:esp-nn
|
||||
tags:
|
||||
- build
|
||||
script:
|
||||
# Clone IDF
|
||||
- git clone --recursive --single-branch -b release/v4.4 --reference-if-able /local_references/gitlab/ https://gitlab-ci-token:${BOT_TOKEN}@gitlab.espressif.cn:6688/espressif/esp-idf.git
|
||||
- cd esp-idf
|
||||
- ./install.sh
|
||||
- . ./export.sh
|
||||
- cd ..
|
||||
# Build examples now
|
||||
- cd test_app
|
||||
# Build esp32s3
|
||||
- *build_esp32s3
|
||||
# Build esp32
|
||||
- *build_esp32
|
||||
- cd -
|
||||
50
code/components/esp-nn/CMakeLists.txt
Normal file
50
code/components/esp-nn/CMakeLists.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
idf_build_get_property(idf_target IDF_TARGET)
|
||||
|
||||
set(c_srcs
|
||||
"src/activation_functions/esp_nn_relu_ansi.c"
|
||||
"src/basic_math/esp_nn_add_ansi.c"
|
||||
"src/basic_math/esp_nn_mul_ansi.c"
|
||||
"src/convolution/esp_nn_conv_ansi.c"
|
||||
"src/convolution/esp_nn_conv_opt.c"
|
||||
"src/convolution/esp_nn_depthwise_conv_ansi.c"
|
||||
"src/convolution/esp_nn_depthwise_conv_opt.c"
|
||||
"src/fully_connected/esp_nn_fully_connected_ansi.c"
|
||||
"src/softmax/esp_nn_softmax_ansi.c"
|
||||
"src/softmax/esp_nn_softmax_opt.c"
|
||||
"src/pooling/esp_nn_avg_pool_ansi.c"
|
||||
"src/pooling/esp_nn_max_pool_ansi.c")
|
||||
|
||||
if(CONFIG_IDF_TARGET_ESP32S3)
|
||||
set(s3_srcs
|
||||
"src/common/esp_nn_common_functions_esp32s3.S"
|
||||
"src/common/esp_nn_multiply_by_quantized_mult_esp32s3.S"
|
||||
"src/common/esp_nn_multiply_by_quantized_mult_ver1_esp32s3.S"
|
||||
"src/activation_functions/esp_nn_relu_s8_esp32s3.S"
|
||||
"src/basic_math/esp_nn_add_s8_esp32s3.S"
|
||||
"src/basic_math/esp_nn_mul_s8_esp32s3.S"
|
||||
"src/convolution/esp_nn_conv_esp32s3.c"
|
||||
"src/convolution/esp_nn_depthwise_conv_s8_esp32s3.c"
|
||||
"src/convolution/esp_nn_conv_s16_mult8_esp32s3.S"
|
||||
"src/convolution/esp_nn_conv_s8_mult8_1x1_esp32s3.S"
|
||||
"src/convolution/esp_nn_conv_s16_mult4_1x1_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult1_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult1_3x3_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult1_3x3_no_pad_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult8_3x3_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult4_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult8_esp32s3.S"
|
||||
"src/fully_connected/esp_nn_fully_connected_s8_esp32s3.S"
|
||||
"src/pooling/esp_nn_max_pool_s8_esp32s3.S"
|
||||
"src/pooling/esp_nn_avg_pool_s8_esp32s3.S")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "${c_srcs}"
|
||||
"${s3_srcs}"
|
||||
INCLUDE_DIRS "include" "src/common")
|
||||
|
||||
if(CONFIG_IDF_TARGET_ESP32S3)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -mlongcalls -fno-unroll-loops -O2 -Wno-unused-function)
|
||||
else()
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-function)
|
||||
endif()
|
||||
29
code/components/esp-nn/Kconfig.projbuild
Normal file
29
code/components/esp-nn/Kconfig.projbuild
Normal file
@@ -0,0 +1,29 @@
|
||||
menu "ESP-NN"
|
||||
|
||||
choice NN_OPTIMIZATIONS
|
||||
bool "Optimization for nn functions"
|
||||
default NN_OPTIMIZED
|
||||
help
|
||||
Use ANSI-C versions for verification and debug purpose.
|
||||
Optimisations are automatically picked up for a chipset.
|
||||
For ESP32-S3, assembly optimisations are selected.
|
||||
For other platforms(viz., ESP32, ESP32-C3), generic optimisations are used.
|
||||
|
||||
config NN_ANSI_C
|
||||
bool "ANSI C"
|
||||
help
|
||||
ANSI C versions for verification and debug purposes.
|
||||
config NN_OPTIMIZED
|
||||
bool "Optimized versions"
|
||||
help
|
||||
Optimisations are automatically picked up for a chipset.
|
||||
For ESP32-S3, assembly optimisations are selected.
|
||||
For other platforms(viz., ESP32, ESP32-C3), generic optimisations are used.
|
||||
endchoice
|
||||
|
||||
config NN_OPTIMIZATIONS
|
||||
int
|
||||
default 0 if NN_ANSI_C
|
||||
default 1 if NN_OPTIMIZED
|
||||
|
||||
endmenu
|
||||
202
code/components/esp-nn/LICENSE
Normal file
202
code/components/esp-nn/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
55
code/components/esp-nn/README.md
Normal file
55
code/components/esp-nn/README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# ESP-NN
|
||||
|
||||
The library contains optimised NN (Neural Network) functions for various Espressif chipsets.
|
||||
|
||||
* Supported platforms:
|
||||
* TensorFlow Lite Micro (TFLite Micro). Repo can be found [here](https://github.com/espressif/tflite-micro-esp-examples)
|
||||
|
||||
* Supported ESP chipsets include:
|
||||
* ESP32-S3 (Assembly versions optimised to benefit from vector instructions of ESP32-S3)
|
||||
* ESP32 (Generic optimisations)
|
||||
* ESP32-C3 (Generic optimisations)
|
||||
|
||||
## Performance
|
||||
|
||||
### Kernelwise performance for s8 versions:
|
||||
|
||||
* Kernelwise performance on ESP32-S3 chip
|
||||
* Numbers are ticks taken for kernel to execute
|
||||
* Chip config: 240MHz, SPI: QPI 80MHz, Data cache: 64KB
|
||||
|
||||
| Function | ANSI C | ESP32-S3 Opt | Opt Ratio | Data info | Memory |
|
||||
| ----------------| --------|---------|---------|-------------|-----------|
|
||||
| elementwise_add | 320397 | 87119 | 3.68 | size = 1615 | External |
|
||||
| elementwise_mul | 125958 | 44239 | 2.85 | size = 1615 | External |
|
||||
| convolution | 4663012 | 428675 | 10.88 | input(10,10), filter(64x1x1x64) | External |
|
||||
| convolution | 301014 | 32433 | 9.28 | input(8,8), filter(16x1x1x16) | External |
|
||||
| convolution | 2115418 | 1020923 | 2.07 | input(10,10), filter(64x3x3x3) | External |
|
||||
| depthwise conv | 1190062 | 203278 | 5.85 | input (18, 18), pad(0,0), stride(1,1) filter: 1x3x3x16 | External |
|
||||
| depthwise conv | 837072 | 182335 | 4.59 | input (12, 12), pad(1,1), stride(1,1) filter: 8x5x5x4 | External |
|
||||
| max pool | 485714 | 76747 | 6.33 | input(16,16), filter (1x3x3x16) | Internal |
|
||||
| avg pool | 541462 | 160580 | 3.37 | input(16,16), filter (1x3x3x16) | Internal |
|
||||
| fully connected | 15853 | 9547 | 1.66 | len: 265, ch = 3 | Internal |
|
||||
| prelu (relu6) | 19472 | 2734 | 7.12 | size, 1615 | Internal |
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
* To configure, please use `idf.py menuconfig` and under `ESP-NN` select `NN_OPTIMIZATIONS`
|
||||
* There are two options presented:
|
||||
* Optimized versions
|
||||
* ANSI C
|
||||
|
||||
* Default selection is for `Optimized versions`. For ESP32-S3, assembly versions are automatically selected, whereas for other chipsets (viz., ESP32, ESP32-C3), generic optimisations are selected.
|
||||
* For debugging purposes, you may want to select `ANSI C` reference versions.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
If you encounter an issue with ESP-NN, or wish to submit a feature request, please use the Issues section on the Github.
|
||||
|
||||
For general questions related to this library, please use the esp32.com forum.
|
||||
|
||||
## Copyrights and License
|
||||
|
||||
All original source code in this repository is Copyright (C) 2020-2021 Espressif Systems. This source code is licensed under the Apache License 2.0 as described in the file LICENSE.
|
||||
46
code/components/esp-nn/include/esp_nn.h
Normal file
46
code/components/esp-nn/include/esp_nn.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(CONFIG_NN_OPTIMIZED)
|
||||
// select apt optimisations
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
||||
#define ARCH_ESP32_S3 1
|
||||
#endif
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
#define ARCH_ESP32 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* reference kernels included by default */
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
#if defined(CONFIG_NN_OPTIMIZED)
|
||||
#if defined(ARCH_ESP32_S3)
|
||||
#include "esp_nn_esp32s3.h"
|
||||
#else // for other platforms use generic optimisations
|
||||
#include "esp_nn_generic_opt.h"
|
||||
#endif // #if defined(ARCH_ESP32_S3)
|
||||
#else
|
||||
#include "esp_nn_ansi_c.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
47
code/components/esp-nn/include/esp_nn_ansi_c.h
Normal file
47
code/components/esp-nn/include/esp_nn_ansi_c.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @file Header definitions to include for ANSI C versions.
|
||||
* These are just typedefs to pick up ANSI versions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
#define esp_nn_add_elementwise_s8 esp_nn_add_elementwise_s8_ansi
|
||||
#define esp_nn_mul_elementwise_s8 esp_nn_mul_elementwise_s8_ansi
|
||||
|
||||
#define esp_nn_depthwise_conv_s8 esp_nn_depthwise_conv_s8_ansi
|
||||
|
||||
#define esp_nn_conv_s8 esp_nn_conv_s8_ansi
|
||||
|
||||
#define esp_nn_get_conv_scratch_size esp_nn_get_conv_scratch_size_ansi
|
||||
#define esp_nn_set_conv_scratch_buf esp_nn_set_conv_scratch_buf_ansi
|
||||
|
||||
#define esp_nn_get_depthwise_conv_scratch_size esp_nn_get_depthwise_conv_scratch_size_ansi
|
||||
#define esp_nn_set_depthwise_conv_scratch_buf esp_nn_set_depthwise_conv_scratch_buf_ansi
|
||||
|
||||
#define esp_nn_relu6_s8 esp_nn_relu6_s8_ansi
|
||||
|
||||
#define esp_nn_avg_pool_s8 esp_nn_avg_pool_s8_ansi
|
||||
#define esp_nn_max_pool_s8 esp_nn_max_pool_s8_ansi
|
||||
|
||||
#define esp_nn_fully_connected_s8 esp_nn_fully_connected_s8_ansi
|
||||
|
||||
#define esp_nn_get_softmax_scratch_size esp_nn_get_softmax_scratch_size_ansi
|
||||
#define esp_nn_set_softmax_scratch_buf esp_nn_set_softmax_scratch_buf_ansi
|
||||
#define esp_nn_softmax_s8 esp_nn_softmax_s8_ansi
|
||||
309
code/components/esp-nn/include/esp_nn_ansi_headers.h
Normal file
309
code/components/esp-nn/include/esp_nn_ansi_headers.h
Normal file
@@ -0,0 +1,309 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file Header definitions to include for esp_nn reference functions
|
||||
*/
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
/************************** Basic math functions ****************************/
|
||||
|
||||
/**
|
||||
* @brief elementwise addition
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*
|
||||
* shift values are expected to be <= 0
|
||||
*/
|
||||
void esp_nn_add_elementwise_s8_ansi(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
const int32_t input1_mult,
|
||||
const int32_t input2_mult,
|
||||
const int32_t input1_shift,
|
||||
const int32_t input2_shift,
|
||||
const int32_t left_shift,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size);
|
||||
/**
|
||||
* @brief elementwise multiplication
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*
|
||||
* output shift is expected to be <= 0
|
||||
*/
|
||||
void esp_nn_mul_elementwise_s8_ansi(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size);
|
||||
|
||||
|
||||
/************************** Convolution functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief depthwise convolution per channel
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* Version used in tflite is per channel.
|
||||
* This version follows the same footsprints.
|
||||
* Meaning, it has per out_channel shift and multiplier for
|
||||
* requantization
|
||||
*
|
||||
* optimization notes: Though input_offset is int32 type,
|
||||
* offset values are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_depthwise_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
/**
|
||||
* @brief 2d-convolution channelwise
|
||||
*
|
||||
* @note operation: result += (input + offset) * filter
|
||||
*
|
||||
* inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
int esp_nn_get_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params);
|
||||
void esp_nn_set_conv_scratch_buf_ansi(const void *buf);
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params);
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_ansi(const void *buf);
|
||||
|
||||
/************************** Activation functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief relu6
|
||||
*
|
||||
* @note inout: int8_t
|
||||
*/
|
||||
void esp_nn_relu6_s8_ansi(int8_t *data, uint16_t size);
|
||||
|
||||
/************************** Pooling functions *****************************/
|
||||
|
||||
|
||||
/**
|
||||
* @brief max_pool
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_max_pool_s8_ansi(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels);
|
||||
|
||||
/**
|
||||
* @brief avg_pool
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_avg_pool_s8_ansi(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels);
|
||||
|
||||
|
||||
/************************** Fully connected functions ***********************/
|
||||
|
||||
/**
|
||||
* @brief fully connected
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_fully_connected_s8_ansi(const int8_t *input_data,
|
||||
const int32_t input_offset,
|
||||
const uint16_t row_len,
|
||||
const int8_t *filter_data,
|
||||
const int32_t filter_offset,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_shift,
|
||||
const int32_t out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
/**
|
||||
* @brief Get scratch buffer size needed by softmax function
|
||||
*
|
||||
* @param width
|
||||
* @param height
|
||||
* @return size in bytes
|
||||
*
|
||||
* @note buffer must be 4 byte aligned
|
||||
*/
|
||||
int32_t esp_nn_get_softmax_scratch_size_ansi(const int32_t width, const int32_t height);
|
||||
|
||||
/* ANSI C function to be hooked up when optimised version needed */
|
||||
int32_t esp_nn_get_softmax_scratch_size_opt(const int32_t width, const int32_t height);
|
||||
|
||||
/**
|
||||
* @brief Set scratch buffer to be used by softmax function
|
||||
*
|
||||
* @param buffer this can be NULL if one needs to unset it
|
||||
* must be aligned to 4 bytes
|
||||
*/
|
||||
void esp_nn_set_softmax_scratch_buf_ansi(void *buffer);
|
||||
|
||||
/**
|
||||
* @brief reference softmax function
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
*/
|
||||
void esp_nn_softmax_s8_ansi(const int8_t *input_data,
|
||||
const int32_t height,
|
||||
const int32_t width,
|
||||
const int32_t mult,
|
||||
const int32_t shift,
|
||||
const int32_t diff_min,
|
||||
int8_t *output_data);
|
||||
|
||||
|
||||
//////////////////////////// Generic optimisations /////////////////////////////
|
||||
|
||||
/************************** Convolution functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief 2d-convolution channelwise optimized version
|
||||
*
|
||||
* @note operation: result += (input + offset) * filter
|
||||
*
|
||||
* inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
/**
|
||||
* @brief depthwise convolution per channel optimized version
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* Version used in tflite is per channel.
|
||||
* This version follows the same footsprints.
|
||||
* Meaning, it has per out_channel shift and multiplier for
|
||||
* requantization
|
||||
*
|
||||
* optimization notes: Though input_offset is int32 type,
|
||||
* offset values are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_depthwise_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
int esp_nn_get_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params);
|
||||
void esp_nn_set_conv_scratch_buf_opt(const void *buf);
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params);
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_opt(const void *buf);
|
||||
|
||||
/* ANSI C function to be hooked up when optimised version needed */
|
||||
void esp_nn_set_softmax_scratch_buf_opt(void *buffer);
|
||||
|
||||
/**
|
||||
* @brief optimised version of softmax function
|
||||
*
|
||||
* @note the function uses extra buffer (4 * width bytes)
|
||||
* hence, scratch buffers must be set before calling this.
|
||||
*/
|
||||
void esp_nn_softmax_s8_opt(const int8_t *input_data,
|
||||
const int32_t height,
|
||||
const int32_t width,
|
||||
const int32_t mult,
|
||||
const int32_t shift,
|
||||
const int32_t diff_min,
|
||||
int8_t *output_data);
|
||||
83
code/components/esp-nn/include/esp_nn_defs.h
Normal file
83
code/components/esp-nn/include/esp_nn_defs.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief structure to club data dims
|
||||
* this structure can be used for input, output and filter
|
||||
*/
|
||||
typedef struct data_dims {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
int32_t channels;
|
||||
|
||||
int32_t extra; // can be used as batch or any other param
|
||||
} data_dims_t;
|
||||
|
||||
/**
|
||||
* @brief 2d data structure (width, height)
|
||||
*
|
||||
*/
|
||||
typedef struct data_2d {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
} data_2d_t;
|
||||
|
||||
/**
|
||||
* @brief min/max activation
|
||||
*/
|
||||
typedef struct act_params {
|
||||
int32_t min;
|
||||
int32_t max;
|
||||
} act_params_t;
|
||||
|
||||
/**
|
||||
* @brief per channel quant data
|
||||
*
|
||||
* @note number of shift and mult elements are equal to output channels
|
||||
*/
|
||||
typedef struct quant_data {
|
||||
int32_t *shift;
|
||||
int32_t *mult;
|
||||
} quant_data_t;
|
||||
|
||||
/**
|
||||
* @brief params specific to convolution 2d
|
||||
*
|
||||
*/
|
||||
typedef struct conv_params {
|
||||
int32_t in_offset;
|
||||
int32_t out_offset;
|
||||
data_2d_t stride;
|
||||
data_2d_t padding;
|
||||
data_2d_t dilation;
|
||||
act_params_t activation;
|
||||
} conv_params_t;
|
||||
|
||||
/**
|
||||
* @brief params specific to depthwise convolution 2d
|
||||
*
|
||||
*/
|
||||
typedef struct dw_conv_params {
|
||||
int32_t in_offset;
|
||||
int32_t out_offset;
|
||||
int32_t ch_mult; // channel multiplier. (in_ch * ch_mult = out_ch)
|
||||
data_2d_t stride;
|
||||
data_2d_t padding;
|
||||
data_2d_t dilation;
|
||||
act_params_t activation;
|
||||
} dw_conv_params_t;
|
||||
231
code/components/esp-nn/include/esp_nn_esp32s3.h
Normal file
231
code/components/esp-nn/include/esp_nn_esp32s3.h
Normal file
@@ -0,0 +1,231 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @file Header definitions to include for esp_nn optimized functions for
|
||||
* the ESP32-S3 platform
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
/************************** Basic math functions *****************************/
|
||||
|
||||
|
||||
/**
|
||||
* @brief elementwise addition
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*
|
||||
* shift values are expected to be <= 0
|
||||
*/
|
||||
void esp_nn_add_elementwise_s8_esp32s3(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
const int32_t input1_mult,
|
||||
const int32_t input2_mult,
|
||||
const int32_t input1_shift,
|
||||
const int32_t input2_shift,
|
||||
const int32_t left_shift,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size);
|
||||
|
||||
/**
|
||||
* @brief elementwise multiplication
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*
|
||||
* output shift is expected to be <= 0
|
||||
*/
|
||||
void esp_nn_mul_elementwise_s8_esp32s3(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size);
|
||||
|
||||
|
||||
/************************** Convolution functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief depthwise convolution per channel
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* Version used in tflite is per channel.
|
||||
* This version follows the same footsprints.
|
||||
* Meaning, it has per out_channel shift and multiplier for
|
||||
* requantization
|
||||
*
|
||||
* optimization notes: Though input_offset is int32 type,
|
||||
* offset values are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_depthwise_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *output_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
/**
|
||||
* @brief 2d - convolution channelwise
|
||||
*
|
||||
* @note operation: result += (input + offset) * filter
|
||||
*
|
||||
* inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *output_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
int esp_nn_get_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params);
|
||||
void esp_nn_set_conv_scratch_buf_esp32s3(const void *buf);
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params);
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_esp32s3(const void *buf);
|
||||
|
||||
/************************** Pooling functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief max_pool
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_max_pool_s8_esp32s3(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels);
|
||||
|
||||
/**
|
||||
* @brief avg_pool
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_avg_pool_s8_esp32s3(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels);
|
||||
|
||||
|
||||
/************************** Fully connected functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief fully connected
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*
|
||||
* Current version works only on aligned input.
|
||||
* row_len and channels should both be multiple of 8.
|
||||
*/
|
||||
void esp_nn_fully_connected_s8_esp32s3(const int8_t *input_data,
|
||||
const int32_t input_offset,
|
||||
const uint16_t row_len,
|
||||
const int8_t *filter_data,
|
||||
const int32_t filter_offset,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_shift,
|
||||
const int32_t out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
/**
|
||||
* @brief relu6
|
||||
*
|
||||
* @note inout: int8_t
|
||||
*/
|
||||
void esp_nn_relu6_s8_esp32s3(int8_t *data, uint16_t size);
|
||||
|
||||
/********************** function defines ***************************/
|
||||
|
||||
#define esp_nn_add_elementwise_s8 esp_nn_add_elementwise_s8_esp32s3
|
||||
#define esp_nn_mul_elementwise_s8 esp_nn_mul_elementwise_s8_esp32s3
|
||||
|
||||
#define esp_nn_depthwise_conv_s8 esp_nn_depthwise_conv_s8_esp32s3
|
||||
|
||||
#define esp_nn_get_conv_scratch_size esp_nn_get_conv_scratch_size_esp32s3
|
||||
#define esp_nn_set_conv_scratch_buf esp_nn_set_conv_scratch_buf_esp32s3
|
||||
|
||||
#define esp_nn_get_depthwise_conv_scratch_size esp_nn_get_depthwise_conv_scratch_size_esp32s3
|
||||
#define esp_nn_set_depthwise_conv_scratch_buf esp_nn_set_depthwise_conv_scratch_buf_esp32s3
|
||||
|
||||
#define esp_nn_conv_s8 esp_nn_conv_s8_esp32s3
|
||||
|
||||
#define esp_nn_relu6_s8 esp_nn_relu6_s8_esp32s3
|
||||
|
||||
#define esp_nn_avg_pool_s8 esp_nn_avg_pool_s8_esp32s3
|
||||
#define esp_nn_max_pool_s8 esp_nn_max_pool_s8_esp32s3
|
||||
|
||||
#define esp_nn_fully_connected_s8 esp_nn_fully_connected_s8_esp32s3
|
||||
|
||||
#define esp_nn_get_softmax_scratch_size esp_nn_get_softmax_scratch_size_opt
|
||||
#define esp_nn_set_softmax_scratch_buf esp_nn_set_softmax_scratch_buf_opt
|
||||
#define esp_nn_softmax_s8 esp_nn_softmax_s8_opt
|
||||
47
code/components/esp-nn/include/esp_nn_generic_opt.h
Normal file
47
code/components/esp-nn/include/esp_nn_generic_opt.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @file Header definitions to include for esp_nn generic optimisations
|
||||
* For functions which not having optimisations, _ansi versions are picked.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
#define esp_nn_add_elementwise_s8 esp_nn_add_elementwise_s8_ansi
|
||||
#define esp_nn_mul_elementwise_s8 esp_nn_mul_elementwise_s8_ansi
|
||||
|
||||
#define esp_nn_depthwise_conv_s8 esp_nn_depthwise_conv_s8_opt
|
||||
|
||||
#define esp_nn_conv_s8 esp_nn_conv_s8_opt
|
||||
|
||||
#define esp_nn_get_conv_scratch_size esp_nn_get_conv_scratch_size_opt
|
||||
#define esp_nn_set_conv_scratch_buf esp_nn_set_conv_scratch_buf_opt
|
||||
|
||||
#define esp_nn_get_depthwise_conv_scratch_size esp_nn_get_depthwise_conv_scratch_size_opt
|
||||
#define esp_nn_set_depthwise_conv_scratch_buf esp_nn_set_depthwise_conv_scratch_buf_opt
|
||||
|
||||
#define esp_nn_relu6_s8 esp_nn_relu6_s8_ansi
|
||||
|
||||
#define esp_nn_avg_pool_s8 esp_nn_avg_pool_s8_ansi
|
||||
#define esp_nn_max_pool_s8 esp_nn_max_pool_s8_ansi
|
||||
|
||||
#define esp_nn_fully_connected_s8 esp_nn_fully_connected_s8_ansi
|
||||
|
||||
#define esp_nn_get_softmax_scratch_size esp_nn_get_softmax_scratch_size_opt
|
||||
#define esp_nn_set_softmax_scratch_buf esp_nn_set_softmax_scratch_buf_opt
|
||||
#define esp_nn_softmax_s8 esp_nn_softmax_s8_opt
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_relu6_s8_ansi(int8_t *data, uint16_t size)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
int32_t ip = data[i];
|
||||
|
||||
ip = max(ip, 0);
|
||||
data[i] = min(ip, 6);
|
||||
}
|
||||
}
|
||||
97
code/components/esp-nn/src/basic_math/esp_nn_add_ansi.c
Normal file
97
code/components/esp-nn/src/basic_math/esp_nn_add_ansi.c
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_add_elementwise_u8_ansi(const uint8_t *input1_data,
|
||||
const uint8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
const int32_t input1_mult,
|
||||
const int32_t input2_mult,
|
||||
const int32_t input1_shift,
|
||||
const int32_t input2_shift,
|
||||
const int32_t left_shift,
|
||||
uint8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size)
|
||||
{
|
||||
for (int i = 0; i < size; i++) {
|
||||
int32_t tmp1 = input1_data[i] + input1_offset;
|
||||
int32_t tmp2 = input2_data[i] + input2_offset;
|
||||
|
||||
tmp1 <<= left_shift;
|
||||
tmp2 <<= left_shift;
|
||||
|
||||
tmp1 = esp_nn_sat_round_doubling_high_mul(tmp1, input1_mult);
|
||||
tmp2 = esp_nn_sat_round_doubling_high_mul(tmp2, input2_mult);
|
||||
|
||||
tmp1 = esp_nn_div_by_power_of_two(tmp1, -input1_shift);
|
||||
tmp2 = esp_nn_div_by_power_of_two(tmp2, -input2_shift);
|
||||
|
||||
int32_t out = tmp1 + tmp2;
|
||||
out = esp_nn_sat_round_doubling_high_mul(out, out_mult);
|
||||
out = esp_nn_div_by_power_of_two(out, -out_shift);
|
||||
out = out + out_offset;
|
||||
|
||||
out = max(activation_min, min(out, activation_max));
|
||||
output[i] = (uint8_t) out;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_add_elementwise_s8_ansi(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
const int32_t input1_mult,
|
||||
const int32_t input2_mult,
|
||||
const int32_t input1_shift,
|
||||
const int32_t input2_shift,
|
||||
const int32_t left_shift,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size)
|
||||
{
|
||||
for (int i = 0; i < size; i++) {
|
||||
int32_t tmp1 = input1_data[i] + input1_offset;
|
||||
int32_t tmp2 = input2_data[i] + input2_offset;
|
||||
|
||||
tmp1 <<= left_shift;
|
||||
tmp2 <<= left_shift;
|
||||
|
||||
tmp1 = esp_nn_sat_round_doubling_high_mul(tmp1, input1_mult);
|
||||
tmp2 = esp_nn_sat_round_doubling_high_mul(tmp2, input2_mult);
|
||||
|
||||
tmp1 = esp_nn_div_by_power_of_two(tmp1, -input1_shift);
|
||||
tmp2 = esp_nn_div_by_power_of_two(tmp2, -input2_shift);
|
||||
|
||||
int32_t out = tmp1 + tmp2;
|
||||
out = esp_nn_sat_round_doubling_high_mul(out, out_mult);
|
||||
out = esp_nn_div_by_power_of_two(out, -out_shift);
|
||||
out = out + out_offset;
|
||||
|
||||
out = max(activation_min, min(out, activation_max));
|
||||
output[i] = (int8_t) out;
|
||||
}
|
||||
}
|
||||
42
code/components/esp-nn/src/basic_math/esp_nn_mul_ansi.c
Normal file
42
code/components/esp-nn/src/basic_math/esp_nn_mul_ansi.c
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_mul_elementwise_s8_ansi(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size)
|
||||
{
|
||||
for (int i = 0; i < size; i++) {
|
||||
int32_t tmp1 = input1_data[i] + input1_offset;
|
||||
int32_t tmp2 = input2_data[i] + input2_offset;
|
||||
|
||||
int32_t out = tmp1 * tmp2;
|
||||
out = esp_nn_multiply_by_quantized_mult(out, out_mult, out_shift);
|
||||
out = out + out_offset;
|
||||
|
||||
out = max(activation_min, min(out, activation_max));
|
||||
output[i] = (int8_t) out;
|
||||
}
|
||||
}
|
||||
255
code/components/esp-nn/src/common/common_functions.h
Normal file
255
code/components/esp-nn/src/common/common_functions.h
Normal file
@@ -0,0 +1,255 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* c99 standard still doesn't strictly inline functions
|
||||
* We need to use attribute as well to do this.
|
||||
*/
|
||||
#define __NN_FORCE_INLINE__ __attribute((always_inline)) static inline
|
||||
|
||||
/* min/max macros */
|
||||
#ifndef max
|
||||
#define max(a, b) ({ \
|
||||
__typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
|
||||
#define min(a, b) ({ \
|
||||
__typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
#endif
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_clz32(uint32_t in)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
__asm__ volatile("nsau %0, %0" : "+r" (in));
|
||||
return in;
|
||||
#elif defined(__GNUC__)
|
||||
return __builtin_clz(in);
|
||||
#else
|
||||
int32_t count = 32;
|
||||
uint32_t x = in, y = in >> 16;
|
||||
if (y != 0) {
|
||||
count -= 16;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 8;
|
||||
if (y != 0) {
|
||||
count -= 8;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 4;
|
||||
if (y != 0) {
|
||||
count -= 4;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 2;
|
||||
if (y != 0) {
|
||||
count -= 2;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 1;
|
||||
if (y != 0) {
|
||||
return count - 2;
|
||||
}
|
||||
return count - x;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Signed saturate a 32 bit value to 8 bits keeping output in 32 bit variable.
|
||||
*/
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_saturate8(int32_t in)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
__asm__ volatile("clamps %0, %0, 7" : "+a"(in));
|
||||
return in;
|
||||
#else
|
||||
return max(INT8_MIN, min(in, INT8_MAX));
|
||||
#endif
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_pick_sat_high32_of64(int64_t val64)
|
||||
{
|
||||
int32_t sign = (int32_t) (val64 >> 63);
|
||||
int32_t to_add = sign & ((1ul << 31) - 1);
|
||||
return (int32_t) ((int64_t) (val64 + to_add) >> 31);
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_sat_round_doubling_high_mul(int32_t in0, int32_t in1)
|
||||
{
|
||||
int32_t result;
|
||||
int64_t in0_64 = (int64_t) in0;
|
||||
bool overflow = (in0 == in1) && (in0 == (int32_t) INT32_MIN);
|
||||
|
||||
/* Nudge value */
|
||||
int64_t nudge_val = 1 << 30;
|
||||
if ((in0 < 0) ^ (in1 < 0)) {
|
||||
nudge_val = 1 - nudge_val;
|
||||
}
|
||||
|
||||
/* Multiply and add nudge */
|
||||
int64_t mult = in0_64 * in1 + nudge_val;
|
||||
|
||||
/* Round and pickup 32 bits */
|
||||
result = esp_nn_pick_sat_high32_of64(mult);
|
||||
|
||||
return overflow ? INT32_MAX : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* fast version
|
||||
* this will fail for values closer to INT32_MAX and INT32_MIN by `1 << (exponent - 1)`.
|
||||
* We can afford to do this because we are at the very last stage of filter.
|
||||
* Also it is pretty rare condition as our output is going to be 8 bit.
|
||||
*/
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_div_by_power_of_two_fast(int32_t val, int32_t exponent)
|
||||
{
|
||||
int32_t to_add = (1 << (exponent - 1)) - (val < 0);
|
||||
return (int32_t) ((val + to_add) >> exponent);
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_div_by_power_of_two(int32_t val, int32_t exponent)
|
||||
{
|
||||
int32_t result;
|
||||
|
||||
const int32_t mask = (1 << exponent) - 1;
|
||||
const int32_t remainder = val & mask;
|
||||
|
||||
result = val >> exponent;
|
||||
int32_t threshold = (mask >> 1) + (result < 0);
|
||||
|
||||
if (remainder > threshold) {
|
||||
result += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_multiply_by_quantized_mult(int32_t x, int32_t mult, int32_t shift)
|
||||
{
|
||||
int32_t left_shift = shift > 0 ? shift : 0;
|
||||
int32_t right_shift = shift > 0 ? 0 : -shift;
|
||||
int32_t result = esp_nn_sat_round_doubling_high_mul(x * (1 << left_shift), mult);
|
||||
return esp_nn_div_by_power_of_two(result, right_shift);
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_multiply_by_quantized_mult_fast(int32_t x, int32_t mult, int32_t shift)
|
||||
{
|
||||
int32_t left_shift = max(shift, 0);
|
||||
int32_t right_shift = left_shift - shift;
|
||||
|
||||
int64_t nudge_val = 1 << 30;
|
||||
int64_t in0_64 = (int64_t) (x << left_shift);
|
||||
|
||||
/* Multiply and add nudge */
|
||||
int64_t mult_64 = in0_64 * mult + nudge_val;
|
||||
int32_t result = (int32_t) (mult_64 >> 31);
|
||||
if (right_shift) {
|
||||
result = esp_nn_div_by_power_of_two_fast(result, right_shift);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void esp_nn_aligned_s8_pad_with_value(const int8_t *src, int8_t *dst,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t pad_val,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht)
|
||||
{
|
||||
/* memset with pad_val */
|
||||
memset(dst, pad_val, ((input_wd + 2 * pad_wd) * (input_ht + 2 * pad_ht)) * channels);
|
||||
dst += (pad_wd + input_wd + pad_wd) * channels;
|
||||
|
||||
for (int i = 0; i < input_ht; i++) {
|
||||
dst += pad_wd * channels;
|
||||
for (int j = 0; j < input_wd * channels; j++) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
dst += pad_wd * channels;
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_nn_aligned_s8_pad_end_with_value(const int8_t *src, int8_t *dst,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t pad_val,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht)
|
||||
{
|
||||
for (int i = 0; i < input_ht; i++) {
|
||||
for (int j = 0; j < input_wd * channels; j++) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
if (pad_wd) {
|
||||
memset(dst, pad_val, pad_wd * channels);
|
||||
dst += pad_wd * channels;
|
||||
}
|
||||
}
|
||||
/* pad end `pad_ht` lines at end */
|
||||
if (pad_ht) {
|
||||
memset(dst, pad_val, (input_wd + pad_wd) * pad_ht * channels);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief convert 8 bit input data to 16 bit
|
||||
*
|
||||
* @param src int8_t source data
|
||||
* @param dst int16_t dst data
|
||||
* @param size length of data
|
||||
* @param offset offset to be added to src data. Range: [-128, 127]
|
||||
*/
|
||||
__NN_FORCE_INLINE__ void esp_nn_s8_to_s16_with_offset(const int8_t *src, int16_t *dst,
|
||||
const int size, const int32_t offset)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < size; i += 2) {
|
||||
dst[i + 0] = src[i + 0] + offset;
|
||||
dst[i + 1] = src[i + 1] + offset;
|
||||
}
|
||||
if(i < size) {
|
||||
dst[i] = src[i] + offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief convert 8 bit input data to 16 bit
|
||||
*
|
||||
* @param src int8_t source data
|
||||
* @param dst int16_t dst data
|
||||
* @param size length of data
|
||||
*/
|
||||
__NN_FORCE_INLINE__ void esp_nn_s8_to_s16(const int8_t *src, int16_t *dst, const int size)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < size; i += 2) {
|
||||
dst[i + 0] = src[i + 0];
|
||||
dst[i + 1] = src[i + 1];
|
||||
}
|
||||
if(i < size) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
179
code/components/esp-nn/src/convolution/esp_nn_conv_ansi.c
Normal file
179
code/components/esp-nn/src/convolution/esp_nn_conv_ansi.c
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <esp_nn_defs.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_conv_scratch_buf_ansi(const void *buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumption 1: i/p channels == o/p channels
|
||||
* Assumption 2: Pointers are valid
|
||||
* Assumption 3: dialation width = 1
|
||||
*/
|
||||
void esp_nn_conv_u8_ansi(const uint8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t filter_offset,
|
||||
const int32_t *bias,
|
||||
uint8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_shift,
|
||||
const int32_t out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
for (int out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {//channel_loop
|
||||
int32_t result = 0;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
for (int in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * in_channels + in_ch_idx;
|
||||
int32_t filter_index = ((out_ch_idx * filter_ht + filter_y_idx)
|
||||
* filter_wd + filter_x_idx) * in_channels
|
||||
+ in_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index] + filter_offset;
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[out_ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult(result, out_mult, out_shift);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
int out_index = (out_y * out_wd + out_x) * out_channels + out_ch_idx;
|
||||
out_data[out_index] = (uint8_t) result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumption 1: i/p channels == o/p channels
|
||||
* Assumption 2: Pointers are valid
|
||||
* Assumption 3: dialation width = 1
|
||||
*/
|
||||
void esp_nn_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int32_t base_y = stride_ht * out_y - pad_ht;
|
||||
const int32_t base_x = stride_wd * out_x - pad_wd;
|
||||
|
||||
const int32_t filter_y_start = max(0, -base_y);
|
||||
const int32_t filter_x_start = max(0, -base_x);
|
||||
|
||||
const int32_t filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
const int32_t filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
for (filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
int32_t input_base_offset = (in_row * input_wd + in_col) * in_channels;
|
||||
int32_t filter_base_offset = out_ch_idx * in_channels * filter_ht * filter_wd +
|
||||
(filter_y_idx * filter_wd + filter_x_idx) * in_channels;
|
||||
for (in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
|
||||
conv_out +=
|
||||
(input_data[input_base_offset + in_ch_idx] + input_offset) *
|
||||
filter_data[filter_base_offset + in_ch_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
463
code/components/esp-nn/src/convolution/esp_nn_conv_esp32s3.c
Normal file
463
code/components/esp-nn/src/convolution/esp_nn_conv_esp32s3.c
Normal file
@@ -0,0 +1,463 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <esp_nn_defs.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
static int16_t *scratch_buffer = NULL;
|
||||
|
||||
extern void esp_nn_conv_s8_mult8_1x1_esp32s3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const int8_t *filter_aligned,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
void *buffer /* scratch buffer */);
|
||||
|
||||
extern void esp_nn_conv_s16_mult4_1x1_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int16_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
void *buffer /* scratch buffer */);
|
||||
|
||||
extern void esp_nn_conv_s16_mult8_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int16_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_aligned_s8_to_s16_with_offset_esp32s3(const int8_t *src, int16_t *dst,
|
||||
const int size, const int32_t offset);
|
||||
|
||||
extern void esp_nn_s8_to_s16_esp32s3(const int8_t *src, int16_t *dst, const int size);
|
||||
|
||||
static void esp_nn_conv_s8_unrolled(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_ch = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_ch = output_dims->channels;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
for (out_ch_idx = 0; out_ch_idx < out_ch; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int32_t base_y = stride_ht * out_y - pad_ht;
|
||||
const int32_t base_x = stride_wd * out_x - pad_wd;
|
||||
|
||||
const int32_t filter_y_start = max(0, -base_y);
|
||||
const int32_t filter_x_start = max(0, -base_x);
|
||||
|
||||
const int32_t filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
const int32_t filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
for (filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
int32_t input_base_offset = (in_row * input_wd + in_col) * in_ch;
|
||||
int32_t filter_base_offset = out_ch_idx * in_ch * filter_ht * filter_wd +
|
||||
(filter_y_idx * filter_wd + filter_x_idx) * in_ch;
|
||||
for (in_ch_idx = 0; in_ch_idx < in_ch; in_ch_idx++) {
|
||||
conv_out +=
|
||||
(input_data[input_base_offset + in_ch_idx] + input_offset) *
|
||||
filter_data[filter_base_offset + in_ch_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_nn_conv_s8_pad_valid(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int32_t base_y = stride_ht * out_y;
|
||||
const int32_t base_x = stride_wd * out_x;
|
||||
|
||||
for (filter_y_idx = 0; filter_y_idx < filter_ht; filter_y_idx++) {
|
||||
for (filter_x_idx = 0; filter_x_idx < filter_wd; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
int32_t input_base_offset = (in_row * input_wd + in_col) * in_channels;
|
||||
int32_t filter_base_offset = out_ch_idx * in_channels * filter_ht * filter_wd +
|
||||
(filter_y_idx * filter_wd + filter_x_idx) * in_channels;
|
||||
const int8_t *input_data_ptr = input_data + input_base_offset;
|
||||
const int8_t *filter_data_ptr = filter_data + filter_base_offset;
|
||||
for (in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
|
||||
conv_out += (*input_data_ptr++ + input_offset) * *filter_data_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_nn_conv_s8_pad_valid_3x3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
const int32_t base_y = stride_ht * out_y;
|
||||
const int32_t base_x = stride_wd * out_x;
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
for (filter_y_idx = 0; filter_y_idx < 3; filter_y_idx++) {
|
||||
for (filter_x_idx = 0; filter_x_idx < 3; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
int32_t input_base_offset = (in_row * input_wd + in_col) * in_channels;
|
||||
int32_t filter_base_offset = out_ch_idx * in_channels * 3 * 3 +
|
||||
(filter_y_idx * 3 + filter_x_idx) * in_channels;
|
||||
const int8_t *input_data_ptr = input_data + input_base_offset;
|
||||
const int8_t *filter_data_ptr = filter_data + filter_base_offset;
|
||||
for (in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
|
||||
conv_out += (*input_data_ptr++ + input_offset) * *filter_data_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_nn_conv_s8_pad_valid_ch3_3x3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const int32_t input_offset,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
int32_t out_ch_idx, out_y, out_x, filter_y_idx;
|
||||
|
||||
/* use scratch_buffer to pre-compute offset factor */
|
||||
int16_t *filter_sum = (int16_t *) scratch_buffer;
|
||||
const int8_t *filter_ptr = filter_data;
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int16_t sum_val = 0;
|
||||
for (int i = 0; i < 9; i++) {
|
||||
sum_val += *filter_ptr++;
|
||||
sum_val += *filter_ptr++;
|
||||
sum_val += *filter_ptr++;
|
||||
}
|
||||
*filter_sum++ = sum_val;
|
||||
}
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
const int8_t *filter_data_ptr = filter_data;
|
||||
const int32_t base_y = stride_ht * out_y;
|
||||
const int32_t base_x = stride_wd * out_x;
|
||||
const int8_t *input_base_ptr = input_data + (base_y * input_wd + base_x) * 3;
|
||||
int16_t *filter_sum = (int16_t *) scratch_buffer;
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
for (filter_y_idx = 0; filter_y_idx < 3; filter_y_idx++) {
|
||||
const int8_t *input_data_ptr = input_base_ptr + (filter_y_idx * input_wd) * 3;
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
}
|
||||
|
||||
conv_out += *filter_sum++ * input_offset;
|
||||
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int esp_nn_get_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_ch = input_dims->channels;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_ch = output_dims->channels;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * in_ch * out_ch;
|
||||
int input_size = input_wd * input_ht * in_ch;
|
||||
|
||||
int transpose_buf_size = 2 * (8 * in_ch); /* to store intermediate data */
|
||||
if (input_wd * input_ht < 8) {
|
||||
transpose_buf_size = 0; // not using this for leftover
|
||||
}
|
||||
int align_buf_size = 32; /* extra buffer for alignment */
|
||||
if (in_ch % 8 == 0 && filter_wd == 1 && filter_ht == 1 &&
|
||||
pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
|
||||
return filter_size + transpose_buf_size + align_buf_size;
|
||||
}
|
||||
return 2 * (filter_size + input_size) + transpose_buf_size + align_buf_size;
|
||||
}
|
||||
|
||||
void esp_nn_set_conv_scratch_buf_esp32s3(void *buf)
|
||||
{
|
||||
scratch_buffer = (int16_t *) buf;
|
||||
}
|
||||
|
||||
void esp_nn_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * channels * out_channels;
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
int align_len = 16 - (filter_size & 15);
|
||||
int16_t *filter_data16 = scratch_buffer;
|
||||
int16_t *input_data16 = scratch_buffer + filter_size + align_len;
|
||||
|
||||
if (scratch_buffer == NULL) {
|
||||
printf("esp_nn_conv error! scratch_buffer not set!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (channels % 8 == 0 && filter_wd == 1 && filter_ht == 1 &&
|
||||
pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
|
||||
int8_t *filter_aligned = (int8_t *) scratch_buffer;
|
||||
int scratch_offset = (int) (filter_aligned + filter_size);
|
||||
void *scratch_buf = (void *) (scratch_offset + 16 - (scratch_offset & 15));
|
||||
memcpy(filter_aligned, filter_data, filter_size); // copy to aligned address
|
||||
esp_nn_conv_s8_mult8_1x1_esp32s3(
|
||||
input, input_wd, input_ht, channels, input_offset, filter_aligned,
|
||||
bias, out_data, out_wd, out_ht, out_channels, out_offset,
|
||||
out_shift, out_mult, activation_min, activation_max, scratch_buf);
|
||||
} else if (channels % 4 == 0 && filter_wd == 1 && filter_ht == 1 &&
|
||||
(input_wd * input_ht) % 4 == 0 && /* TODO: remove this check */
|
||||
pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
|
||||
int scratch_offset = (int) (input_data16 + input_size);
|
||||
void *scratch_buf = (void *) (scratch_offset + 16 - (scratch_offset & 15));
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input, input_data16, input_size, input_offset);
|
||||
esp_nn_conv_s16_mult4_1x1_esp32s3(
|
||||
input_data16, input_wd, input_ht, channels, filter_data16,
|
||||
bias, out_data, out_wd, out_ht, out_channels, out_offset,
|
||||
out_shift, out_mult, activation_min, activation_max, scratch_buf);
|
||||
} else if (channels % 8 == 0) {
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input, input_data16, input_size, input_offset);
|
||||
esp_nn_conv_s16_mult8_esp32s3(
|
||||
input_data16, input_wd, input_ht, channels, pad_wd, pad_ht,
|
||||
stride_wd, stride_ht, filter_data16, filter_wd, filter_ht, bias,
|
||||
out_data, out_wd, out_ht, out_channels, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else if (pad_wd == 0 && pad_ht == 0) {
|
||||
if (filter_wd == 3 && filter_ht == 3 && channels == 3) {
|
||||
esp_nn_conv_s8_pad_valid_ch3_3x3(input, input_wd, input_ht, input_offset,
|
||||
stride_wd, stride_ht, filter_data, bias,
|
||||
out_data, out_wd, out_ht, out_channels, out_offset,
|
||||
out_shift, out_mult, activation_min, activation_max);
|
||||
} else {
|
||||
esp_nn_conv_s8_pad_valid(input, input_wd, input_ht, channels, input_offset,
|
||||
stride_wd, stride_ht, filter_data, filter_wd, filter_ht, bias,
|
||||
out_data, out_wd, out_ht, out_channels, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
}
|
||||
} else {
|
||||
/* Basic unrolled version */
|
||||
esp_nn_conv_s8_unrolled(input_dims, input, filter_dims, filter_data,
|
||||
bias, output_dims, out_data, conv_params, quant_data);
|
||||
}
|
||||
}
|
||||
179
code/components/esp-nn/src/convolution/esp_nn_conv_opt.c
Normal file
179
code/components/esp-nn/src/convolution/esp_nn_conv_opt.c
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <esp_nn_defs.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_conv_scratch_buf_opt(const void *buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
__attribute__ ((noinline))
|
||||
static void esp_nn_conv_s8_1x1(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t in_channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
for (int32_t in_row = 0; in_row < out_ht * stride_ht; in_row += stride_ht) {
|
||||
for (int32_t in_col = 0; in_col < out_wd * stride_wd; in_col += stride_wd) {
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int8_t *filter_ptr = filter_data;
|
||||
const int8_t *input_base_ptr = input_data + (in_row * input_wd + in_col) * in_channels;
|
||||
int32_t out_ch_idx = 0;
|
||||
for (; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int8_t *input_ptr = input_base_ptr;
|
||||
|
||||
int32_t in_ch_idx = 0;
|
||||
for (; in_ch_idx < in_channels - 3; in_ch_idx += 4) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
for (; in_ch_idx < in_channels; in_ch_idx ++) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, *out_mult++, *out_shift++);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumption 1: i/p channels == o/p channels
|
||||
* Assumption 2: Pointers are valid
|
||||
* Assumption 3: dialation width = 1
|
||||
*/
|
||||
void esp_nn_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
|
||||
if (filter_wd == 1 && filter_ht == 1) {
|
||||
esp_nn_conv_s8_1x1(input_dims, input_data, filter_data, bias,
|
||||
output_dims, out_data, conv_params, quant_data);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int32_t out_ch_idx, out_y, out_x, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int32_t base_y = stride_ht * out_y - pad_ht;
|
||||
const int32_t base_x = stride_wd * out_x - pad_wd;
|
||||
|
||||
const int32_t filter_y_start = max(0, -base_y);
|
||||
const int32_t filter_x_start = max(0, -base_x);
|
||||
|
||||
const int32_t filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
const int32_t filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
for (filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
|
||||
const int8_t *input_ptr = input_data +
|
||||
(in_row * input_wd + in_col) * in_channels;
|
||||
const int8_t *filter_ptr = filter_data +
|
||||
out_ch_idx * in_channels * filter_ht * filter_wd +
|
||||
(filter_y_idx * filter_wd + filter_x_idx) * in_channels;
|
||||
int32_t in_ch_idx = 0;
|
||||
for (; in_ch_idx < in_channels - 3; in_ch_idx += 4) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
for (; in_ch_idx < in_channels; in_ch_idx ++) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, *out_mult++, *out_shift++);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <esp_nn_defs.h>
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_ansi(const void *buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void esp_nn_depthwise_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
for (int ch_mult_idx = 0; ch_mult_idx < ch_mult; ch_mult_idx++) {
|
||||
int32_t result = 0;
|
||||
const int out_ch_idx = ch_mult_idx + ch_idx * ch_mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[out_ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult(result, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <esp_nn_defs.h>
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_opt(const void *buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* common channel multiplier == 1 case */
|
||||
__attribute__ ((noinline))
|
||||
static void esp_nn_depthwise_conv_s8_ch_mult_1(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
int ch_idx = 0;
|
||||
for (; ch_idx < channels - 3; ch_idx += 4) {//channel_loop
|
||||
int32_t result0 = 0;
|
||||
int32_t result1 = 0;
|
||||
int32_t result2 = 0;
|
||||
int32_t result3 = 0;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels) + ch_idx;
|
||||
int32_t input_val0 = input_data[input_index + 0] + input_offset;
|
||||
int32_t input_val1 = input_data[input_index + 1] + input_offset;
|
||||
int32_t input_val2 = input_data[input_index + 2] + input_offset;
|
||||
int32_t input_val3 = input_data[input_index + 3] + input_offset;
|
||||
int32_t filter_val0 = filter_data[filter_index + 0];
|
||||
int32_t filter_val1 = filter_data[filter_index + 1];
|
||||
int32_t filter_val2 = filter_data[filter_index + 2];
|
||||
int32_t filter_val3 = filter_data[filter_index + 3];
|
||||
result0 += input_val0 * filter_val0;
|
||||
result1 += input_val1 * filter_val1;
|
||||
result2 += input_val2 * filter_val2;
|
||||
result3 += input_val3 * filter_val3;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result0 += bias[ch_idx + 0];
|
||||
result1 += bias[ch_idx + 1];
|
||||
result2 += bias[ch_idx + 2];
|
||||
result3 += bias[ch_idx + 3];
|
||||
}
|
||||
result0 = esp_nn_multiply_by_quantized_mult_fast(result0, *out_mult++, *out_shift++);
|
||||
result1 = esp_nn_multiply_by_quantized_mult_fast(result1, *out_mult++, *out_shift++);
|
||||
result2 = esp_nn_multiply_by_quantized_mult_fast(result2, *out_mult++, *out_shift++);
|
||||
result3 = esp_nn_multiply_by_quantized_mult_fast(result3, *out_mult++, *out_shift++);
|
||||
|
||||
result0 += out_offset;
|
||||
result1 += out_offset;
|
||||
result2 += out_offset;
|
||||
result3 += out_offset;
|
||||
|
||||
result0 = max(result0, activation_min);
|
||||
result1 = max(result1, activation_min);
|
||||
result2 = max(result2, activation_min);
|
||||
result3 = max(result3, activation_min);
|
||||
|
||||
result0 = min(result0, activation_max);
|
||||
result1 = min(result1, activation_max);
|
||||
result2 = min(result2, activation_max);
|
||||
result3 = min(result3, activation_max);
|
||||
|
||||
out_data[out_idx++] = result0;
|
||||
out_data[out_idx++] = result1;
|
||||
out_data[out_idx++] = result2;
|
||||
out_data[out_idx++] = result3;
|
||||
}
|
||||
for (; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
int32_t result = 0;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels) + ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult_fast(result, *out_mult++, *out_shift++);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_depthwise_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
if (ch_mult == 1) {
|
||||
esp_nn_depthwise_conv_s8_ch_mult_1(input_dims, input_data, filter_dims, filter_data,
|
||||
bias, output_dims, out_data, conv_params, quant_data);
|
||||
return;
|
||||
}
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
int ch_mult_idx = 0;
|
||||
for (; ch_mult_idx < ch_mult - 3; ch_mult_idx += 4) {
|
||||
int32_t result0 = 0;
|
||||
int32_t result1 = 0;
|
||||
int32_t result2 = 0;
|
||||
int32_t result3 = 0;
|
||||
const int out_ch_idx = ch_idx * ch_mult + ch_mult_idx;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val0 = filter_data[filter_index + 0];
|
||||
int32_t filter_val1 = filter_data[filter_index + 1];
|
||||
int32_t filter_val2 = filter_data[filter_index + 2];
|
||||
int32_t filter_val3 = filter_data[filter_index + 3];
|
||||
result0 += input_val * filter_val0;
|
||||
result1 += input_val * filter_val1;
|
||||
result2 += input_val * filter_val2;
|
||||
result3 += input_val * filter_val3;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result0 += bias[out_ch_idx + 0];
|
||||
result1 += bias[out_ch_idx + 1];
|
||||
result2 += bias[out_ch_idx + 2];
|
||||
result3 += bias[out_ch_idx + 3];
|
||||
}
|
||||
result0 = esp_nn_multiply_by_quantized_mult_fast(result0, *out_mult++, *out_shift++);
|
||||
result1 = esp_nn_multiply_by_quantized_mult_fast(result1, *out_mult++, *out_shift++);
|
||||
result2 = esp_nn_multiply_by_quantized_mult_fast(result2, *out_mult++, *out_shift++);
|
||||
result3 = esp_nn_multiply_by_quantized_mult_fast(result3, *out_mult++, *out_shift++);
|
||||
|
||||
result0 += out_offset;
|
||||
result1 += out_offset;
|
||||
result2 += out_offset;
|
||||
result3 += out_offset;
|
||||
|
||||
result0 = max(result0, activation_min);
|
||||
result1 = max(result1, activation_min);
|
||||
result2 = max(result2, activation_min);
|
||||
result3 = max(result3, activation_min);
|
||||
result0 = min(result0, activation_max);
|
||||
result1 = min(result1, activation_max);
|
||||
result2 = min(result2, activation_max);
|
||||
result3 = min(result3, activation_max);
|
||||
|
||||
out_data[out_idx++] = result0;
|
||||
out_data[out_idx++] = result1;
|
||||
out_data[out_idx++] = result2;
|
||||
out_data[out_idx++] = result3;
|
||||
}
|
||||
for (; ch_mult_idx < ch_mult; ch_mult_idx++) {
|
||||
int32_t result = 0;
|
||||
const int out_ch_idx = ch_idx * ch_mult + ch_mult_idx;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[out_ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult_fast(result, *out_mult++, *out_shift++);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,543 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <esp_nn_defs.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
static int16_t *scratch_buffer = NULL;
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult8_3x3_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int16_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult1_3x3_no_pad_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int16_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult8_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int16_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult4_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int16_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult1_3x3_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int16_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult1_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int16_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_s8_to_s16_esp32s3(const int8_t *src, int16_t *dst, const int size);
|
||||
|
||||
extern void esp_nn_aligned_s8_to_s16_with_offset_esp32s3(const int8_t *src, int16_t *dst,
|
||||
const int size, const int32_t offset);
|
||||
|
||||
static void esp_nn_depthwise_conv_s8_unrolled(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
int ch_mult_idx = 0;
|
||||
for (; ch_mult_idx < ch_mult - 3; ch_mult_idx += 4) {
|
||||
int32_t result0 = 0, result1 = 0, result2 = 0, result3 = 0;
|
||||
const int out_ch_idx = ch_mult_idx + ch_idx * ch_mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val0 = filter_data[filter_index + 0];
|
||||
int32_t filter_val1 = filter_data[filter_index + 1];
|
||||
int32_t filter_val2 = filter_data[filter_index + 2];
|
||||
int32_t filter_val3 = filter_data[filter_index + 3];
|
||||
result0 += input_val * filter_val0;
|
||||
result1 += input_val * filter_val1;
|
||||
result2 += input_val * filter_val2;
|
||||
result3 += input_val * filter_val3;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result0 += bias[out_ch_idx + 0];
|
||||
result1 += bias[out_ch_idx + 1];
|
||||
result2 += bias[out_ch_idx + 2];
|
||||
result3 += bias[out_ch_idx + 3];
|
||||
}
|
||||
result0 = esp_nn_multiply_by_quantized_mult(result0,
|
||||
out_mult[out_ch_idx + 0], out_shift[out_ch_idx + 0]);
|
||||
result1 = esp_nn_multiply_by_quantized_mult(result1,
|
||||
out_mult[out_ch_idx + 1], out_shift[out_ch_idx + 1]);
|
||||
result2 = esp_nn_multiply_by_quantized_mult(result2,
|
||||
out_mult[out_ch_idx + 2], out_shift[out_ch_idx + 2]);
|
||||
result3 = esp_nn_multiply_by_quantized_mult(result3,
|
||||
out_mult[out_ch_idx + 3], out_shift[out_ch_idx + 3]);
|
||||
|
||||
result0 += out_offset;
|
||||
result1 += out_offset;
|
||||
result2 += out_offset;
|
||||
result3 += out_offset;
|
||||
|
||||
result0 = max(result0, activation_min);
|
||||
result1 = max(result1, activation_min);
|
||||
result2 = max(result2, activation_min);
|
||||
result3 = max(result3, activation_min);
|
||||
|
||||
result0 = min(result0, activation_max);
|
||||
result1 = min(result1, activation_max);
|
||||
result2 = min(result2, activation_max);
|
||||
result3 = min(result3, activation_max);
|
||||
|
||||
out_data[out_idx++] = result0;
|
||||
out_data[out_idx++] = result1;
|
||||
out_data[out_idx++] = result2;
|
||||
out_data[out_idx++] = result3;
|
||||
}
|
||||
|
||||
/* left-over */
|
||||
for (; ch_mult_idx < ch_mult; ch_mult_idx++) {
|
||||
int32_t result = 0;
|
||||
const int out_ch_idx = ch_mult_idx + ch_idx * ch_mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[out_ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult(result, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_depthwise_conv_s8_ch_mult1(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
int32_t result = 0;
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * channels + ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult(result, out_mult[ch_idx], out_shift[ch_idx]);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * channels * ch_mult;
|
||||
int pad_width = 0, pad_height = 0;
|
||||
|
||||
if ((ch_mult == 1) && (channels % 8 == 0) && (filter_wd == 3) && (filter_ht == 3)) {
|
||||
if (channels % 16 == 0) {
|
||||
if (pad_wd || pad_ht) {
|
||||
pad_width = pad_wd * 2;
|
||||
pad_height = pad_ht * 2;
|
||||
} else {
|
||||
// check if we need to pad additionally
|
||||
pad_width = (out_wd * stride_wd + filter_wd - 1) - input_wd;
|
||||
pad_height = (out_ht * stride_ht + filter_ht - 1) - input_ht;
|
||||
// printf("in(%d %d %d), out(%d %d), filter (%d %d) stride (%d %d), pad (%d %d)",
|
||||
// input_wd, input_ht, channels, out_wd, out_ht, filter_wd, filter_ht,
|
||||
// stride_wd, stride_ht, pad_wd, pad_ht);
|
||||
}
|
||||
if (pad_width || pad_height) {
|
||||
int input_size = (input_wd + pad_width) * (input_ht + pad_height) * channels;
|
||||
// printf("ask1 %d\n", filter_size + input_size + 16);
|
||||
return filter_size + input_size + 16; // 16 for alignment
|
||||
} else {
|
||||
// printf("ask2 %d\n", filter_size + 16);
|
||||
return filter_size + 16; // 16 for alignment
|
||||
}
|
||||
} else {
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
// printf("ask3 %d\n", 2 * (filter_size + input_size) + 16);
|
||||
return 2 * (filter_size + input_size) + 16; // 16 for alignment
|
||||
}
|
||||
} else if (ch_mult % 4 == 0) {
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
// printf("ask4 %d\n", 2 * (filter_size + input_size) + 16);
|
||||
return 2 * (filter_size + input_size) + 16; // 16 for alignment
|
||||
}
|
||||
return 32; // just few bytes
|
||||
}
|
||||
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_esp32s3(void *buf)
|
||||
{
|
||||
scratch_buffer = (int16_t *) buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumption 1: i/p channels == o/p channels
|
||||
* Assumption 2: Pointers are valid
|
||||
* Assumption 3: dialation width = 1
|
||||
*/
|
||||
|
||||
|
||||
|
||||
void esp_nn_depthwise_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * channels * ch_mult;
|
||||
int align_len = 16 - (filter_size & 15);
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
int16_t *filter_data16 = scratch_buffer;
|
||||
int16_t *input_data16 = scratch_buffer + filter_size + align_len;
|
||||
if (scratch_buffer == NULL) {
|
||||
printf("esp_nn_depthwise_conv error! scratch_buffer not set!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ch_mult == 1) && (channels % 8 == 0)) {
|
||||
if ((filter_wd == 3) && (filter_ht == 3)) {
|
||||
if ((channels % 16 == 0) && (pad_wd == 1) && (pad_ht == 1)) {
|
||||
/* process in 8 bits */
|
||||
int8_t *filter_aligned = (int8_t *) scratch_buffer;
|
||||
int8_t *input_padded = (int8_t *) scratch_buffer + filter_size + align_len;
|
||||
memcpy(filter_aligned, filter_data, filter_size);
|
||||
esp_nn_aligned_s8_pad_with_value(input_data, input_padded, input_wd, input_ht, channels,
|
||||
-input_offset, pad_wd, pad_ht);
|
||||
esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(input_padded, input_wd + 2 * pad_wd,
|
||||
input_ht + 2 * pad_ht, channels, input_offset,
|
||||
stride_wd, stride_ht, filter_aligned, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else if ((channels % 16 == 0) && (pad_wd == 0) && (pad_ht == 0)) {
|
||||
/* process in 8 bits */
|
||||
int8_t *filter_aligned = (int8_t *) scratch_buffer;
|
||||
int8_t *input_padded = (int8_t *) scratch_buffer + filter_size + align_len;
|
||||
|
||||
// check if we need to pad additionally
|
||||
int pad_right = (out_wd * stride_wd + filter_wd - 1) - input_wd;
|
||||
int pad_bottom = (out_ht * stride_ht + filter_ht - 1) - input_ht;
|
||||
if (pad_right || pad_bottom) { // pad right and bottom
|
||||
esp_nn_aligned_s8_pad_end_with_value(input_data, input_padded, input_wd, input_ht,
|
||||
channels, -input_offset, pad_right, pad_bottom);
|
||||
} else {
|
||||
input_padded = (int8_t *) input_data;
|
||||
}
|
||||
memcpy(filter_aligned, filter_data, filter_size);
|
||||
esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(input_padded, input_wd + pad_right,
|
||||
input_ht + pad_bottom, channels, input_offset,
|
||||
stride_wd, stride_ht, filter_aligned, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else { /* (channels % 8) == 0 */
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input_data, input_data16, input_size, input_offset);
|
||||
esp_nn_depthwise_conv_s16_mult1_3x3_esp32s3(input_data16, input_wd, input_ht, channels,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, filter_data16,
|
||||
bias, out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
}
|
||||
} else { // all other ch_mult == 1, `channels % 8 == 0`
|
||||
esp_nn_depthwise_conv_s8_ch_mult1(input_data, input_wd, input_ht, channels, input_offset,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht,
|
||||
filter_data, filter_wd, filter_ht,
|
||||
bias, out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
}
|
||||
} else if (ch_mult % 8 == 0) {
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input_data, input_data16, input_size, input_offset);
|
||||
if (filter_wd == 3 && filter_ht == 3) {
|
||||
esp_nn_depthwise_conv_s16_mult8_3x3_esp32s3(input_data16, input_wd, input_ht, channels,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
|
||||
filter_data16, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else {
|
||||
esp_nn_depthwise_conv_s16_mult8_esp32s3(input_data16, input_wd, input_ht, channels,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
|
||||
filter_data16, filter_wd, filter_ht, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
}
|
||||
} else if (ch_mult % 4 == 0) {
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input_data, input_data16, input_size, input_offset);
|
||||
esp_nn_depthwise_conv_s16_mult4_esp32s3(input_data16, input_wd, input_ht, channels,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
|
||||
filter_data16, filter_wd, filter_ht, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else {
|
||||
esp_nn_depthwise_conv_s8_unrolled(input_data, input_wd, input_ht, channels, input_offset,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
|
||||
filter_data, filter_wd, filter_ht,
|
||||
bias, out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_fully_connected_s8_ansi(const int8_t *input_data,
|
||||
const int32_t input_offset,
|
||||
const uint16_t row_len,
|
||||
const int8_t *filter_data,
|
||||
const int32_t filter_offset,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_shift,
|
||||
const int32_t out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
for (int32_t out_c = 0; out_c < out_channels; ++out_c) {
|
||||
int32_t result = 0;
|
||||
for (int32_t data_idx = 0; data_idx < row_len; data_idx++) {
|
||||
int32_t filter_index = row_len * out_c + data_idx;
|
||||
int32_t input_val = input_data[data_idx];
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += (filter_val + filter_offset) * (input_val + input_offset);
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[out_c];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult(result, out_mult, out_shift);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
out_data[out_c] = (int8_t) result;
|
||||
}
|
||||
}
|
||||
72
code/components/esp-nn/src/pooling/esp_nn_avg_pool_ansi.c
Normal file
72
code/components/esp-nn/src/pooling/esp_nn_avg_pool_ansi.c
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_avg_pool_s8_ansi(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels)
|
||||
{
|
||||
int32_t base_y = -pad_ht;
|
||||
for (int32_t out_y = 0; out_y < output_ht; out_y++, base_y += stride_ht) {
|
||||
int32_t base_x = -pad_wd;
|
||||
for (int32_t out_x = 0; out_x < output_wd; out_x++, base_x += stride_wd) {
|
||||
for (int32_t ch_idx = 0; ch_idx < channels; ch_idx++) {
|
||||
int32_t result = 0;
|
||||
int32_t filter_cnt = 0;
|
||||
/* Make sure filter does not cross the input box */
|
||||
int32_t filter_y_start = max(0, -base_y);
|
||||
int32_t filter_x_start = max(0, -base_x);
|
||||
|
||||
int32_t filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int32_t filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int32_t filter_y = filter_y_start; filter_y < filter_y_end; filter_y++) {
|
||||
for (int32_t filter_x = filter_x_start; filter_x < filter_x_end; filter_x++) {
|
||||
int32_t in_x_idx = base_x + filter_x;
|
||||
int32_t in_y_idx = base_y + filter_y;
|
||||
int32_t input_index = (in_y_idx * input_wd + in_x_idx) * channels + ch_idx;
|
||||
result += input[input_index];
|
||||
filter_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Rounded average */
|
||||
result = result > 0 ? (result + filter_cnt / 2) / filter_cnt
|
||||
: (result - filter_cnt / 2) / filter_cnt;
|
||||
|
||||
/* Activation function */
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
int32_t output_index = (out_y * output_wd + out_x) * channels + ch_idx;
|
||||
output[output_index] = (int8_t) result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
66
code/components/esp-nn/src/pooling/esp_nn_max_pool_ansi.c
Normal file
66
code/components/esp-nn/src/pooling/esp_nn_max_pool_ansi.c
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_max_pool_s8_ansi(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels)
|
||||
{
|
||||
int32_t base_y = -pad_ht;
|
||||
for (int32_t out_y = 0; out_y < output_ht; out_y++, base_y += stride_ht) {
|
||||
int32_t base_x = -pad_wd;
|
||||
for (int32_t out_x = 0; out_x < output_wd; out_x++, base_x += stride_wd) {
|
||||
/* Make sure filter does not cross the input box */
|
||||
int32_t filter_y_start = max(0, -base_y);
|
||||
int32_t filter_x_start = max(0, -base_x);
|
||||
int32_t filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int32_t filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int32_t ch_idx = 0; ch_idx < channels; ch_idx++) {
|
||||
int8_t result = INT8_MIN;
|
||||
|
||||
for (int32_t filter_y = filter_y_start; filter_y < filter_y_end; filter_y++) {
|
||||
for (int32_t filter_x = filter_x_start; filter_x < filter_x_end; filter_x++) {
|
||||
int32_t in_x_idx = base_x + filter_x;
|
||||
int32_t in_y_idx = base_y + filter_y;
|
||||
int32_t input_index = (in_y_idx * input_wd + in_x_idx) * channels + ch_idx;
|
||||
result = max(input[input_index], result);
|
||||
}
|
||||
}
|
||||
|
||||
/* Activation function */
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
int32_t output_index = (out_y * output_wd + out_x) * channels + ch_idx;
|
||||
output[output_index] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
code/components/esp-nn/src/softmax/esp_nn_softmax_ansi.c
Normal file
88
code/components/esp-nn/src/softmax/esp_nn_softmax_ansi.c
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "softmax_common.h"
|
||||
|
||||
int32_t esp_nn_get_softmax_scratch_size_ansi(const int32_t width, const int32_t height)
|
||||
{
|
||||
(void) width;
|
||||
(void) height;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_softmax_scratch_buf_ansi(void *buffer)
|
||||
{
|
||||
(void) buffer;
|
||||
return;
|
||||
}
|
||||
|
||||
void esp_nn_softmax_s8_ansi(const int8_t *input_data,
|
||||
const int32_t height,
|
||||
const int32_t width,
|
||||
const int32_t mult,
|
||||
const int32_t shift,
|
||||
const int32_t diff_min,
|
||||
int8_t *output_data)
|
||||
{
|
||||
// The representation chosen for the input to the exp() function is Q5.26.
|
||||
// We need to leave extra space since values that we skip might be as large as
|
||||
// -32 before multiplying by input mult, and therefore as large as
|
||||
// -16 afterwards. Note that exp(-8) is definitely not insignificant to
|
||||
// accumulation, but exp(-16) definitely is.
|
||||
#define ACCUM_BITS 12
|
||||
#define DIFF_BITS 5
|
||||
|
||||
const int32_t mask = (1 << shift);
|
||||
int32_t col = 0;
|
||||
const int8_t *in_ptr = input_data;
|
||||
int8_t *out_ptr = output_data;
|
||||
|
||||
for (int row_idx = 0; row_idx < height; row_idx++) {
|
||||
int8_t max_in_row = in_ptr[0];
|
||||
for (col = 1; col < width; col++) {
|
||||
max_in_row = max(max_in_row, in_ptr[col]);
|
||||
}
|
||||
|
||||
int32_t input_diff = 0;
|
||||
int32_t sum_of_exps = 0;
|
||||
|
||||
for (col = 0; col < width; col++) {
|
||||
input_diff = in_ptr[col] - max_in_row;
|
||||
if (input_diff >= diff_min) {
|
||||
const int32_t input_diff_rescaled = SAT_HIGH_MUL(input_diff * mask, mult);
|
||||
const int32_t exp_raw = esp_nn_exp_on_negative_values(input_diff_rescaled);
|
||||
sum_of_exps += DIV_POW2(exp_raw, ACCUM_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
const int32_t headroom_plus1 = esp_nn_clz32((uint32_t) sum_of_exps);
|
||||
const int32_t shifted_scale = ONE_OVER_ONE_X((sum_of_exps << headroom_plus1) - (1 << 31));
|
||||
const int32_t bits_over_unit = ACCUM_BITS - headroom_plus1 + 31 - sizeof(int8_t) * 8;
|
||||
|
||||
for (col = 0; col < width; col++) {
|
||||
input_diff = in_ptr[col] - max_in_row;
|
||||
if (input_diff >= diff_min) {
|
||||
const int32_t input_diff_rescaled = SAT_HIGH_MUL(input_diff * mask, mult);
|
||||
const int32_t exp_raw = esp_nn_exp_on_negative_values(input_diff_rescaled);
|
||||
const int32_t shifted_output = SAT_HIGH_MUL(shifted_scale, exp_raw);
|
||||
const int32_t result = DIV_POW2(shifted_output, bits_over_unit) - 128;
|
||||
out_ptr[col] = (int8_t) esp_nn_saturate8(result);
|
||||
} else {
|
||||
out_ptr[col] = -128;
|
||||
}
|
||||
}
|
||||
in_ptr += width;
|
||||
out_ptr += width;
|
||||
}
|
||||
}
|
||||
108
code/components/esp-nn/src/softmax/esp_nn_softmax_opt.c
Normal file
108
code/components/esp-nn/src/softmax/esp_nn_softmax_opt.c
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "softmax_common.h"
|
||||
#include <stdio.h>
|
||||
|
||||
static int32_t *scratch_buf = NULL;
|
||||
|
||||
/**
|
||||
* @brief Get scratch buffer size needed by softmax function
|
||||
*
|
||||
* @param width
|
||||
* @param height
|
||||
* @return size in bytes
|
||||
*
|
||||
* @note buffer must be 4 byte aligned
|
||||
*/
|
||||
int32_t esp_nn_get_softmax_scratch_size_opt(const int32_t width, const int32_t height)
|
||||
{
|
||||
(void) height;
|
||||
return width * 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set scratch buffer to be used by softmax function
|
||||
*
|
||||
* @param buffer this can be NULL if one needs to unset it
|
||||
* must be aligned to 4 bytes
|
||||
*/
|
||||
void esp_nn_set_softmax_scratch_buf_opt(void *buffer)
|
||||
{
|
||||
scratch_buf = (int32_t *) buffer;
|
||||
}
|
||||
|
||||
void esp_nn_softmax_s8_opt(const int8_t *input_data,
|
||||
const int32_t height,
|
||||
const int32_t width,
|
||||
const int32_t mult,
|
||||
const int32_t shift,
|
||||
const int32_t diff_min,
|
||||
int8_t *output_data)
|
||||
{
|
||||
if (scratch_buf == NULL) {
|
||||
printf("%s error! scratch buffer not set\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
// The representation chosen for the input to the exp() function is Q5.26.
|
||||
// We need to leave extra space since values that we skip might be as large as
|
||||
// -32 before multiplying by input mult, and therefore as large as
|
||||
// -16 afterwards. Note that exp(-8) is definitely not insignificant to
|
||||
// accumulation, but exp(-16) definitely is.
|
||||
#define ACCUM_BITS 12
|
||||
#define DIFF_BITS 5
|
||||
|
||||
const int32_t mask = (1 << shift);
|
||||
int32_t col = 0;
|
||||
const int8_t *in_ptr = input_data;
|
||||
int8_t *out_ptr = output_data;
|
||||
|
||||
for (int row_idx = 0; row_idx < height; row_idx++) {
|
||||
int8_t max_in_row = in_ptr[0];
|
||||
for (col = 1; col < width; col++) {
|
||||
max_in_row = max(max_in_row, in_ptr[col]);
|
||||
}
|
||||
|
||||
int32_t input_diff = 0;
|
||||
int32_t sum_of_exps = 0;
|
||||
|
||||
for (col = 0; col < width; col++) {
|
||||
input_diff = in_ptr[col] - max_in_row;
|
||||
if (input_diff >= diff_min) {
|
||||
const int32_t input_diff_rescaled = SAT_HIGH_MUL(input_diff * mask, mult);
|
||||
const int32_t exp_raw = esp_nn_exp_on_negative_values(input_diff_rescaled);
|
||||
scratch_buf[col] = exp_raw; // store to avoid duplicate calculation later
|
||||
sum_of_exps += DIV_POW2(exp_raw, ACCUM_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
const int32_t headroom_plus1 = esp_nn_clz32((uint32_t) sum_of_exps);
|
||||
const int32_t shifted_scale = ONE_OVER_ONE_X((sum_of_exps << headroom_plus1) - (1 << 31));
|
||||
const int32_t bits_over_unit = ACCUM_BITS - headroom_plus1 + 31 - sizeof(int8_t) * 8;
|
||||
|
||||
for (col = 0; col < width; col++) {
|
||||
input_diff = in_ptr[col] - max_in_row;
|
||||
if (input_diff >= diff_min) {
|
||||
int32_t exp_raw = scratch_buf[col];
|
||||
const int32_t shifted_output = SAT_HIGH_MUL(shifted_scale, exp_raw);
|
||||
const int32_t result = DIV_POW2(shifted_output, bits_over_unit) - 128;
|
||||
out_ptr[col] = (int8_t) esp_nn_saturate8(result);
|
||||
} else {
|
||||
out_ptr[col] = -128;
|
||||
}
|
||||
}
|
||||
in_ptr += width;
|
||||
out_ptr += width;
|
||||
}
|
||||
}
|
||||
104
code/components/esp-nn/src/softmax/softmax_common.h
Normal file
104
code/components/esp-nn/src/softmax/softmax_common.h
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <common_functions.h>
|
||||
|
||||
#define MASK_IF_ZERO(x) (x) == 0 ? ~0 : 0
|
||||
#define MASK_IF_NON_ZERO(x) (x) != 0 ? ~0 : 0
|
||||
#define SELECT_USING_MASK(mask, a, b) ((mask) & (a)) ^ (~(mask) & (b))
|
||||
#define SAT_HIGH_MUL(x, y) esp_nn_sat_round_doubling_high_mul((x), (y))
|
||||
#define DIV_POW2(x,y) esp_nn_div_by_power_of_two((x), (y))
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t mul_power_of_2(int val, int exp)
|
||||
{
|
||||
const int32_t thresh = ((1 << (31 - exp)) - 1);
|
||||
int32_t result = val << exp;
|
||||
result = SELECT_USING_MASK(MASK_IF_NON_ZERO(val > thresh), INT32_MAX, result);
|
||||
result = SELECT_USING_MASK(MASK_IF_NON_ZERO(val < -thresh), INT32_MIN, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate `1 / (1 + x)` for x in [0, 1]
|
||||
*
|
||||
* @param val input value to calculate `1/(1+x)` for
|
||||
* @return `int32_t` result
|
||||
* @note Newton-Raphson division
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Division_algorithm#Newton.E2.80.93Raphson_division
|
||||
* Refer to that page for the logic behind the 48/17 and 32/17 constants.
|
||||
* Pseudocode: https://en.wikipedia.org/wiki/Division_algorithm#Pseudocode
|
||||
*/
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_one_over_one_plus_x_for_x_in_0_1(int32_t val)
|
||||
{
|
||||
const int64_t sum = (int64_t) val + INT32_MAX;
|
||||
const int32_t half_denominator = (int32_t) ((sum + (sum >= 0 ? 1 : -1)) / 2L);
|
||||
int32_t constant_48_over_17 = 1515870810;
|
||||
int32_t constant_neg_32_over_17 = -1010580540;
|
||||
int32_t x = constant_48_over_17 + SAT_HIGH_MUL(half_denominator, constant_neg_32_over_17);
|
||||
const int32_t fixed_2_one = (1 << 29);
|
||||
|
||||
x += mul_power_of_2(SAT_HIGH_MUL(x, fixed_2_one - SAT_HIGH_MUL(half_denominator, x)), 2);
|
||||
x += mul_power_of_2(SAT_HIGH_MUL(x, fixed_2_one - SAT_HIGH_MUL(half_denominator, x)), 2);
|
||||
x += mul_power_of_2(SAT_HIGH_MUL(x, fixed_2_one - SAT_HIGH_MUL(half_denominator, x)), 2);
|
||||
|
||||
return mul_power_of_2(x, 1);
|
||||
}
|
||||
|
||||
#define ONE_OVER_ONE_X(x) esp_nn_one_over_one_plus_x_for_x_in_0_1((x))
|
||||
|
||||
/**
|
||||
* @brief Return exp(x) for x < 0.
|
||||
*
|
||||
*/
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_exp_on_negative_values(int32_t val)
|
||||
{
|
||||
int32_t shift = 24;
|
||||
|
||||
const int32_t one_quarter = (1 << shift);
|
||||
int32_t mask = one_quarter - 1;
|
||||
const int32_t val_mod_minus_quarter = (val & mask) - one_quarter;
|
||||
const int32_t remainder = val_mod_minus_quarter - val;
|
||||
|
||||
// calculate exponent for x in [-1/4, 0) in `result`
|
||||
const int32_t x = (val_mod_minus_quarter << 5) + (1 << 28);
|
||||
const int32_t x2 = SAT_HIGH_MUL(x, x);
|
||||
const int32_t x3 = SAT_HIGH_MUL(x2, x);
|
||||
const int32_t x4 = SAT_HIGH_MUL(x2, x2);
|
||||
const int32_t one_over_3 = 715827883;
|
||||
const int32_t one_over_8 = 1895147668;
|
||||
|
||||
const int32_t x4_over_4 = DIV_POW2(x4, 2);
|
||||
const int32_t x4_over_4_plus_x3_over_6_plus_x2_over_2 = DIV_POW2(SAT_HIGH_MUL(x4_over_4 + x3, one_over_3) + x2, 1);
|
||||
int32_t result = one_over_8 + SAT_HIGH_MUL(one_over_8, x + x4_over_4_plus_x3_over_6_plus_x2_over_2);
|
||||
|
||||
#define SELECT_IF_NON_ZERO(x) { \
|
||||
mask = MASK_IF_NON_ZERO(remainder & (1 << shift++)); \
|
||||
result = SELECT_USING_MASK(mask, SAT_HIGH_MUL(result, x), result); \
|
||||
}
|
||||
|
||||
SELECT_IF_NON_ZERO(1672461947)
|
||||
SELECT_IF_NON_ZERO(1302514674)
|
||||
SELECT_IF_NON_ZERO(790015084)
|
||||
SELECT_IF_NON_ZERO(290630308)
|
||||
SELECT_IF_NON_ZERO(39332535)
|
||||
SELECT_IF_NON_ZERO(720401)
|
||||
SELECT_IF_NON_ZERO(242)
|
||||
|
||||
#undef SELECT_IF_NON_ZERO
|
||||
|
||||
mask = MASK_IF_ZERO(val);
|
||||
return SELECT_USING_MASK(mask, INT32_MAX, result);
|
||||
}
|
||||
9
code/components/esp-nn/test_app/CMakeLists.txt
Normal file
9
code/components/esp-nn/test_app/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../" "../tests/")
|
||||
set(IDF_EXCLUDE_COMPONENTS test test_app)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(test_app)
|
||||
7
code/components/esp-nn/test_app/main/CMakeLists.txt
Normal file
7
code/components/esp-nn/test_app/main/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
set(COMPONENT_SRCS "main.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS "")
|
||||
|
||||
set(COMPONENT_PRIV_REQUIRES tests)
|
||||
|
||||
register_component()
|
||||
8
code/components/esp-nn/test_app/main/component.mk
Normal file
8
code/components/esp-nn/test_app/main/component.mk
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# Main component makefile.
|
||||
#
|
||||
# This Makefile can be left empty. By default, it will take the sources in the
|
||||
# src/ directory, compile them and link them into lib(subdirectory_name).a
|
||||
# in the build directory. This behaviour is entirely configurable,
|
||||
# please read the ESP-IDF documents if you need to do this.
|
||||
#
|
||||
87
code/components/esp-nn/test_app/main/main.c
Normal file
87
code/components/esp-nn/test_app/main/main.c
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <test_functions.h>
|
||||
#include <esp_timer.h>
|
||||
|
||||
static const char *TAG = "test_app";
|
||||
static uint32_t start_c, start_opt, total_c, total_opt;
|
||||
|
||||
void profile_c_start()
|
||||
{
|
||||
/* initiate profiling */
|
||||
start_c = esp_cpu_get_ccount();
|
||||
}
|
||||
|
||||
void profile_c_end()
|
||||
{
|
||||
/* record profile number */
|
||||
total_c = esp_cpu_get_ccount() - start_c;
|
||||
}
|
||||
|
||||
void profile_opt_start()
|
||||
{
|
||||
/* initiate profiling */
|
||||
start_opt = esp_cpu_get_ccount();
|
||||
}
|
||||
|
||||
void profile_opt_end()
|
||||
{
|
||||
/* record profile number */
|
||||
total_opt = esp_cpu_get_ccount() - start_opt;
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
/* s8 tests */
|
||||
ESP_LOGI(TAG, "Running s8 tests...");
|
||||
esp_nn_add_elementwise_s8_test();
|
||||
printf("add, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_mul_elementwise_s8_test();
|
||||
printf("mul, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_depthwise_conv_s8_test();
|
||||
printf("depthwise, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_conv_s8_test();
|
||||
printf("conv2d, c %u opt %u\n", total_c, total_opt);
|
||||
|
||||
esp_nn_relu6_s8_test();
|
||||
printf("relu, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_avg_pool_s8_test();
|
||||
printf("avg_pool, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_max_pool_s8_test();
|
||||
printf("max_pool, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_fully_connected_s8_test();
|
||||
printf("fully_connected, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_softmax_s8_test();
|
||||
printf("softmax, c %u opt %u\n", total_c, total_opt);
|
||||
ESP_LOGI(TAG, "s8 tests done!\n");
|
||||
|
||||
/* u8 tests */
|
||||
//ESP_LOGI(TAG, "Running u8 tests...");
|
||||
//esp_nn_add_elementwise_u8_test();
|
||||
//esp_nn_depthwise_conv_u8_test();
|
||||
//esp_nn_conv_u8_test();
|
||||
//esp_nn_avg_pool_u8_test();
|
||||
//esp_nn_max_pool_u8_test();
|
||||
//esp_nn_fully_connected_u8_test();
|
||||
//ESP_LOGI(TAG, "u8 tests done!\n");
|
||||
}
|
||||
5
code/components/esp-nn/test_app/sdkconfig.defaults
Normal file
5
code/components/esp-nn/test_app/sdkconfig.defaults
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
#
|
||||
# esp-nn
|
||||
#
|
||||
CONFIG_NN_ESP32=y
|
||||
@@ -0,0 +1,8 @@
|
||||
# Default configurations for ESP32-S3
|
||||
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
||||
|
||||
CONFIG_ESP32S3_DATA_CACHE_64KB=y
|
||||
CONFIG_ESP32S3_DATA_CACHE_8WAYS=y
|
||||
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
|
||||
15
code/components/esp-nn/tests/CMakeLists.txt
Normal file
15
code/components/esp-nn/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ./include/)
|
||||
set(COMPONENT_SRCS "src/basic_math_test.c"
|
||||
"src/convolution_test.c"
|
||||
"src/fully_connected_test.c"
|
||||
"src/pooling_test.c"
|
||||
"src/relu_test.c"
|
||||
"src/softmax_test.c")
|
||||
|
||||
set(COMPONENT_REQUIRES )
|
||||
set(COMPONENT_PRIV_REQUIRES esp-nn)
|
||||
|
||||
register_component()
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-function)
|
||||
4
code/components/esp-nn/tests/README.md
Normal file
4
code/components/esp-nn/tests/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Tests for esp_nn library
|
||||
|
||||
- Include these in your test framework and run the framework.
|
||||
- For IDF test please refer `test_app`
|
||||
5
code/components/esp-nn/tests/component.mk
Normal file
5
code/components/esp-nn/tests/component.mk
Normal file
@@ -0,0 +1,5 @@
|
||||
#FIXME
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := include/
|
||||
|
||||
COMPONENT_SRCDIRS := src/
|
||||
48
code/components/esp-nn/tests/include/test_functions.h
Normal file
48
code/components/esp-nn/tests/include/test_functions.h
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
/* int8_t ops tests */
|
||||
void esp_nn_add_elementwise_s8_test();
|
||||
void esp_nn_mul_elementwise_s8_test();
|
||||
|
||||
void esp_nn_depthwise_conv_s8_test();
|
||||
void esp_nn_conv_s8_test();
|
||||
|
||||
void esp_nn_avg_pool_s8_test();
|
||||
void esp_nn_max_pool_s8_test();
|
||||
|
||||
void esp_nn_fully_connected_s8_test();
|
||||
|
||||
void esp_nn_relu6_s8_test();
|
||||
|
||||
void esp_nn_softmax_s8_test();
|
||||
|
||||
/* uint8_t ops tests */
|
||||
void esp_nn_add_elementwise_u8_test();
|
||||
|
||||
void esp_nn_depthwise_conv_u8_test();
|
||||
void esp_nn_conv_u8_test();
|
||||
|
||||
void esp_nn_avg_pool_u8_test();
|
||||
void esp_nn_max_pool_u8_test();
|
||||
|
||||
void esp_nn_fully_connected_u8_test();
|
||||
|
||||
/* instructions test functions */
|
||||
void compare_instructions_test();
|
||||
void arith_instructions_test();
|
||||
void min_max_instructions_test();
|
||||
void bitwise_instructions_test();
|
||||
void load_store_instructions_test();
|
||||
87
code/components/esp-nn/tests/include/test_utils.h
Normal file
87
code/components/esp-nn/tests/include/test_utils.h
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <common_functions.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* mult value range */
|
||||
#define MULT_MAX INT32_MAX
|
||||
#define MULT_MIN 0
|
||||
|
||||
/* shift value range */
|
||||
#define SHIFT_MIN -31
|
||||
#define SHIFT_MAX 30
|
||||
|
||||
/**
|
||||
* @brief callback function to run before C function
|
||||
*/
|
||||
void profile_c_start();
|
||||
|
||||
/**
|
||||
* @brief callback function to run after C function
|
||||
*/
|
||||
void profile_c_end();
|
||||
|
||||
/**
|
||||
* @brief callback function to run before optimized function
|
||||
*/
|
||||
void profile_opt_start();
|
||||
|
||||
/**
|
||||
* @brief callback function to run after optimized function
|
||||
*/
|
||||
void profile_opt_end();
|
||||
|
||||
#define ANSI_COLOR_RED "\x1b[31m"
|
||||
#define ANSI_COLOR_GREEN "\x1b[32m"
|
||||
#define ANSI_COLOR_YELLOW "\x1b[33m"
|
||||
#define ANSI_COLOR_BLUE "\x1b[34m"
|
||||
#define ANSI_COLOR_MAGENTA "\x1b[35m"
|
||||
#define ANSI_COLOR_CYAN "\x1b[36m"
|
||||
#define ANSI_COLOR_RESET "\x1b[0m"
|
||||
|
||||
#define CHECK_EQUAL(ARRAY1, ARRAY2, size) ({ \
|
||||
bool res = true; \
|
||||
for (int _i = 0; _i < size; _i++) { \
|
||||
if (ARRAY1[_i] != ARRAY2[_i]) { \
|
||||
res = false; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
res; \
|
||||
})
|
||||
|
||||
#define PRINT_ARRAY_INT(ARRAY, width, height) ({ \
|
||||
int *_array = (int *) ARRAY; \
|
||||
for (int _j = 0; _j < height; _j++) { \
|
||||
for (int _i = 0; _i < width; _i++) { \
|
||||
printf("%d\t", _array[width * _j + _i]); \
|
||||
} \
|
||||
printf("\n"); \
|
||||
} \
|
||||
printf("\n"); \
|
||||
})
|
||||
|
||||
#define PRINT_ARRAY_HEX(ARRAY, width, height) ({ \
|
||||
uint8_t *_array = (uint8_t *) ARRAY; \
|
||||
for (int _j = 0; _j < height; _j++) { \
|
||||
for (int _i = 0; _i < width; _i++) { \
|
||||
printf("%02x\t", _array[width * _j + _i]); \
|
||||
} \
|
||||
printf("\n"); \
|
||||
} \
|
||||
printf("\n"); \
|
||||
})
|
||||
355
code/components/esp-nn/tests/src/basic_math_test.c
Normal file
355
code/components/esp-nn/tests/src/basic_math_test.c
Normal file
@@ -0,0 +1,355 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
#if CONFIG_IDF_CMAKE
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
#define IDF_HEAP_CAPS 1
|
||||
#endif
|
||||
|
||||
#if IDF_HEAP_CAPS
|
||||
#include "esp_heap_caps.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void esp_nn_add_elementwise_s8_test()
|
||||
{
|
||||
/* prepare data */
|
||||
const int size = 1600 + 8 + 7; /* odd len to test leftover */
|
||||
int8_t *input1;
|
||||
int8_t *input2;
|
||||
int8_t *out_data_c;
|
||||
int8_t *out_data_opt;
|
||||
int8_t *input1_orig = NULL;
|
||||
int8_t *input2_orig = NULL;
|
||||
int8_t *out_c_orig = NULL;
|
||||
int8_t *out_opt_orig = NULL;
|
||||
int32_t input1_offset = 34;
|
||||
int32_t input2_offset = 35;
|
||||
int32_t output_offset = 36;
|
||||
int32_t input1_shift = -8; // right_shift amt always <= 0
|
||||
int32_t input2_shift = -8; // right_shift amt always <= 0
|
||||
int32_t output_shift = -9; // right_shift amt always <= 0
|
||||
int32_t left_shift = 15; // always +ve
|
||||
int32_t input1_mult = INT32_MAX;
|
||||
int32_t input2_mult = INT32_MAX;
|
||||
int32_t output_mult = INT32_MAX;
|
||||
int32_t activation_min = -128;
|
||||
int32_t activation_max = 127;
|
||||
|
||||
for (int itr = 0; itr < 10; itr++) {
|
||||
switch (itr) {
|
||||
case 0: // all zeros
|
||||
input1_offset = 0;
|
||||
input2_offset = 0;
|
||||
output_offset = 0;
|
||||
input1_mult = 0;
|
||||
input2_mult = 0;
|
||||
output_mult = 0;
|
||||
input1_shift = 0;
|
||||
input2_shift = 0;
|
||||
output_shift = 0;
|
||||
left_shift = 0;
|
||||
break;
|
||||
case 1: // hit min
|
||||
input1_offset = -127;
|
||||
input2_offset = -127;
|
||||
output_offset = -128;
|
||||
input1_mult = MULT_MIN;
|
||||
input2_mult = MULT_MIN;
|
||||
output_mult = MULT_MIN;
|
||||
input1_shift = 0;
|
||||
input2_shift = 0;
|
||||
output_shift = 0;
|
||||
left_shift = 0;
|
||||
break;
|
||||
case 2: // hit max
|
||||
input1_offset = 128;
|
||||
input2_offset = 128;
|
||||
output_offset = -127;
|
||||
input1_mult = MULT_MAX;
|
||||
input2_mult = MULT_MAX;
|
||||
output_mult = MULT_MAX;
|
||||
input1_shift = SHIFT_MIN;
|
||||
input2_shift = SHIFT_MIN;
|
||||
output_shift = SHIFT_MIN;
|
||||
left_shift = 30 - 8; // since input is 8 bits
|
||||
break;
|
||||
case 3: // hit extreme max
|
||||
input1_offset = 128;
|
||||
input2_offset = 128;
|
||||
output_offset = -127;
|
||||
input1_mult = MULT_MAX;
|
||||
input2_mult = MULT_MAX;
|
||||
output_mult = MULT_MAX;
|
||||
input1_shift = 0;
|
||||
input2_shift = 0;
|
||||
output_shift = 0;
|
||||
left_shift = 30 - 8; // -8 since input is 8 bit
|
||||
break;
|
||||
default: // practical random input
|
||||
input1_offset = rand() % 256 - 127; // range [-127, 128]
|
||||
input2_offset = rand() % 256 - 127; // range [-127, 128]
|
||||
output_offset = rand() % 256 - 128; // range [-128, 127]
|
||||
input1_mult = MULT_MAX / 2 + rand() % INT16_MAX;
|
||||
input2_mult = MULT_MAX / 2 + rand() % INT16_MAX;
|
||||
output_mult = MULT_MAX / 2 + rand() % INT16_MAX;
|
||||
input1_shift = -8 + rand() % 4;
|
||||
input2_shift = -8 + rand() % 4;
|
||||
output_shift = -8 + rand() % 4;
|
||||
left_shift = rand() % 15;
|
||||
}
|
||||
#if IDF_HEAP_CAPS
|
||||
input1_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
input2_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_c_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_opt_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
|
||||
input1 = 16 + input1_orig - ((uint32_t) input1_orig & 0xf);
|
||||
input2 = 16 + input2_orig - ((uint32_t) input2_orig & 0xf);
|
||||
out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
|
||||
out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
|
||||
#else
|
||||
input1 = memalign(16, size);
|
||||
input2 = memalign(16, size);
|
||||
out_data_c = memalign(16, size);
|
||||
out_data_opt = memalign(16, size);
|
||||
|
||||
input1_orig = input1;
|
||||
input2_orig = input2;
|
||||
out_c_orig = out_data_c;
|
||||
out_opt_orig = out_data_opt;
|
||||
#endif
|
||||
if (input1_orig == NULL || input2_orig == NULL || out_c_orig == NULL ||
|
||||
out_opt_orig == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s error allocating buffers\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto elementwise_add_test_cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input1[i] = rand() % 256 - 128;
|
||||
input2[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
if (itr == 0) {
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
}
|
||||
/* C function */
|
||||
esp_nn_add_elementwise_s8_ansi(input1, input2, input1_offset, input2_offset,
|
||||
input1_mult, input2_mult, input1_shift, input2_shift,
|
||||
left_shift, out_data_c, output_offset, output_mult,
|
||||
output_shift, activation_min, activation_max, size);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
}
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_add_elementwise_s8(input1, input2, input1_offset, input2_offset,
|
||||
input1_mult, input2_mult, input1_shift, input2_shift,
|
||||
left_shift, out_data_opt, output_offset, output_mult,
|
||||
output_shift, activation_min, activation_max, size);
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
}
|
||||
|
||||
bool ret = CHECK_EQUAL(out_data_c, out_data_opt, size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(out_data_opt, size, 1);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(out_data_c, size, 1);
|
||||
printf("Input1:\n");
|
||||
PRINT_ARRAY_HEX(input1, size, 1);
|
||||
printf("Input2:\n");
|
||||
PRINT_ARRAY_HEX(input2, size, 1);
|
||||
printf("in1_shift %d, in2_shift %d, left_shift %d, out_shift %d\n",
|
||||
input1_shift, input2_shift, left_shift, output_shift);
|
||||
printf("in1_mult %d, in2_mult %d, out_mult %d\n", input1_mult, input2_mult, output_mult);
|
||||
goto elementwise_add_test_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
|
||||
elementwise_add_test_cleanup:
|
||||
if (input1_orig) {
|
||||
free(input1_orig);
|
||||
}
|
||||
if (input2_orig) {
|
||||
free(input2_orig);
|
||||
}
|
||||
if (out_c_orig) {
|
||||
free(out_c_orig);
|
||||
}
|
||||
if (out_opt_orig) {
|
||||
free(out_opt_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_mul_elementwise_s8_test()
|
||||
{
|
||||
/* prepare data */
|
||||
const int size = 1600 + 8 + 7; /* odd len to test leftover */
|
||||
int8_t *input1;
|
||||
int8_t *input2;
|
||||
int8_t *out_data_c;
|
||||
int8_t *out_data_opt;
|
||||
int32_t input1_offset = 34;
|
||||
int32_t input2_offset = 35;
|
||||
int32_t output_offset = 36;
|
||||
int32_t output_shift = -7;
|
||||
int32_t output_mult = MULT_MAX; // max out_mult
|
||||
int32_t activation_min = -128;
|
||||
int32_t activation_max = 127;
|
||||
int8_t *input1_orig = NULL;
|
||||
int8_t *input2_orig = NULL;
|
||||
int8_t *out_c_orig = NULL;
|
||||
int8_t *out_opt_orig = NULL;
|
||||
|
||||
for (int itr = 0; itr < 10; itr++) {
|
||||
switch (itr) {
|
||||
case 0: // all zeros
|
||||
input1_offset = 0;
|
||||
input2_offset = 0;
|
||||
output_offset = 0;
|
||||
output_mult = 0;
|
||||
output_shift = 0;
|
||||
break;
|
||||
case 1: // hit min
|
||||
input1_offset = -127;
|
||||
input2_offset = -127;
|
||||
output_offset = -128;
|
||||
output_mult = MULT_MIN;
|
||||
output_shift = 0;
|
||||
break;
|
||||
case 2: // hit max
|
||||
input1_offset = 128;
|
||||
input2_offset = 128;
|
||||
output_offset = -127;
|
||||
output_mult = MULT_MAX;
|
||||
output_shift = SHIFT_MIN;
|
||||
break;
|
||||
case 3: // hit extreme max
|
||||
input1_offset = 128;
|
||||
input2_offset = 128;
|
||||
output_offset = -127;
|
||||
output_mult = MULT_MAX;
|
||||
output_shift = 0;
|
||||
break;
|
||||
default: // practical random input
|
||||
input1_offset = rand() % 256 - 127; // range [-127, 128]
|
||||
input2_offset = rand() % 256 - 127; // range [-127, 128]
|
||||
output_offset = rand() % 256 - 128; // range [-128, 127]
|
||||
output_mult = MULT_MAX / 2 + rand() % INT16_MAX;
|
||||
output_shift = -8 + rand() % 4;
|
||||
}
|
||||
|
||||
#if IDF_HEAP_CAPS
|
||||
input1_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
input2_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_c_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_opt_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
|
||||
input1 = 16 + input1_orig - ((uint32_t) input1_orig & 0xf);
|
||||
input2 = 16 + input2_orig - ((uint32_t) input2_orig & 0xf);
|
||||
out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
|
||||
out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
|
||||
#else
|
||||
input1 = memalign(16, size);
|
||||
input2 = memalign(16, size);
|
||||
out_data_c = memalign(16, size);
|
||||
out_data_opt = memalign(16, size);
|
||||
|
||||
input1_orig = input1;
|
||||
input2_orig = input2;
|
||||
out_c_orig = out_data_c;
|
||||
out_opt_orig = out_data_opt;
|
||||
#endif
|
||||
if (input1_orig == NULL || input2_orig == NULL || out_c_orig == NULL ||
|
||||
out_opt_orig == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s error allocating buffers\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto elementwise_mult_test_cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input1[i] = rand() % 256 - 128;
|
||||
input2[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
if (itr == 0) {
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
}
|
||||
/* C function */
|
||||
esp_nn_mul_elementwise_s8_ansi(input1, input2, input1_offset, input2_offset,
|
||||
out_data_c, output_offset, output_mult, output_shift,
|
||||
activation_min, activation_max, size);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
}
|
||||
/* Optimized function */
|
||||
esp_nn_mul_elementwise_s8(input1, input2, input1_offset, input2_offset,
|
||||
out_data_opt, output_offset, output_mult, output_shift,
|
||||
activation_min, activation_max, size);
|
||||
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
}
|
||||
|
||||
bool ret = CHECK_EQUAL(out_data_c, out_data_opt, size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(out_data_opt, size, 1);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(out_data_c, size, 1);
|
||||
printf("Input1:\n");
|
||||
PRINT_ARRAY_HEX(input1, size, 1);
|
||||
printf("Input2:\n");
|
||||
PRINT_ARRAY_HEX(input2, size, 1);
|
||||
goto elementwise_mult_test_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
|
||||
elementwise_mult_test_cleanup:
|
||||
if (input1_orig) {
|
||||
free(input1_orig);
|
||||
}
|
||||
if (input2_orig) {
|
||||
free(input2_orig);
|
||||
}
|
||||
if (out_c_orig) {
|
||||
free(out_c_orig);
|
||||
}
|
||||
if (out_opt_orig) {
|
||||
free(out_opt_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
605
code/components/esp-nn/tests/src/convolution_test.c
Normal file
605
code/components/esp-nn/tests/src/convolution_test.c
Normal file
@@ -0,0 +1,605 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
#if CONFIG_IDF_CMAKE
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
#define IDF_HEAP_CAPS 1
|
||||
#endif
|
||||
#if IDF_HEAP_CAPS
|
||||
#include "esp_heap_caps.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void esp_nn_depthwise_conv_s8_test()
|
||||
{
|
||||
int8_t *input = NULL, *filter_data = NULL, *out_data_c = NULL, *out_data_opt = NULL;
|
||||
int32_t *bias = NULL;
|
||||
int32_t input_offset = 5; /* some number in [-128, 127] */
|
||||
int32_t out_offset = 7;
|
||||
int32_t activation_min = -125;
|
||||
int32_t activation_max = 120;
|
||||
void *scratch_buf = NULL;
|
||||
|
||||
/* independent variables */
|
||||
int input_wd, input_ht, channels;
|
||||
uint16_t filter_ht, filter_wd, ch_mult;
|
||||
uint16_t pad_wd, pad_ht, stride_wd, stride_ht;
|
||||
|
||||
// run for 15 iterations
|
||||
for (int itr = 0; itr < 15; itr++) {
|
||||
/* prepare data */
|
||||
switch (itr) {
|
||||
case 0: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (0,0)
|
||||
input_wd = 18;
|
||||
input_ht = 18;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 1: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (1,1)
|
||||
input_wd = 10;
|
||||
input_ht = 10;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 2: // (ch_mult 1, (channels % 8) = 0), filter (3,3), pad (1,1)
|
||||
input_wd = 10;
|
||||
input_ht = 10;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 24;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 3: // other filter sizes (ch_mult 1, (channels % 8) = 0)
|
||||
input_wd = 10;
|
||||
input_ht = 10;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 24;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 4: // other filter sizes (ch_mult 8 = 0)
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 8;
|
||||
channels = 4;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 5: // other filter sizes (ch_mult 8 = 0)
|
||||
input_wd = 12;
|
||||
input_ht = 12;
|
||||
filter_ht = 5;
|
||||
filter_wd = 5;
|
||||
ch_mult = 8;
|
||||
channels = 4;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 6: // other filter sizes (ch_mult 4 = 0)
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 4;
|
||||
channels = 4;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 7: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (0,0) stride (2,2)
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 2;
|
||||
stride_ht = 2;
|
||||
break;
|
||||
case 8: // same as case 7, with large parameters
|
||||
input_wd = 58;
|
||||
input_ht = 58;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 128;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 2;
|
||||
stride_ht = 2;
|
||||
break;
|
||||
case 9: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (0,0) stride (2,2)
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 2;
|
||||
stride_ht = 2;
|
||||
break;
|
||||
default:
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
stride_wd = rand() % 2 + 1;
|
||||
stride_ht = stride_wd;
|
||||
pad_wd = stride_wd == 1 ? 0 : rand() % 2;
|
||||
pad_ht = pad_wd;
|
||||
printf("stride(%d), pad (%d)\t", stride_wd, pad_wd);
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t out_wd = (input_wd - filter_wd + 1) / stride_wd;
|
||||
uint16_t out_ht = (input_ht - filter_ht + 1) / stride_ht;
|
||||
if (itr == 9) {
|
||||
// expect the function to handle this gracefully
|
||||
out_wd += 1;
|
||||
out_ht += 1;
|
||||
}
|
||||
int in_size = input_wd * input_ht * channels;
|
||||
int out_size = out_wd * out_ht * channels * ch_mult;
|
||||
int filter_size = filter_wd * filter_ht * channels * ch_mult + 4;
|
||||
int bias_size = channels * ch_mult + 1;
|
||||
int32_t out_shift[channels * ch_mult];
|
||||
int32_t out_mult[channels * ch_mult];
|
||||
|
||||
#if IDF_HEAP_CAPS
|
||||
int8_t *input_orig = (int8_t *) heap_caps_malloc(in_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
int8_t *out_c_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
int8_t *out_opt_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
filter_data = (int8_t *) heap_caps_malloc(filter_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
bias = (int32_t *) heap_caps_malloc(bias_size * 4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
|
||||
input = 16 + input_orig - ((uint32_t) input_orig & 0xf);
|
||||
out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
|
||||
out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
|
||||
#else
|
||||
input = memalign(16, in_size + 16);
|
||||
filter_data = memalign(16, filter_size);
|
||||
out_data_c = memalign(16, out_size + 16);
|
||||
out_data_opt = memalign(16, out_size + 16);
|
||||
bias = memalign(16, bias_size * 4);
|
||||
int8_t *input_orig = input;
|
||||
int8_t *out_c_orig = out_data_c;
|
||||
int8_t *out_opt_orig = out_data_opt;
|
||||
#endif
|
||||
if (bias == NULL || input == NULL || filter_data == NULL ||
|
||||
out_data_c == NULL || out_data_opt == NULL || bias == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
goto dc_s8_cleanup;
|
||||
}
|
||||
|
||||
/* Generate input data */
|
||||
for (int i = 0; i < in_size; ++i) {
|
||||
input[i] = rand() % 128;
|
||||
}
|
||||
|
||||
/* Generate filter data */
|
||||
for (int i = 0; i < filter_size; ++i) {
|
||||
filter_data[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
/* Generate bias data */
|
||||
for (int i = 0; i < channels * ch_mult; ++i) {
|
||||
bias[i + 1] = rand() % INT16_MAX; //0th index left for unalignment
|
||||
out_shift[i] = -8 + rand() % 3;
|
||||
out_mult[i] = 0x7eb0e200 + rand() % 50;
|
||||
}
|
||||
|
||||
data_dims_t input_dims = {.width = input_wd, .height = input_ht, .channels = channels, 1};
|
||||
data_dims_t output_dims = {.width = out_wd, .height = out_ht, .channels = channels * ch_mult, 1};
|
||||
data_dims_t filter_dims = {.width = filter_wd, .height = filter_ht, 0, 0};
|
||||
dw_conv_params_t conv_params = {.in_offset = input_offset, .out_offset = out_offset, .ch_mult = ch_mult,
|
||||
.stride = {stride_wd, stride_ht}, .padding = {pad_wd, pad_ht},
|
||||
.dilation = {0, 0}, .activation = {activation_min, activation_max}};
|
||||
quant_data_t quant_data = {.shift = out_shift, .mult = out_mult};
|
||||
|
||||
int scratch_buf_size = esp_nn_get_depthwise_conv_scratch_size(&input_dims, &filter_dims,
|
||||
&output_dims, &conv_params);
|
||||
if (scratch_buf_size > 0) {
|
||||
#if IDF_HEAP_CAPS
|
||||
scratch_buf = heap_caps_malloc(scratch_buf_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
int align_sz = 16 - (((int32_t) scratch_buf) & 0xf);
|
||||
#else
|
||||
scratch_buf = memalign(16, scratch_buf_size);
|
||||
int align_sz = 0;
|
||||
#endif
|
||||
if (scratch_buf == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] scratch_buf alloc failed size %d\n"ANSI_COLOR_RESET,
|
||||
__FUNCTION__, itr, scratch_buf_size);
|
||||
goto dc_s8_cleanup;
|
||||
}
|
||||
esp_nn_set_depthwise_conv_scratch_buf(scratch_buf + align_sz);
|
||||
}
|
||||
if (itr == 0) {
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
}
|
||||
|
||||
/* C function */
|
||||
esp_nn_depthwise_conv_s8_ansi(&input_dims, input, &filter_dims, filter_data + 4,
|
||||
bias + 1, &output_dims, out_data_c, &conv_params, &quant_data);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
}
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_depthwise_conv_s8(&input_dims, input, &filter_dims, filter_data + 4,
|
||||
bias + 1, &output_dims, out_data_opt, &conv_params, &quant_data);
|
||||
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
}
|
||||
|
||||
bool ret = CHECK_EQUAL(out_data_c, out_data_opt, out_size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(out_data_opt, out_size / out_ht, out_ht);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(out_data_c, out_size / out_ht, out_ht);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, in_size / input_ht, input_ht);
|
||||
printf("Filter data:\n");
|
||||
PRINT_ARRAY_HEX(filter_data + 4, (filter_size - 4) / filter_ht, filter_ht);
|
||||
printf("bias data:\n");
|
||||
PRINT_ARRAY_INT(bias + 1, ch_mult * channels, 1);
|
||||
goto dc_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
|
||||
dc_s8_cleanup:
|
||||
if (input) {
|
||||
free(input_orig);
|
||||
}
|
||||
if (filter_data) {
|
||||
free(filter_data);
|
||||
}
|
||||
if (out_data_c) {
|
||||
free(out_c_orig);
|
||||
}
|
||||
if (out_data_opt) {
|
||||
free(out_opt_orig);
|
||||
}
|
||||
if (bias) {
|
||||
free(bias);
|
||||
}
|
||||
if (scratch_buf) {
|
||||
free(scratch_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_conv_s8_test()
|
||||
{
|
||||
const int32_t input_offset = 5; /* some number in [-128, 127] */
|
||||
const int32_t activation_min = -125;
|
||||
const int32_t activation_max = 122;
|
||||
const int32_t out_offset = 3;
|
||||
|
||||
void *scratch_buf = NULL;
|
||||
int8_t *input_orig;
|
||||
int8_t *out_c_orig;
|
||||
int8_t *out_opt_orig;
|
||||
int8_t *filter_data;
|
||||
int32_t *bias;
|
||||
|
||||
/* independent variable */
|
||||
int in_wd, in_ht, in_channels, out_channels;
|
||||
uint16_t filter_ht, filter_wd;
|
||||
uint16_t pad_wd, pad_ht, stride_wd, stride_ht;
|
||||
|
||||
// run for 10 iterations
|
||||
for (int itr = 0; itr < 10; itr++) {
|
||||
switch (itr) {
|
||||
case 0: // ch % 8 == 0 && filter (1,1), padding (0,0)
|
||||
in_wd = 10;
|
||||
in_ht = 10;
|
||||
in_channels = 64;
|
||||
out_channels = 64;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 1: // ch % 4 == 0 && (in_wd * in_ht) % 16 == 0
|
||||
in_wd = 4;
|
||||
in_ht = 4;
|
||||
in_channels = 20;
|
||||
out_channels = 8;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 2: // ch, filter (3x3x3)
|
||||
in_wd = 10;
|
||||
in_ht = 10;
|
||||
in_channels = 3;
|
||||
out_channels = 64;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 3: // remaining pad (0, 0)
|
||||
in_wd = 10;
|
||||
in_ht = 10;
|
||||
in_channels = 3;
|
||||
out_channels = 64;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 4: // unopt case
|
||||
in_wd = 10;
|
||||
in_ht = 10;
|
||||
in_channels = 12;
|
||||
out_channels = 64;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 5: // ch % 8 == 0 & stride (2,2)
|
||||
in_wd = 16;
|
||||
in_ht = 16;
|
||||
in_channels = 16;
|
||||
out_channels = 16;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 2;
|
||||
stride_ht = 2;
|
||||
break;
|
||||
case 6: // ch % 8 == 0 && filter (1,1), padding (0,0)
|
||||
in_wd = 2;
|
||||
in_ht = 2;
|
||||
in_channels = 8;
|
||||
out_channels = 8;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
default: // ch % 8 == 0
|
||||
in_wd = 8;
|
||||
in_ht = 8;
|
||||
in_channels = 16;
|
||||
out_channels = 16;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* prepare data */
|
||||
uint16_t out_wd = (in_wd - filter_wd + 1) / stride_wd;
|
||||
uint16_t out_ht = (in_ht - filter_ht + 1) / stride_ht;
|
||||
|
||||
int in_size = in_wd * in_ht * in_channels;
|
||||
int filter_size = filter_wd * filter_ht * in_channels * out_channels + 2;
|
||||
int out_size = out_wd * out_ht * out_channels;
|
||||
|
||||
#if IDF_HEAP_CAPS
|
||||
input_orig = (int8_t *) heap_caps_malloc(in_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_c_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_opt_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
filter_data = (int8_t *) heap_caps_malloc(filter_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
bias = (int32_t *) heap_caps_malloc(128 + sizeof (int32_t) * out_channels, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
|
||||
int8_t *input = 16 + input_orig - ((uint32_t) input_orig & 0xf);
|
||||
int8_t *out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
|
||||
int8_t *out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
|
||||
#else
|
||||
int8_t *input = memalign(16, in_size);
|
||||
int8_t *out_data_c = memalign(16, out_size);
|
||||
int8_t *out_data_opt = memalign(16, out_size);
|
||||
filter_data = memalign(16, filter_size);
|
||||
bias = calloc(1, 128 + sizeof (int32_t) * out_channels);
|
||||
input_orig = input;
|
||||
out_c_orig = out_data_c;
|
||||
out_opt_orig = out_data_opt;
|
||||
#endif
|
||||
int32_t *out_shift = calloc(1, 128 + sizeof (int32_t) * out_channels);
|
||||
int32_t *out_mult = calloc(1, 128 + sizeof (int32_t) * out_channels);
|
||||
|
||||
if (input == NULL || filter_data == NULL ||
|
||||
out_data_c == NULL || out_data_opt == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto conv_s8_cleanup;
|
||||
}
|
||||
|
||||
if (bias == NULL || out_shift == NULL || out_mult == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto conv_s8_cleanup;
|
||||
}
|
||||
|
||||
/* Generate input data between -128 -> +127 */
|
||||
for (int i = 0; i < in_size; ++i) {
|
||||
input[i] = rand() % 255 - 128;
|
||||
}
|
||||
|
||||
/* Generate filter data between -128 -> +127 */
|
||||
for (int i = 0; i < filter_size; ++i) {
|
||||
filter_data[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
/* Generate bias data */
|
||||
for (int i = 0; i < out_channels; ++i) {
|
||||
bias[i] = (int32_t)rand() % UINT16_MAX + UINT8_MAX;
|
||||
}
|
||||
|
||||
/* Shift and multiplier */
|
||||
for (int i = 0; i < out_channels; ++i) {
|
||||
out_shift[i] = -10 + rand() % 2;
|
||||
out_mult[i] = 0x7f67f4f8 + rand() % 50;
|
||||
}
|
||||
|
||||
data_dims_t input_dims = {.width = in_wd, .height = in_ht, .channels = in_channels, 1};
|
||||
data_dims_t output_dims = {.width = out_wd, .height = out_ht, .channels = out_channels, 1};
|
||||
data_dims_t filter_dims = {.width = filter_wd, .height = filter_ht, 0, 0};
|
||||
conv_params_t conv_params = {.in_offset = input_offset, .out_offset = out_offset,
|
||||
.stride = {stride_wd, stride_ht}, .padding = {pad_wd, pad_ht},
|
||||
.dilation = {0, 0}, .activation = {activation_min, activation_max}};
|
||||
quant_data_t quant_data = {.shift = out_shift, .mult = out_mult};
|
||||
|
||||
int scratch_buf_size = esp_nn_get_conv_scratch_size(&input_dims, &filter_dims,
|
||||
&output_dims, &conv_params);
|
||||
if (scratch_buf_size > 0) {
|
||||
#if IDF_HEAP_CAPS
|
||||
void *scratch_buf = heap_caps_malloc(scratch_buf_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
int align_sz = 16 - (((int32_t) scratch_buf) & 0xf);
|
||||
#else
|
||||
void *scratch_buf = memalign(16, scratch_buf_size);
|
||||
int align_sz = 0;
|
||||
#endif
|
||||
if (scratch_buf == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s scratch_buf alloc failed size %d\n"ANSI_COLOR_RESET, __FUNCTION__, scratch_buf_size);
|
||||
goto conv_s8_cleanup;
|
||||
}
|
||||
esp_nn_set_conv_scratch_buf(scratch_buf + align_sz);
|
||||
}
|
||||
|
||||
if (itr == 0) {
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
}
|
||||
|
||||
/* C function */
|
||||
esp_nn_conv_s8_ansi(&input_dims, input, &filter_dims, filter_data + 2,
|
||||
bias, &output_dims, out_data_c, &conv_params, &quant_data);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
}
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_conv_s8(&input_dims, input, &filter_dims, filter_data + 2,
|
||||
bias, &output_dims, out_data_opt, &conv_params, &quant_data);
|
||||
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
}
|
||||
|
||||
bool ret = CHECK_EQUAL(out_data_c, out_data_opt, out_size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(out_data_opt, out_size / out_ht, out_ht);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(out_data_c, out_size / out_ht, out_ht);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, in_size / in_ht, in_ht);
|
||||
printf("Filter data:\n");
|
||||
PRINT_ARRAY_HEX(filter_data + 2, (filter_size - 2) / filter_ht, filter_ht);
|
||||
printf("bias data:\n");
|
||||
PRINT_ARRAY_INT(bias, out_channels, 1);
|
||||
goto conv_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
|
||||
conv_s8_cleanup:
|
||||
if (input) {
|
||||
free(input_orig);
|
||||
}
|
||||
if (filter_data) {
|
||||
free(filter_data);
|
||||
}
|
||||
if (out_data_c) {
|
||||
free(out_c_orig);
|
||||
}
|
||||
if (out_data_opt) {
|
||||
free(out_opt_orig);
|
||||
}
|
||||
if (bias) {
|
||||
free(bias);
|
||||
}
|
||||
if (out_shift) {
|
||||
free(out_shift);
|
||||
}
|
||||
if (out_mult) {
|
||||
free(out_mult);
|
||||
}
|
||||
if (scratch_buf) {
|
||||
free(scratch_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
111
code/components/esp-nn/tests/src/fully_connected_test.c
Normal file
111
code/components/esp-nn/tests/src/fully_connected_test.c
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
|
||||
void esp_nn_fully_connected_s8_test()
|
||||
{
|
||||
/* prepare data */
|
||||
static uint16_t row_len = 256 + 8 + 7; /* odd len to test unaligned+left-over */
|
||||
static uint16_t out_channels = 3;
|
||||
int8_t input[row_len];
|
||||
int8_t filter_data[row_len * out_channels];
|
||||
int8_t output_c[out_channels], output_opt[out_channels];
|
||||
static int32_t activation_min = -128;
|
||||
static int32_t activation_max = 127;
|
||||
static int32_t input_offset = 0;
|
||||
static int32_t filter_offset = 0;
|
||||
int32_t out_shift = -10;
|
||||
static int32_t out_offset = 127;
|
||||
int32_t out_mult = 0x59e492c4;
|
||||
for (int itr = 0; itr < 5; itr++) {
|
||||
out_mult = INT32_MAX / row_len + rand() % INT16_MAX;
|
||||
switch (itr) {
|
||||
case 0:
|
||||
out_shift = -10;
|
||||
break;
|
||||
case 1:
|
||||
out_shift = SHIFT_MIN;
|
||||
break;
|
||||
case 2:
|
||||
out_shift = SHIFT_MAX;
|
||||
break;
|
||||
case 3:
|
||||
out_shift = 0;
|
||||
break;
|
||||
default:
|
||||
out_shift = -10 + rand() % 5;
|
||||
break;
|
||||
}
|
||||
if (itr == 0) {
|
||||
out_shift = SHIFT_MAX;
|
||||
}
|
||||
/* Generate input and filter data */
|
||||
for (int i = 0; i < row_len; ++i) {
|
||||
input[i] = rand() % 256 - 128;
|
||||
}
|
||||
for (int i = 0; i < row_len * out_channels; ++i) {
|
||||
filter_data[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
if (itr == 0) {
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
}
|
||||
|
||||
/* C function */
|
||||
esp_nn_fully_connected_s8_ansi(input, input_offset, row_len, filter_data, filter_offset,
|
||||
NULL, output_c, out_channels, out_offset, out_shift, out_mult,
|
||||
activation_min, activation_max);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
}
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_fully_connected_s8(input, input_offset, row_len, filter_data, filter_offset,
|
||||
NULL, output_opt, out_channels, out_offset, out_shift, out_mult,
|
||||
activation_min, activation_max);
|
||||
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
}
|
||||
|
||||
bool ret = CHECK_EQUAL(output_c, output_opt, out_channels);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(output_opt, out_channels, 1);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(output_c, out_channels, 1);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, row_len, 1);
|
||||
printf("Filter data:\n");
|
||||
PRINT_ARRAY_HEX(filter_data, row_len, out_channels);
|
||||
printf("Out shift: %d\n", out_shift);
|
||||
printf("Out mult: %x\n", out_mult);
|
||||
return;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
}
|
||||
}
|
||||
184
code/components/esp-nn/tests/src/pooling_test.c
Normal file
184
code/components/esp-nn/tests/src/pooling_test.c
Normal file
@@ -0,0 +1,184 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
|
||||
void esp_nn_avg_pool_s8_test()
|
||||
{
|
||||
/* prepare data */
|
||||
const uint16_t input_wd = 16;
|
||||
const uint16_t input_ht = 16;
|
||||
const uint16_t channels = 16; /* With TFLite example, I have seen it 256 */
|
||||
const int size = input_wd * input_ht * channels;
|
||||
int8_t *input, *output_c, *output_opt;
|
||||
const int32_t activation_min = -128;
|
||||
const int32_t activation_max = 127;
|
||||
const uint16_t pad_wd = 1;
|
||||
const uint16_t pad_ht = 1;
|
||||
const uint16_t stride_wd = 1;
|
||||
const uint16_t stride_ht = 1;
|
||||
const uint16_t filter_ht = 3;
|
||||
const uint16_t filter_wd = 3;
|
||||
const uint16_t out_wd = input_wd / stride_wd;
|
||||
const uint16_t out_ht = input_ht / stride_ht;
|
||||
const int out_size = out_wd * out_ht * channels;
|
||||
|
||||
input = memalign(16, size);
|
||||
output_c = memalign(16, out_size);
|
||||
output_opt = memalign(16, out_size);
|
||||
|
||||
if (input == NULL || output_c == NULL || output_opt == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto avg_pool_s8_cleanup;
|
||||
}
|
||||
/**
|
||||
* width/height, channels etc look suspicious but it it true.
|
||||
* It actually depends upon where in model this is actually placed.
|
||||
* If at the end wd/ht tends to be smaller and depth larger.
|
||||
*/
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
|
||||
/* C function */
|
||||
esp_nn_avg_pool_s8_ansi(input, input_wd, input_ht, output_c, out_wd, out_ht,
|
||||
stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
|
||||
activation_min, activation_max, channels);
|
||||
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_avg_pool_s8(input, input_wd, input_ht, output_opt, out_wd, out_ht,
|
||||
stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
|
||||
activation_min, activation_max, channels);
|
||||
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
|
||||
|
||||
bool ret = CHECK_EQUAL(output_c, output_opt, out_size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(output_opt, out_wd * channels, out_ht);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(output_c, out_wd * channels, out_ht);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, input_wd * channels, input_ht);
|
||||
goto avg_pool_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
|
||||
avg_pool_s8_cleanup:
|
||||
if (input) {
|
||||
free(input);
|
||||
}
|
||||
if (output_c) {
|
||||
free(output_c);
|
||||
}
|
||||
if (output_opt) {
|
||||
free(output_opt);
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_max_pool_s8_test()
|
||||
{
|
||||
/* prepare data */
|
||||
const uint16_t input_wd = 16;
|
||||
const uint16_t input_ht = 16;
|
||||
const uint16_t channels = 16; /* With TFLite example, I have seen it 256 */
|
||||
int8_t *input, *output_c, *output_opt;
|
||||
const int size = input_wd * input_ht * channels;
|
||||
const int32_t activation_min = -128;
|
||||
const int32_t activation_max = 127;
|
||||
const uint16_t pad_wd = 1;
|
||||
const uint16_t pad_ht = 1;
|
||||
const uint16_t stride_wd = 1;
|
||||
const uint16_t stride_ht = 1;
|
||||
const uint16_t filter_ht = 3;
|
||||
const uint16_t filter_wd = 3;
|
||||
const uint16_t out_wd = input_wd / stride_wd;
|
||||
const uint16_t out_ht = input_ht / stride_ht;
|
||||
const int out_size = out_wd * out_ht * channels;
|
||||
|
||||
input = memalign(16, size);
|
||||
output_c = memalign(16, out_size);
|
||||
output_opt = memalign(16, out_size);
|
||||
|
||||
if (input == NULL || output_c == NULL || output_opt == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto max_pool_s8_cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
|
||||
/* C function */
|
||||
esp_nn_max_pool_s8_ansi(input, input_wd, input_ht, output_c, out_wd, out_ht,
|
||||
stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
|
||||
activation_min, activation_max, channels);
|
||||
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_max_pool_s8(input, input_wd, input_ht, output_opt, out_wd, out_ht,
|
||||
stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
|
||||
activation_min, activation_max, channels);
|
||||
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
|
||||
|
||||
bool ret = CHECK_EQUAL(output_c, output_opt, out_wd * out_ht * channels);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(output_opt, out_wd * out_ht * channels, 1);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(output_c, out_wd * out_ht * channels, 1);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, 8, size / 8);
|
||||
goto max_pool_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
|
||||
max_pool_s8_cleanup:
|
||||
if (input) {
|
||||
free(input);
|
||||
}
|
||||
if (output_c) {
|
||||
free(output_c);
|
||||
}
|
||||
if (output_opt) {
|
||||
free(output_opt);
|
||||
}
|
||||
}
|
||||
83
code/components/esp-nn/tests/src/relu_test.c
Normal file
83
code/components/esp-nn/tests/src/relu_test.c
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
void esp_nn_relu6_s8_test()
|
||||
{
|
||||
const int size = 1600 + 8 + 7;
|
||||
int8_t *input, *inout_ansi, *inout_opt;
|
||||
|
||||
input = memalign(16, size);
|
||||
inout_ansi = memalign(16, size);
|
||||
inout_opt = memalign(16, size);
|
||||
|
||||
if (input == NULL || inout_ansi == NULL || inout_opt == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto relu6_s8_cleanup;
|
||||
}
|
||||
/* Generate filter data between -128 -> +127 */
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input[i] = rand() % 255 - 128;
|
||||
inout_ansi[i] = input[i];
|
||||
inout_opt[i] = input[i];
|
||||
}
|
||||
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
|
||||
/* C function */
|
||||
esp_nn_relu6_s8_ansi(inout_ansi, size);
|
||||
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_relu6_s8(inout_opt, size);
|
||||
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
|
||||
bool ret = CHECK_EQUAL(inout_ansi, inout_opt, size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(inout_opt, size, 1);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(inout_ansi, size, 1);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, size, 1);
|
||||
goto relu6_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
|
||||
relu6_s8_cleanup:
|
||||
if (input) {
|
||||
free (input);
|
||||
}
|
||||
if (inout_ansi) {
|
||||
free (inout_ansi);
|
||||
}
|
||||
if (inout_opt) {
|
||||
free (inout_opt);
|
||||
}
|
||||
|
||||
}
|
||||
101
code/components/esp-nn/tests/src/softmax_test.c
Normal file
101
code/components/esp-nn/tests/src/softmax_test.c
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
void esp_nn_softmax_s8_test()
|
||||
{
|
||||
const int32_t height = 8;
|
||||
const int32_t width = 32;
|
||||
const int32_t diff_min = -128;
|
||||
const int32_t mult = INT32_MAX / 2;
|
||||
const int32_t shift = 7;
|
||||
void *scratch_buf = NULL;
|
||||
const int size = width * height;
|
||||
int8_t *input, *out_ansi, *out_opt;
|
||||
|
||||
input = memalign(16, size);
|
||||
out_ansi = memalign(16, size);
|
||||
out_opt = memalign(16, size);
|
||||
|
||||
if (input == NULL || out_ansi == NULL || out_opt == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s buffer allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto softmax_s8_cleanup;
|
||||
}
|
||||
|
||||
/* Generate input data between -128 -> +127 */
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input[i] = rand() % 255 - 128;
|
||||
}
|
||||
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
|
||||
/* C function */
|
||||
esp_nn_softmax_s8_ansi(input, height, width, mult, shift, diff_min, out_ansi);
|
||||
|
||||
profile_c_end();
|
||||
|
||||
int32_t scratch_buf_size = esp_nn_get_softmax_scratch_size(width, height);
|
||||
if (scratch_buf_size) {
|
||||
scratch_buf = memalign(4, scratch_buf_size);
|
||||
if (scratch_buf == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s scratch_buf alloc failed size %d\n"ANSI_COLOR_RESET, __FUNCTION__, scratch_buf_size);
|
||||
goto softmax_s8_cleanup;
|
||||
}
|
||||
esp_nn_set_softmax_scratch_buf(scratch_buf);
|
||||
}
|
||||
|
||||
profile_opt_start();
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_softmax_s8(input, height, width, mult, shift, diff_min, out_opt);
|
||||
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
|
||||
bool ret = CHECK_EQUAL(out_ansi, out_opt, size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(out_opt, width, height);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(out_ansi, width, height);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, width, height);
|
||||
goto softmax_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
|
||||
softmax_s8_cleanup:
|
||||
if (input) {
|
||||
free (input);
|
||||
}
|
||||
if (out_ansi) {
|
||||
free (out_ansi);
|
||||
}
|
||||
if (out_opt) {
|
||||
free (out_opt);
|
||||
}
|
||||
if (scratch_buf) {
|
||||
free (scratch_buf);
|
||||
}
|
||||
}
|
||||
BIN
code/components/esp-nn_20220716.zip
Normal file
BIN
code/components/esp-nn_20220716.zip
Normal file
Binary file not shown.
Binary file not shown.
@@ -8,26 +8,37 @@ on:
|
||||
jobs:
|
||||
build-master:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
idf_target: ["esp32", "esp32s2", "esp32s3"]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: esp-idf build
|
||||
uses: espressif/esp-idf-ci-action@latest
|
||||
uses: espressif/esp-idf-ci-action@main
|
||||
with:
|
||||
target: ${{ matrix.idf_target }}
|
||||
path: 'examples'
|
||||
|
||||
build-release-v4_0:
|
||||
build-release-v4_4:
|
||||
name: Build for ${{ matrix.idf_target }} on ${{ matrix.idf_ver }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["v4.4"]
|
||||
idf_target: ["esp32", "esp32s2", "esp32s3"]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: esp-idf build
|
||||
uses: espressif/esp-idf-ci-action@release-v4.0
|
||||
uses: espressif/esp-idf-ci-action@main
|
||||
with:
|
||||
esp_idf_version: ${{ matrix.idf_ver }}
|
||||
target: ${{ matrix.idf_target }}
|
||||
path: 'examples'
|
||||
|
||||
build-release-v4_1:
|
||||
@@ -65,15 +76,3 @@ jobs:
|
||||
uses: espressif/esp-idf-ci-action@release-v4.3
|
||||
with:
|
||||
path: 'examples'
|
||||
|
||||
build-release-v3_3:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: esp-idf build
|
||||
uses: espressif/esp-idf-ci-action@release-v3.3
|
||||
with:
|
||||
path: 'examples'
|
||||
|
||||
@@ -10,12 +10,10 @@ jobs:
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Upload component to the component registry
|
||||
uses: espressif/github-actions/upload_components@master
|
||||
with:
|
||||
name: "esp32-camera"
|
||||
version: "git"
|
||||
namespace: "espressif"
|
||||
service_url: ${{ secrets.IDF_COMPONENT_API_URL }}
|
||||
version: ${{ github.ref_name }}
|
||||
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# get IDF version for comparison
|
||||
set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}")
|
||||
|
||||
# set conversion sources
|
||||
set(COMPONENT_SRCS
|
||||
conversions/yuv.c
|
||||
conversions/to_jpg.cpp
|
||||
conversions/to_bmp.c
|
||||
conversions/jpge.cpp
|
||||
conversions/esp_jpg_decode.c
|
||||
)
|
||||
|
||||
set(COMPONENT_PRIV_INCLUDEDIRS
|
||||
conversions/private_include
|
||||
)
|
||||
|
||||
set(COMPONENT_ADD_INCLUDEDIRS
|
||||
driver/include
|
||||
conversions/include
|
||||
)
|
||||
|
||||
set(COMPONENT_REQUIRES driver)
|
||||
|
||||
# set driver sources only for supported platforms
|
||||
if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3")
|
||||
set(COMPONENT_SRCS
|
||||
list(APPEND COMPONENT_SRCS
|
||||
driver/esp_camera.c
|
||||
driver/cam_hal.c
|
||||
driver/sccb.c
|
||||
@@ -14,22 +38,14 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
|
||||
sensors/gc2145.c
|
||||
sensors/gc032a.c
|
||||
sensors/bf3005.c
|
||||
conversions/yuv.c
|
||||
conversions/to_jpg.cpp
|
||||
conversions/to_bmp.c
|
||||
conversions/jpge.cpp
|
||||
conversions/esp_jpg_decode.c
|
||||
sensors/bf20a6.c
|
||||
sensors/sc101iot.c
|
||||
sensors/sc030iot.c
|
||||
)
|
||||
|
||||
set(COMPONENT_ADD_INCLUDEDIRS
|
||||
driver/include
|
||||
conversions/include
|
||||
)
|
||||
|
||||
set(COMPONENT_PRIV_INCLUDEDIRS
|
||||
list(APPEND COMPONENT_PRIV_INCLUDEDIRS
|
||||
driver/private_include
|
||||
sensors/private_include
|
||||
conversions/private_include
|
||||
target/private_include
|
||||
)
|
||||
|
||||
@@ -58,8 +74,13 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
|
||||
)
|
||||
endif()
|
||||
|
||||
set(COMPONENT_REQUIRES driver)
|
||||
set(COMPONENT_PRIV_REQUIRES freertos nvs_flash)
|
||||
|
||||
register_component()
|
||||
set(min_version_for_esp_timer "4.2")
|
||||
if (idf_version VERSION_GREATER_EQUAL min_version_for_esp_timer)
|
||||
list(APPEND COMPONENT_PRIV_REQUIRES esp_timer)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
register_component()
|
||||
|
||||
@@ -69,6 +69,45 @@ menu "Camera configuration"
|
||||
help
|
||||
Enable this option if you want to use the BF3005.
|
||||
Disable this option to save memory.
|
||||
|
||||
config BF20A6_SUPPORT
|
||||
bool "Support BF20A6(BYD20A6) VGA"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the BF20A6.
|
||||
Disable this option to save memory.
|
||||
|
||||
config SC101IOT_SUPPORT
|
||||
bool "Support SC101IOT HD"
|
||||
default n
|
||||
help
|
||||
Enable this option if you want to use the SC101IOT.
|
||||
Disable this option to save memory.
|
||||
|
||||
choice SC101_REGS_SELECT
|
||||
prompt "SC101iot default regs"
|
||||
default SC101IOT_720P_15FPS_ENABLED
|
||||
depends on SC101IOT_SUPPORT
|
||||
help
|
||||
Currently SC010iot has several register sets available.
|
||||
Select the one that matches your needs.
|
||||
|
||||
config SC101IOT_720P_15FPS_ENABLED
|
||||
bool "xclk20M_720p_15fps"
|
||||
help
|
||||
Select this option means that when xclk is 20M, the frame rate is 15fps at 720p resolution.
|
||||
config SC101IOT_VGA_25FPS_ENABLED
|
||||
bool "xclk20M_VGA_25fps"
|
||||
help
|
||||
Select this option means that when xclk is 20M, the frame rate is 25fps at VGA resolution.
|
||||
endchoice
|
||||
|
||||
config SC030IOT_SUPPORT
|
||||
bool "Support SC030IOT VGA"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the SC030IOT.
|
||||
Disable this option to save memory.
|
||||
|
||||
choice SCCB_HARDWARE_I2C_PORT
|
||||
bool "I2C peripheral to use for SCCB"
|
||||
@@ -125,5 +164,24 @@ menu "Camera configuration"
|
||||
help
|
||||
Maximum value of DMA buffer
|
||||
Larger values may fail to allocate due to insufficient contiguous memory blocks, and smaller value may cause DMA interrupt to be too frequent
|
||||
|
||||
config CAMERA_CONVERTER_ENABLED
|
||||
bool "Enable camera RGB/YUV converter"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
default n
|
||||
help
|
||||
Enable this option if you want to use RGB565/YUV422/YUV420/YUV411 format conversion.
|
||||
|
||||
choice CAMERA_CONV_PROTOCOL
|
||||
bool "Camera converter protocol"
|
||||
depends on CAMERA_CONVERTER_ENABLED
|
||||
default LCD_CAM_CONV_BT601_ENABLED
|
||||
help
|
||||
Supports format conversion under both BT601 and BT709 standards.
|
||||
|
||||
config LCD_CAM_CONV_BT601_ENABLED
|
||||
bool "BT601"
|
||||
config LCD_CAM_CONV_BT709_ENABLED
|
||||
bool "BT709"
|
||||
endchoice
|
||||
endmenu
|
||||
|
||||
@@ -25,6 +25,9 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi
|
||||
| GC0308 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/6.5" |
|
||||
| GC2145 | 1600 x 1200 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/5" |
|
||||
| BF3005 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/4" |
|
||||
| BF20A6 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer | 1/10" |
|
||||
| SC101IOT| 1280 x 720 | color | YUV/YCbCr422<br/>Raw RGB | 1/4.2" |
|
||||
| SC030IOT| 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer | 1/6.5" |
|
||||
|
||||
## Important to Remember
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
#include "tjpgd.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/tjpgd.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/tjpgd.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#include "esp32h2/rom/tjpgd.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
@@ -57,7 +61,7 @@ static const char * jd_errors[] = {
|
||||
"Not supported JPEG standard"
|
||||
};
|
||||
|
||||
static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
|
||||
static unsigned int _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
|
||||
{
|
||||
uint16_t x = rect->left;
|
||||
uint16_t y = rect->top;
|
||||
@@ -73,7 +77,7 @@ static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t _jpg_read(JDEC *decoder, uint8_t *buf, uint32_t len)
|
||||
static unsigned int _jpg_read(JDEC *decoder, uint8_t *buf, unsigned int len)
|
||||
{
|
||||
esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device;
|
||||
if (jpeg->len && len > (jpeg->len - jpeg->index)) {
|
||||
|
||||
@@ -29,7 +29,12 @@ namespace jpge {
|
||||
if(b){
|
||||
return b;
|
||||
}
|
||||
// check if SPIRAM is enabled and allocate on SPIRAM if allocatable
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
return heap_caps_malloc(nSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
static inline void jpge_free(void *p) { free(p); }
|
||||
|
||||
|
||||
@@ -21,19 +21,6 @@
|
||||
#include "esp_jpg_decode.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/spiram.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "esp_spiram.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
@@ -72,7 +59,12 @@ typedef struct {
|
||||
|
||||
static void *_malloc(size_t size)
|
||||
{
|
||||
// check if SPIRAM is enabled and allocate on SPIRAM if allocatable
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
#endif
|
||||
// try allocating in internal memory
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
//output buffer and image width
|
||||
@@ -168,7 +160,7 @@ static bool _rgb565_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16
|
||||
}
|
||||
|
||||
//input buffer
|
||||
static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
|
||||
static unsigned int _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
|
||||
{
|
||||
rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
|
||||
if(buf) {
|
||||
|
||||
@@ -21,21 +21,6 @@
|
||||
#include "jpge.h"
|
||||
#include "yuv.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/spiram.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "esp_spiram.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define TAG ""
|
||||
@@ -50,7 +35,12 @@ static void *_malloc(size_t size)
|
||||
if(res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// check if SPIRAM is enabled and is allocatable
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static IRAM_ATTR void convert_line_format(uint8_t * src, pixformat_t format, uint8_t * dst, size_t width, size_t in_channels, size_t line)
|
||||
|
||||
@@ -18,8 +18,21 @@
|
||||
#include "ll_cam.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
static const char *TAG = "cam_hal";
|
||||
#if (ESP_IDF_VERSION_MAJOR == 3) && (ESP_IDF_VERSION_MINOR == 3)
|
||||
#include "rom/ets_sys.h"
|
||||
#else
|
||||
#include "esp_timer.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include "esp32/rom/ets_sys.h" // will be removed in idf v5.0
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/ets_sys.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/ets_sys.h"
|
||||
#endif
|
||||
#endif // ESP_IDF_VERSION_MAJOR
|
||||
#define ESP_CAMERA_ETS_PRINTF ets_printf
|
||||
|
||||
static const char *TAG = "cam_hal";
|
||||
static cam_obj_t *cam_obj = NULL;
|
||||
|
||||
static const uint32_t JPEG_SOI_MARKER = 0xFFD8FF; // written in little-endian for esp32
|
||||
@@ -93,7 +106,7 @@ void IRAM_ATTR ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType
|
||||
if (xQueueSendFromISR(cam->event_queue, (void *)&cam_event, HPTaskAwoken) != pdTRUE) {
|
||||
ll_cam_stop(cam);
|
||||
cam->state = CAM_STATE_IDLE;
|
||||
ESP_EARLY_LOGE(TAG, "EV-%s-OVF", cam_event==CAM_IN_SUC_EOF_EVENT ? "EOF" : "VSYNC");
|
||||
ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: EV-%s-OVF\r\n"), cam_event==CAM_IN_SUC_EOF_EVENT ? DRAM_STR("EOF") : DRAM_STR("VSYNC"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,15 @@
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
#include "bf3005.h"
|
||||
#endif
|
||||
#if CONFIG_BF20A6_SUPPORT
|
||||
#include "bf20a6.h"
|
||||
#endif
|
||||
#if CONFIG_SC101IOT_SUPPORT
|
||||
#include "sc101iot.h"
|
||||
#endif
|
||||
#if CONFIG_SC030IOT_SUPPORT
|
||||
#include "sc030iot.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
@@ -119,6 +128,15 @@ static const sensor_func_t g_sensors[] = {
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
{bf3005_detect, bf3005_init},
|
||||
#endif
|
||||
#if CONFIG_BF20A6_SUPPORT
|
||||
{bf20a6_detect, bf20a6_init},
|
||||
#endif
|
||||
#if CONFIG_SC101IOT_SUPPORT
|
||||
{sc101iot_detect, sc101iot_init},
|
||||
#endif
|
||||
#if CONFIG_SC030IOT_SUPPORT
|
||||
{sc030iot_detect, sc030iot_init},
|
||||
#endif
|
||||
};
|
||||
|
||||
static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
|
||||
@@ -218,6 +236,23 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
static pixformat_t get_output_data_format(camera_conv_mode_t conv_mode)
|
||||
{
|
||||
pixformat_t format = PIXFORMAT_RGB565;
|
||||
switch (conv_mode) {
|
||||
case YUV422_TO_YUV420:
|
||||
format = PIXFORMAT_YUV420;
|
||||
break;
|
||||
case YUV422_TO_RGB565: // default format is RGB565
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ESP_LOGD(TAG, "Convert to %d format enabled", format);
|
||||
return format;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_camera_init(const camera_config_t *config)
|
||||
{
|
||||
esp_err_t err;
|
||||
@@ -256,6 +291,7 @@ esp_err_t esp_camera_init(const camera_config_t *config)
|
||||
|
||||
s_state->sensor.status.framesize = frame_size;
|
||||
s_state->sensor.pixformat = pix_format;
|
||||
|
||||
ESP_LOGD(TAG, "Setting frame size to %dx%d", resolution[frame_size].width, resolution[frame_size].height);
|
||||
if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
|
||||
ESP_LOGE(TAG, "Failed to set frame size");
|
||||
@@ -263,6 +299,11 @@ esp_err_t esp_camera_init(const camera_config_t *config)
|
||||
goto fail;
|
||||
}
|
||||
s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
if(config->conv_mode) {
|
||||
s_state->sensor.pixformat = get_output_data_format(config->conv_mode); // If conversion enabled, change the out data format by conversion mode
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s_state->sensor.id.PID == OV2640_PID) {
|
||||
s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
#include "driver/ledc.h"
|
||||
#include "sensor.h"
|
||||
#include "sys/time.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -91,6 +92,19 @@ typedef enum {
|
||||
CAMERA_FB_IN_DRAM /*!< Frame buffer is placed in internal DRAM */
|
||||
} camera_fb_location_t;
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
/**
|
||||
* @brief Camera RGB\YUV conversion mode
|
||||
*/
|
||||
typedef enum {
|
||||
CONV_DISABLE,
|
||||
RGB565_TO_YUV422,
|
||||
|
||||
YUV422_TO_RGB565,
|
||||
YUV422_TO_YUV420
|
||||
} camera_conv_mode_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for camera initialization
|
||||
*/
|
||||
@@ -124,6 +138,9 @@ typedef struct {
|
||||
size_t fb_count; /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) */
|
||||
camera_fb_location_t fb_location; /*!< The location where the frame buffer will be allocated */
|
||||
camera_grab_mode_t grab_mode; /*!< When buffers should be filled */
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
camera_conv_mode_t conv_mode; /*!< RGB<->YUV Conversion mode */
|
||||
#endif
|
||||
} camera_config_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,9 @@ typedef enum {
|
||||
GC032A_PID = 0x232a,
|
||||
GC0308_PID = 0x9b,
|
||||
BF3005_PID = 0x30,
|
||||
BF20A6_PID = 0x20a6,
|
||||
SC101IOT_PID = 0xda4a,
|
||||
SC030IOT_PID = 0x9a46,
|
||||
} camera_pid_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -40,6 +43,9 @@ typedef enum {
|
||||
CAMERA_GC032A,
|
||||
CAMERA_GC0308,
|
||||
CAMERA_BF3005,
|
||||
CAMERA_BF20A6,
|
||||
CAMERA_SC101IOT,
|
||||
CAMERA_SC030IOT,
|
||||
CAMERA_MODEL_MAX,
|
||||
CAMERA_NONE,
|
||||
} camera_model_t;
|
||||
@@ -55,11 +61,15 @@ typedef enum {
|
||||
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
BF3005_SCCB_ADDR = 0x6E,
|
||||
BF20A6_SCCB_ADDR = 0x6E,
|
||||
SC101IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
|
||||
SC030IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
|
||||
} camera_sccb_addr_t;
|
||||
|
||||
typedef enum {
|
||||
PIXFORMAT_RGB565, // 2BPP/RGB565
|
||||
PIXFORMAT_YUV422, // 2BPP/YUV422
|
||||
PIXFORMAT_YUV420, // 1.5BPP/YUV420
|
||||
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
|
||||
PIXFORMAT_JPEG, // JPEG/COMPRESSED
|
||||
PIXFORMAT_RGB888, // 3BPP/RGB888
|
||||
|
||||
@@ -25,6 +25,11 @@ static const char* TAG = "sccb";
|
||||
|
||||
#include "driver/i2c.h"
|
||||
|
||||
// support IDF 5.x
|
||||
#ifndef portTICK_RATE_MS
|
||||
#define portTICK_RATE_MS portTICK_PERIOD_MS
|
||||
#endif
|
||||
|
||||
#define SCCB_FREQ CONFIG_SCCB_CLK_FREQ /*!< I2C master frequency*/
|
||||
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||
|
||||
@@ -13,6 +13,9 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
|
||||
{CAMERA_GC032A, "GC032A", GC032A_SCCB_ADDR, GC032A_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_GC0308, "GC0308", GC0308_SCCB_ADDR, GC0308_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_BF3005, "BF3005", BF3005_SCCB_ADDR, BF3005_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_BF20A6, "BF20A6", BF20A6_SCCB_ADDR, BF20A6_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_SC101IOT, "SC101IOT", SC101IOT_SCCB_ADDR, SC101IOT_PID, FRAMESIZE_HD, false},
|
||||
{CAMERA_SC030IOT, "SC030IOT", SC030IOT_SCCB_ADDR, SC030IOT_PID, FRAMESIZE_VGA, false},
|
||||
};
|
||||
|
||||
const resolution_info_t resolution[FRAMESIZE_INVALID] = {
|
||||
|
||||
@@ -38,6 +38,11 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
// support IDF 5.x
|
||||
#ifndef portTICK_RATE_MS
|
||||
#define portTICK_RATE_MS portTICK_PERIOD_MS
|
||||
#endif
|
||||
|
||||
#include "esp_camera.h"
|
||||
|
||||
#define BOARD_WROVER_KIT 1
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
description: ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors.
|
||||
targets:
|
||||
- esp32
|
||||
- esp32s2
|
||||
- esp32s3
|
||||
url: https://github.com/espressif/esp32-camera
|
||||
|
||||
404
code/components/esp32-camera-master/sensors/bf20a6.c
Normal file
404
code/components/esp32-camera-master/sensors/bf20a6.c
Normal file
@@ -0,0 +1,404 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sccb.h"
|
||||
#include "bf20a6.h"
|
||||
#include "bf20a6_regs.h"
|
||||
#include "bf20a6_settings.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "bf20a6";
|
||||
#endif
|
||||
|
||||
#define H8(v) ((v)>>8)
|
||||
#define L8(v) ((v)&0xff)
|
||||
|
||||
//#define REG_DEBUG_ON
|
||||
|
||||
static int read_reg(uint8_t slv_addr, const uint16_t reg)
|
||||
{
|
||||
int ret = SCCB_Read(slv_addr, reg);
|
||||
// ESP_LOGI(TAG, "READ Register 0x%02x VALUE: 0x%02x", reg, ret);
|
||||
#ifdef REG_DEBUG_ON
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
|
||||
{
|
||||
int ret = SCCB_Write(slv_addr, reg, value);
|
||||
#ifdef REG_DEBUG_ON
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PRINT_REG
|
||||
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
|
||||
{
|
||||
return (read_reg(slv_addr, reg) & mask) == mask;
|
||||
}
|
||||
|
||||
static void print_regs(uint8_t slv_addr)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
ESP_LOGI(TAG, "REG list look ======================");
|
||||
for (size_t i = 0xf0; i <= 0xfe; i++) {
|
||||
ESP_LOGI(TAG, "reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
ESP_LOGI(TAG, "\npage 0 ===");
|
||||
write_reg(slv_addr, 0xfe, 0x00); // page 0
|
||||
for (size_t i = 0x03; i <= 0x24; i++) {
|
||||
ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
for (size_t i = 0x40; i <= 0x95; i++) {
|
||||
ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
ESP_LOGI(TAG, "\npage 3 ===");
|
||||
write_reg(slv_addr, 0xfe, 0x03); // page 3
|
||||
for (size_t i = 0x01; i <= 0x43; i++) {
|
||||
ESP_LOGI(TAG, "p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
}
|
||||
|
||||
static int read_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
|
||||
{
|
||||
int i = 0, ret = 0;
|
||||
while (regs[i][0] != REGLIST_TAIL) {
|
||||
if (regs[i][0] == REG_DLY) {
|
||||
vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
ret = read_reg(slv_addr, regs[i][0]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = SCCB_Read(sensor->slv_addr, reg);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
value = (ret & ~mask) | ((value << offset) & mask);
|
||||
ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
|
||||
{
|
||||
int i = 0, ret = 0;
|
||||
while (!ret && regs[i][0] != REGLIST_TAIL) {
|
||||
if (regs[i][0] == REG_DLY) {
|
||||
vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret;
|
||||
// Software Reset: clear all registers and reset them to their default values
|
||||
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x01);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Software Reset FAILED!");
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
ret = write_regs(sensor->slv_addr, bf20a6_default_init_regs);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Camera defaults loaded");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
// int test_value = read_regs(sensor->slv_addr, bf20a6_default_init_regs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret = 0;
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_YUV422:
|
||||
set_reg_bits(sensor, 0x12, 0, 1, 0);
|
||||
break;
|
||||
case PIXFORMAT_RAW:
|
||||
set_reg_bits(sensor, 0x12, 0, 1, 0x1);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "set_pix unsupport format");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
if (ret == 0) {
|
||||
sensor->pixformat = pixformat;
|
||||
ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
int ret = 0;
|
||||
if (framesize > FRAMESIZE_VGA) {
|
||||
return -1;
|
||||
}
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
|
||||
// Write MSBs
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, 0);
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, w >> 2);
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, 0);
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, h >> 2);
|
||||
|
||||
// Write LSBs
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1b, 0);
|
||||
|
||||
if ((w <= 320) && (h <= 240)) {
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 4));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 4));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 4));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 4));
|
||||
|
||||
} else if ((w <= 640) && (h <= 480)) {
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 8));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 8));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 8));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 8));
|
||||
}
|
||||
|
||||
// Delay
|
||||
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->status.hmirror = enable;
|
||||
//ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret |= set_reg_bits(sensor, 0x4a, 3, 0x01, enable);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set h-mirror to: %d", enable);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->status.vflip = enable;
|
||||
//ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret |= set_reg_bits(sensor, 0x4a, 2, 0x01, enable);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set v-flip to: %d", enable);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = write_reg(sensor->slv_addr, 0xb6, value);
|
||||
if (ret == 0) {
|
||||
sensor->status.colorbar = value;
|
||||
ESP_LOGD(TAG, "Set colorbar to: %d", value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_sharpness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Write(sensor->slv_addr, 0x70, level);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set sharpness to: %d", level);
|
||||
sensor->status.sharpness = level;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_reg(sensor_t *sensor, int reg, int mask)
|
||||
{
|
||||
int ret = 0;
|
||||
if (mask > 0xFF) {
|
||||
ESP_LOGE(TAG, "mask should not more than 0xff");
|
||||
} else {
|
||||
ret = read_reg(sensor->slv_addr, reg);
|
||||
}
|
||||
if (ret > 0) {
|
||||
ret &= mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
if (mask > 0xFF) {
|
||||
ESP_LOGE(TAG, "mask should not more than 0xff");
|
||||
} else {
|
||||
ret = read_reg(sensor->slv_addr, reg);
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
value = (ret & ~mask) | (value & mask);
|
||||
|
||||
if (mask > 0xFF) {
|
||||
|
||||
} else {
|
||||
ret = write_reg(sensor->slv_addr, reg, value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
// write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x6f);
|
||||
sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0xd6);
|
||||
sensor->status.saturation = 0;
|
||||
sensor->status.sharpness = SCCB_Read(sensor->slv_addr, 0x70);
|
||||
sensor->status.denoise = 0;
|
||||
sensor->status.ae_level = 0;
|
||||
sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x13);
|
||||
sensor->status.awb = 0;
|
||||
sensor->status.dcw = 0;
|
||||
sensor->status.agc = 0;
|
||||
sensor->status.aec = 0;
|
||||
sensor->status.hmirror = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x01);
|
||||
sensor->status.vflip = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x02);
|
||||
sensor->status.colorbar = 0;
|
||||
sensor->status.bpc = 0;
|
||||
sensor->status.wpc = 0;
|
||||
sensor->status.raw_gma = 0;
|
||||
sensor->status.lenc = 0;
|
||||
sensor->status.quality = 0;
|
||||
sensor->status.special_effect = 0;
|
||||
sensor->status.wb_mode = 0;
|
||||
sensor->status.awb_gain = 0;
|
||||
sensor->status.agc_gain = 0;
|
||||
sensor->status.aec_value = 0;
|
||||
sensor->status.aec2 = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val)
|
||||
{
|
||||
ESP_LOGW(TAG, "dummy Unsupported");
|
||||
return -1;
|
||||
}
|
||||
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
|
||||
{
|
||||
ESP_LOGW(TAG, "gainceiling Unsupported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bf20a6_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (BF20A6_SCCB_ADDR == slv_addr) {
|
||||
uint8_t MIDL = SCCB_Read(slv_addr, SENSOR_ID_LOW);
|
||||
uint8_t MIDH = SCCB_Read(slv_addr, SENSOR_ID_HIGH);
|
||||
uint16_t PID = MIDH << 8 | MIDL;
|
||||
if (BF20A6_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bf20a6_init(sensor_t *sensor)
|
||||
{
|
||||
sensor->init_status = init_status;
|
||||
sensor->reset = reset;
|
||||
sensor->set_pixformat = set_pixformat;
|
||||
sensor->set_framesize = set_framesize;
|
||||
sensor->set_contrast = set_dummy;
|
||||
sensor->set_brightness = set_dummy;
|
||||
sensor->set_saturation = set_dummy;
|
||||
sensor->set_sharpness = set_sharpness;
|
||||
sensor->set_denoise = set_dummy;
|
||||
sensor->set_gainceiling = set_gainceiling_dummy;
|
||||
sensor->set_quality = set_dummy;
|
||||
sensor->set_colorbar = set_colorbar;
|
||||
sensor->set_whitebal = set_dummy;
|
||||
sensor->set_gain_ctrl = set_dummy;
|
||||
sensor->set_exposure_ctrl = set_dummy;
|
||||
sensor->set_hmirror = set_hmirror; // set_hmirror;
|
||||
sensor->set_vflip = set_vflip; // set_vflip;
|
||||
|
||||
sensor->set_aec2 = set_dummy;
|
||||
sensor->set_awb_gain = set_dummy;
|
||||
sensor->set_agc_gain = set_dummy;
|
||||
sensor->set_aec_value = set_dummy;
|
||||
|
||||
sensor->set_special_effect = set_dummy;
|
||||
sensor->set_wb_mode = set_dummy;
|
||||
sensor->set_ae_level = set_dummy;
|
||||
|
||||
sensor->set_dcw = set_dummy;
|
||||
sensor->set_bpc = set_dummy;
|
||||
sensor->set_wpc = set_dummy;
|
||||
|
||||
sensor->set_raw_gma = set_dummy;
|
||||
sensor->set_lenc = set_dummy;
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = set_reg;
|
||||
sensor->set_res_raw = NULL;
|
||||
sensor->set_pll = NULL;
|
||||
sensor->set_xclk = NULL;
|
||||
|
||||
ESP_LOGD(TAG, "BF20A6 Attached");
|
||||
return 0;
|
||||
}
|
||||
@@ -88,10 +88,10 @@ static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
|
||||
static int write_regs(uint8_t slv_addr, const uint8_t (*regs)[2], size_t regs_size)
|
||||
{
|
||||
int i = 0, ret = 0;
|
||||
while (!ret && regs[i][0] != REGLIST_TAIL) {
|
||||
while (!ret && (i < regs_size)) {
|
||||
if (regs[i][0] == REG_DLY) {
|
||||
vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
@@ -132,11 +132,12 @@ static int reset(sensor_t *sensor)
|
||||
ESP_LOGE(TAG, "Software Reset FAILED!");
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs);
|
||||
|
||||
vTaskDelay(80 / portTICK_PERIOD_MS);
|
||||
ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs, sizeof(gc0308_sensor_default_regs)/(sizeof(uint8_t) * 2));
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Camera defaults loaded");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
vTaskDelay(80 / portTICK_PERIOD_MS);
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
set_reg_bits(sensor->slv_addr, 0x28, 4, 0x07, 1); //frequency division for esp32, ensure pclk <= 15MHz
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
#ifndef __BF20A6_H__
|
||||
#define __BF20A6_H__
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
/**
|
||||
* @brief Detect sensor pid
|
||||
*
|
||||
* @param slv_addr SCCB address
|
||||
* @param id Detection result
|
||||
* @return
|
||||
* 0: Can't detect this sensor
|
||||
* Nonzero: This sensor has been detected
|
||||
*/
|
||||
int bf20a6_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int bf20a6_init(sensor_t *sensor);
|
||||
|
||||
#endif // __BF20A6_H__
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* BF20A6 register definitions.
|
||||
*/
|
||||
#ifndef __BF20A6_REG_REGS_H__
|
||||
#define __BF20A6_REG_REGS_H__
|
||||
|
||||
#define SENSOR_ID_HIGH 0XFC
|
||||
#define SENSOR_ID_LOW 0XFD
|
||||
#define RESET_RELATED 0XF2
|
||||
|
||||
|
||||
#endif //__BF20A6_REG_REGS_H__
|
||||
@@ -0,0 +1,158 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define REG_DLY 0xffff
|
||||
#define REGLIST_TAIL 0xffff /* Array end token */
|
||||
|
||||
static const uint16_t bf20a6_default_init_regs[][2] = {
|
||||
{0xf2,0x01},
|
||||
{0x12,0x20},
|
||||
{0x3a,0x00},
|
||||
{0xe1,0x92},
|
||||
{0xe3,0x12},// PLL Control, important for framerate(choice: 0x02\0x12\0x22\0x32\0x82)
|
||||
{0xe0,0x00},
|
||||
{0x2a,0x98},
|
||||
{0xcd,0x17},
|
||||
{0xc0,0x10},
|
||||
{0xc6,0x1d},
|
||||
{0x10,0x35},
|
||||
{0xe2,0x09},
|
||||
{0xe4,0x72},
|
||||
{0xe5,0x22},
|
||||
{0xe6,0x24},
|
||||
{0xe7,0x64},
|
||||
{0xe8,0xa2}, // DVP:a2}, SPI:f2 VDDIO=1.8V,E8[2]=1},VDDIO=2.8V,E8[2]=0},
|
||||
{0x4a,0x00},
|
||||
{0x00,0x03},
|
||||
{0x1f,0x02},
|
||||
{0x22,0x02},
|
||||
{0x0c,0x31},
|
||||
|
||||
{0x00,0x00},
|
||||
{0x60,0x81},
|
||||
{0x61,0x81},
|
||||
|
||||
{0xa0,0x08},
|
||||
{0x01,0x1a},
|
||||
// {0x01,0x1a},
|
||||
// {0x01,0x1a},
|
||||
// {0x02,0x15},
|
||||
// {0x02,0x15},
|
||||
{0x02,0x15},
|
||||
{0x13,0x08},
|
||||
{0x8a,0x96},
|
||||
{0x8b,0x06},
|
||||
{0x87,0x18},
|
||||
|
||||
|
||||
{0x34,0x48}, // lens
|
||||
{0x35,0x40},
|
||||
{0x36,0x40},
|
||||
|
||||
{0x71,0x44},
|
||||
{0x72,0x48},
|
||||
{0x74,0xa2},
|
||||
{0x75,0xa9},
|
||||
{0x78,0x12},
|
||||
{0x79,0xa0},
|
||||
{0x7a,0x94},
|
||||
{0x7c,0x97},
|
||||
{0x40,0x30},
|
||||
{0x41,0x30},
|
||||
{0x42,0x28},
|
||||
{0x43,0x1f},
|
||||
{0x44,0x1c},
|
||||
{0x45,0x16},
|
||||
{0x46,0x13},
|
||||
{0x47,0x10},
|
||||
{0x48,0x0D},
|
||||
{0x49,0x0C},
|
||||
{0x4B,0x0A},
|
||||
{0x4C,0x0B},
|
||||
{0x4E,0x09},
|
||||
{0x4F,0x08},
|
||||
{0x50,0x08},
|
||||
|
||||
|
||||
{0x5f,0x29},
|
||||
{0x23,0x33},
|
||||
{0xa1,0x10}, // AWB
|
||||
{0xa2,0x0d},
|
||||
{0xa3,0x30},
|
||||
{0xa4,0x06},
|
||||
{0xa5,0x22},
|
||||
{0xa6,0x56},
|
||||
{0xa7,0x18},
|
||||
{0xa8,0x1a},
|
||||
{0xa9,0x12},
|
||||
{0xaa,0x12},
|
||||
{0xab,0x16},
|
||||
{0xac,0xb1},
|
||||
{0xba,0x12},
|
||||
{0xbb,0x12},
|
||||
{0xad,0x12},
|
||||
{0xae,0x56},
|
||||
{0xaf,0x0a},
|
||||
{0x3b,0x30},
|
||||
{0x3c,0x12},
|
||||
{0x3d,0x22},
|
||||
{0x3e,0x3f},
|
||||
{0x3f,0x28},
|
||||
{0xb8,0xc3},
|
||||
{0xb9,0xa3},
|
||||
{0x39,0x47}, // pure color threshold
|
||||
{0x26,0x13},
|
||||
{0x27,0x16},
|
||||
{0x28,0x14},
|
||||
{0x29,0x18},
|
||||
{0xee,0x0d},
|
||||
|
||||
|
||||
{0x13,0x05},
|
||||
{0x24,0x3C},
|
||||
{0x81,0x20},
|
||||
{0x82,0x40},
|
||||
{0x83,0x30},
|
||||
{0x84,0x58},
|
||||
{0x85,0x30},
|
||||
{0x92,0x08},
|
||||
{0x86,0x80},
|
||||
{0x8a,0x96},
|
||||
{0x91,0xff},
|
||||
{0x94,0x62},
|
||||
{0x9a,0x18}, // outdoor threshold
|
||||
{0xf0,0x45}, // integral time control, important for framerate(choice: 0x46\0x45\0x44..)
|
||||
{0x51,0x17}, // color normal
|
||||
{0x52,0x03},
|
||||
{0x53,0x5F},
|
||||
{0x54,0x47},
|
||||
{0x55,0x66},
|
||||
{0x56,0x0F},
|
||||
{0x7e,0x14},
|
||||
{0x57,0x36}, // color
|
||||
{0x58,0x2A},
|
||||
{0x59,0xAA},
|
||||
{0x5a,0xA8},
|
||||
{0x5b,0x43},
|
||||
{0x5c,0x10},
|
||||
{0x5d,0x00},
|
||||
{0x7d,0x36},
|
||||
{0x5e,0x10},
|
||||
|
||||
{0xd6,0x88}, // contrast
|
||||
{0xd5,0x20}, // bright
|
||||
{0xb0,0x84}, // low light ctrl in gray section
|
||||
{0xb5,0x08}, // the threshold of GLB_GAIN
|
||||
{0xb1,0xc8}, // saturation
|
||||
{0xb2,0xc0},
|
||||
{0xb3,0xd0},
|
||||
{0xb4,0xB0},
|
||||
|
||||
{0x32,0x10},
|
||||
// {0x8a,0x00},
|
||||
// {0x8b,0x10},
|
||||
{0xa0,0x09},
|
||||
{0x00,0x03},
|
||||
{0x0b,0x02},
|
||||
{REGLIST_TAIL, 0x00},
|
||||
};
|
||||
@@ -3,10 +3,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define REG_DLY 0xffff
|
||||
#define REGLIST_TAIL 0x0000 /* Array end token */
|
||||
#define REG_DLY 0xff
|
||||
|
||||
static const uint16_t gc0308_sensor_default_regs[][2] = {
|
||||
static const uint8_t gc0308_sensor_default_regs[][2] = {
|
||||
{0xfe, 0x00},
|
||||
{0xec, 0x20},
|
||||
{0x05, 0x00},
|
||||
@@ -239,7 +238,21 @@ static const uint16_t gc0308_sensor_default_regs[][2] = {
|
||||
{0x65, 0xd3},
|
||||
{0x66, 0x60},
|
||||
{0xfe, 0x00},
|
||||
{REGLIST_TAIL, 0x00},
|
||||
|
||||
{0x01, 0x32}, //frame setting
|
||||
{0x02, 0x0c},
|
||||
{0x0f, 0x01},
|
||||
{0xe2, 0x00},
|
||||
{0xe3, 0x78},
|
||||
{0xe4, 0x00},
|
||||
{0xe5, 0xfe},
|
||||
{0xe6, 0x01},
|
||||
{0xe7, 0xe0},
|
||||
{0xe8, 0x01},
|
||||
{0xe9, 0xe0},
|
||||
{0xea, 0x01},
|
||||
{0xeb, 0xe0},
|
||||
{0xfe, 0x00},
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,7 +42,8 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
|
||||
{ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
|
||||
|
||||
//sys reset
|
||||
{0x3000, 0x00},
|
||||
{0x3000, 0x20}, // reset MCU
|
||||
{REG_DLY, 10}, // delay 10ms
|
||||
{0x3002, 0x1c},
|
||||
|
||||
//clock enable
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
*
|
||||
* SC030IOT DVP driver.
|
||||
*
|
||||
*/
|
||||
#ifndef __SC030IOT_H__
|
||||
#define __SC030IOT_H__
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
/**
|
||||
* @brief Detect sensor pid
|
||||
*
|
||||
* @param slv_addr SCCB address
|
||||
* @param id Detection result
|
||||
* @return
|
||||
* 0: Can't detect this sensor
|
||||
* Nonzero: This sensor has been detected
|
||||
*/
|
||||
int sc030iot_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int sc030iot_init(sensor_t *sensor);
|
||||
|
||||
#endif // __SC030IOT_H__
|
||||
@@ -0,0 +1,491 @@
|
||||
//version: V01P00_20220303
|
||||
//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
|
||||
//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
|
||||
//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
|
||||
//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
|
||||
//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
|
||||
//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
|
||||
//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
|
||||
//pin :BIT0 pwdn// BIT1:reset
|
||||
//avdd 0:3.3V// 1:2.5V// 2:1.8V
|
||||
//dovdd 0:2.8V// 1:2.5V// 2:1.8V
|
||||
//dvdd 0:1.8V// 1:1.5V// 2:1.2V
|
||||
|
||||
/*
|
||||
[DataBase]
|
||||
DBName=Dothinkey
|
||||
|
||||
[Vendor]
|
||||
VendorName=SmartSens
|
||||
|
||||
[Sensor]
|
||||
SensorName=SC031IOT
|
||||
width=640
|
||||
height=480
|
||||
port=1
|
||||
type=2
|
||||
pin=3
|
||||
SlaveID=0xd0
|
||||
mode=0
|
||||
FlagReg=0xf7
|
||||
FlagMask=0xff
|
||||
FlagData=0xfa
|
||||
FlagReg1=0xf8
|
||||
FlagMask1=0xff
|
||||
FlagData1=0x46
|
||||
outformat=0
|
||||
mclk=20
|
||||
avdd=2.80000
|
||||
dovdd=2.800000
|
||||
dvdd=1.5
|
||||
|
||||
Ext0=0
|
||||
Ext1=0
|
||||
Ext2=0
|
||||
AFVCC=0.0000
|
||||
VPP=0.000000
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t sc030iot_default_init_regs[][2] = {
|
||||
{0xf0, 0x30},
|
||||
{0x01, 0xff},
|
||||
{0x02, 0xff},
|
||||
{0x22, 0x07},
|
||||
{0x19, 0xff},
|
||||
{0x3f, 0x82},
|
||||
{0x30, 0x02},
|
||||
{0xf0, 0x01},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0x72, 0x20},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x75, 0x10},
|
||||
{0x76, 0x81},
|
||||
{0x77, 0x88},
|
||||
{0x78, 0xe1},
|
||||
{0x79, 0x01},
|
||||
{0xf5, 0x01},
|
||||
{0xf4, 0x0a},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x79},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x33},
|
||||
{0x02, 0x12},
|
||||
{0x7c, 0x02},
|
||||
{0x7d, 0x0e},
|
||||
{0xa2, 0x04},
|
||||
{0x5e, 0x06},
|
||||
{0x5f, 0x0a},
|
||||
{0x0b, 0x58},
|
||||
{0x06, 0x38},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x39},
|
||||
{0x02, 0x70},
|
||||
{0xf0, 0x45},
|
||||
{0x09, 0x1c},
|
||||
{0xf0, 0x37},
|
||||
{0x22, 0x0d},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x10},
|
||||
{0xb1, 0x80},
|
||||
{0x34, 0x40},
|
||||
{0x0b, 0x54},
|
||||
{0xb2, 0x78},
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x30},
|
||||
{0x38, 0x44},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x01, 0x10},
|
||||
{0x0b, 0x6c},
|
||||
{0x06, 0x24},
|
||||
{0xf0, 0x36},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x02},
|
||||
{0xa6, 0x40},
|
||||
{0xa7, 0x47},
|
||||
{0xe8, 0x5f},
|
||||
{0xa8, 0x51},
|
||||
{0xa9, 0x44},
|
||||
{0xe9, 0x36},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x64, 0x17},
|
||||
{0x90, 0x01},
|
||||
{0x91, 0x03},
|
||||
{0x92, 0x07},
|
||||
{0x01, 0x10},
|
||||
{0x93, 0x10},
|
||||
{0x94, 0x10},
|
||||
{0x95, 0x10},
|
||||
{0x96, 0x01},
|
||||
{0x97, 0x07},
|
||||
{0x98, 0x1f},
|
||||
{0x99, 0x10},
|
||||
{0x9a, 0x20},
|
||||
{0x9b, 0x28},
|
||||
{0x9c, 0x28},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0x54},
|
||||
{0xb6, 0x40},
|
||||
{0xb7, 0x41},
|
||||
{0xb8, 0x43},
|
||||
{0xb9, 0x47},
|
||||
{0xba, 0x4f},
|
||||
{0xb0, 0x8b},
|
||||
{0xb1, 0x8b},
|
||||
{0xb2, 0x8b},
|
||||
{0xb3, 0x9b},
|
||||
{0xb4, 0xb8},
|
||||
{0xb5, 0xf0},
|
||||
{0x7e, 0x41},
|
||||
{0x7f, 0x47},
|
||||
{0x77, 0x80},
|
||||
{0x78, 0x84},
|
||||
{0x79, 0x8a},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x43},
|
||||
{0x97, 0x44},
|
||||
{0x98, 0x54},
|
||||
{0xf0, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x74},
|
||||
{0xf0, 0x3f},
|
||||
{0x03, 0xa1},
|
||||
{0xf0, 0x36},//cvbs_off
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
{0xf0, 0x37},
|
||||
{0x24, 0x21},
|
||||
{0xf0, 0x36},
|
||||
{0x41, 0x00},
|
||||
{0xea, 0x09},
|
||||
{0xeb, 0x03},
|
||||
{0xec, 0x19},
|
||||
{0xed, 0x38},
|
||||
{0xe9, 0x30},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x00},
|
||||
{0x34, 0x00},
|
||||
{0xb1, 0x00},
|
||||
{0xf0, 0x00},
|
||||
{0xe0, 0x04},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x32, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x3e, 0xe0},
|
||||
{0x70, 0x56},
|
||||
{0x7c, 0x43},
|
||||
{0x7d, 0x47},
|
||||
{0x74, 0x00},
|
||||
{0x75, 0x00},
|
||||
{0x76, 0x00},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x22},
|
||||
{0x97, 0x22},
|
||||
{0x98, 0x22},
|
||||
{0xf0, 0x00},
|
||||
{0x72, 0x38},
|
||||
{0x7a, 0x80},
|
||||
{0x85, 0x18},
|
||||
{0x9b, 0x35},
|
||||
{0x9e, 0x20},
|
||||
{0xd0, 0x66},
|
||||
{0xd1, 0x34},
|
||||
{0Xd3, 0x44},
|
||||
{0xd6, 0x44},
|
||||
{0xb0, 0x41},
|
||||
{0xb2, 0x48},
|
||||
{0xb3, 0xf4},
|
||||
{0xb4, 0x0b},
|
||||
{0xb5, 0x78},
|
||||
{0xba, 0xff},
|
||||
{0xbb, 0xc0},
|
||||
{0xbc, 0x90},
|
||||
{0xbd, 0x3a},
|
||||
{0xc1, 0x67},
|
||||
{0xf0, 0x01},
|
||||
{0x20, 0x11},
|
||||
{0x23, 0x90},
|
||||
{0x24, 0x15},
|
||||
{0x25, 0x87},
|
||||
{0xbc, 0x9f},
|
||||
{0xbd, 0x3a},
|
||||
{0x48, 0xe6},
|
||||
{0x49, 0xc0},
|
||||
{0x4a, 0xd0},
|
||||
{0x4b, 0x48},
|
||||
|
||||
// [cvbs_on]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xf1},
|
||||
|
||||
// [cvbs_off]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
};
|
||||
|
||||
/*
|
||||
[Sensor]
|
||||
SensorName=SC031IOT
|
||||
width=640
|
||||
height=480
|
||||
port=1
|
||||
type=2
|
||||
pin=3
|
||||
SlaveID=0xd0
|
||||
mode=0
|
||||
FlagReg=0xf7
|
||||
FlagMask=0xff
|
||||
FlagData=0xfa
|
||||
FlagReg1=0xf8
|
||||
FlagMask1=0xff
|
||||
FlagData1=0x46
|
||||
outformat=0
|
||||
mclk=27
|
||||
avdd=2.80000
|
||||
dovdd=2.800000
|
||||
dvdd=1.5
|
||||
|
||||
Ext0=0
|
||||
Ext1=0
|
||||
Ext2=0
|
||||
AFVCC=0.0000
|
||||
VPP=0.000000
|
||||
*/
|
||||
/* 27M MCLK, 30fps
|
||||
static const uint8_t sc030iot_default_init_regs[][2] = {
|
||||
{0xf0, 0x30},
|
||||
{0x01, 0xff},
|
||||
{0x02, 0xff},
|
||||
{0x22, 0x07},
|
||||
{0x19, 0xff},
|
||||
{0x3f, 0x82},
|
||||
{0x30, 0x02},
|
||||
{0xf0, 0x01},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0x72, 0x20},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x75, 0x10},
|
||||
{0x76, 0x81},
|
||||
{0x77, 0x88},
|
||||
{0x78, 0xe1},
|
||||
{0x79, 0x01},
|
||||
{0xf5, 0x01},
|
||||
{0xf4, 0x0a},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x79},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x33},
|
||||
{0x02, 0x12},
|
||||
{0x7c, 0x02},
|
||||
{0x7d, 0x0e},
|
||||
{0xa2, 0x04},
|
||||
{0x5e, 0x06},
|
||||
{0x5f, 0x0a},
|
||||
{0x0b, 0x58},
|
||||
{0x06, 0x38},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x39},
|
||||
{0x02, 0x70},
|
||||
{0xf0, 0x45},
|
||||
{0x09, 0x1c},
|
||||
{0xf0, 0x37},
|
||||
{0x22, 0x0d},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x10},
|
||||
{0xb1, 0x80},
|
||||
{0x34, 0x40},
|
||||
{0x0b, 0x54},
|
||||
{0xb2, 0x78},
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x30},
|
||||
{0x38, 0x44},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x01, 0x10},
|
||||
{0x0b, 0x6c},
|
||||
{0x06, 0x24},
|
||||
{0xf0, 0x36},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x02},
|
||||
{0xa6, 0x40},
|
||||
{0xa7, 0x47},
|
||||
{0xe8, 0x5f},
|
||||
{0xa8, 0x51},
|
||||
{0xa9, 0x44},
|
||||
{0xe9, 0x36},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x64, 0x17},
|
||||
{0x90, 0x01},
|
||||
{0x91, 0x03},
|
||||
{0x92, 0x07},
|
||||
{0x01, 0x10},
|
||||
{0x93, 0x10},
|
||||
{0x94, 0x10},
|
||||
{0x95, 0x10},
|
||||
{0x96, 0x01},
|
||||
{0x97, 0x07},
|
||||
{0x98, 0x1f},
|
||||
{0x99, 0x10},
|
||||
{0x9a, 0x20},
|
||||
{0x9b, 0x28},
|
||||
{0x9c, 0x28},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0x54},
|
||||
{0xb6, 0x40},
|
||||
{0xb7, 0x41},
|
||||
{0xb8, 0x43},
|
||||
{0xb9, 0x47},
|
||||
{0xba, 0x4f},
|
||||
{0xb0, 0x8b},
|
||||
{0xb1, 0x8b},
|
||||
{0xb2, 0x8b},
|
||||
{0xb3, 0x9b},
|
||||
{0xb4, 0xb8},
|
||||
{0xb5, 0xf0},
|
||||
{0x7e, 0x41},
|
||||
{0x7f, 0x47},
|
||||
{0x77, 0x80},
|
||||
{0x78, 0x84},
|
||||
{0x79, 0x8a},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x43},
|
||||
{0x97, 0x44},
|
||||
{0x98, 0x54},
|
||||
{0xf0, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x74},
|
||||
{0xf0, 0x3f},
|
||||
{0x03, 0x93},
|
||||
{0xf0, 0x36},//cvbs_off
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
{0xf0, 0x37},
|
||||
{0x24, 0x21},
|
||||
{0xf0, 0x36},
|
||||
{0x41, 0x00},
|
||||
{0xe9, 0x2c},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x00},
|
||||
{0x34, 0x00},
|
||||
{0xb1, 0x00},
|
||||
{0xf0, 0x00},
|
||||
{0xe0, 0x04},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x32, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x3e, 0xe0},
|
||||
{0x70, 0x56},
|
||||
{0x7c, 0x43},
|
||||
{0x7d, 0x47},
|
||||
{0x74, 0x00},
|
||||
{0x75, 0x00},
|
||||
{0x76, 0x00},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x22},
|
||||
{0x97, 0x22},
|
||||
{0x98, 0x22},
|
||||
{0xf0, 0x00},
|
||||
{0x72, 0x38},
|
||||
{0x7a, 0x80},
|
||||
{0x85, 0x18},
|
||||
{0x9b, 0x35},
|
||||
{0x9e, 0x20},
|
||||
{0xd0, 0x66},
|
||||
{0xd1, 0x34},
|
||||
{0Xd3, 0x44},
|
||||
{0xd6, 0x44},
|
||||
{0xb0, 0x41},
|
||||
{0xb2, 0x48},
|
||||
{0xb3, 0xf4},
|
||||
{0xb4, 0x0b},
|
||||
{0xb5, 0x78},
|
||||
{0xba, 0xff},
|
||||
{0xbb, 0xc0},
|
||||
{0xbc, 0x90},
|
||||
{0xbd, 0x3a},
|
||||
{0xc1, 0x67},
|
||||
{0xf0, 0x01},
|
||||
{0x20, 0x11},
|
||||
{0x23, 0x90},
|
||||
{0x24, 0x15},
|
||||
{0x25, 0x87},
|
||||
{0xbc, 0x9f},
|
||||
{0xbd, 0x3a},
|
||||
{0x48, 0xe6},
|
||||
{0x49, 0xc0},
|
||||
{0x4a, 0xd0},
|
||||
{0x4b, 0x48},
|
||||
|
||||
// [cvbs_on]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xf1},
|
||||
|
||||
// [cvbs_off]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
};
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
*
|
||||
* SC101IOT DVP driver.
|
||||
*
|
||||
*/
|
||||
#ifndef __SC101IOT_H__
|
||||
#define __SC101IOT_H__
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
/**
|
||||
* @brief Detect sensor pid
|
||||
*
|
||||
* @param slv_addr SCCB address
|
||||
* @param id Detection result
|
||||
* @return
|
||||
* 0: Can't detect this sensor
|
||||
* Nonzero: This sensor has been detected
|
||||
*/
|
||||
int sc101iot_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int sc101iot_init(sensor_t *sensor);
|
||||
|
||||
#endif // __SC101IOT_H__
|
||||
@@ -0,0 +1,257 @@
|
||||
//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
|
||||
//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
|
||||
//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
|
||||
//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
|
||||
//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
|
||||
//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
|
||||
//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
|
||||
//pin :BIT0 pwdn// BIT1:reset
|
||||
//avdd 0:2.8V// 1:2.5V// 2:1.8V
|
||||
//dovdd 0:2.8V// 1:2.5V// 2:1.8V
|
||||
//dvdd 0:1.8V// 1:1.5V// 2:1.2V
|
||||
/*
|
||||
[DataBase]
|
||||
DBName=DemoSens
|
||||
|
||||
[Vendor]
|
||||
VendorName=SmartSens
|
||||
I2C_CRC=0
|
||||
|
||||
[Sensor]
|
||||
SensorName=SC101AP_raw
|
||||
width=1280
|
||||
height=720
|
||||
port=1
|
||||
type=2
|
||||
pin=3
|
||||
SlaveID=0xd0
|
||||
mode=0
|
||||
FlagReg=0xf7
|
||||
FlagMask=0xff
|
||||
FlagData=0xda
|
||||
FlagReg1=0xf8
|
||||
FlagMask1=0xff
|
||||
FlagData1=0x4a
|
||||
outformat=0
|
||||
mclk=20
|
||||
avdd=2.800000
|
||||
dovdd=2.800000
|
||||
dvdd=1.200000
|
||||
|
||||
Ext0=0
|
||||
Ext1=0
|
||||
Ext2=0
|
||||
AFVCC=0.00
|
||||
VPP=0.000000
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t sc101iot_default_init_regs[][2] = {
|
||||
#if CONFIG_SC101IOT_720P_15FPS_ENABLED // 720P+YUV422+15FPS sensor default regs
|
||||
/* Here are some test results:
|
||||
# size xclk fps pic pclk
|
||||
# ------- ------- ------ --------- ------- --- --- --- --- ---
|
||||
# 720p 4 3 err
|
||||
# 720p 8 5 normal 15
|
||||
# 720p 10 7.8 normal 19
|
||||
# 720p 20 15 warning 37.5
|
||||
# VGA 8 6 normal
|
||||
# VGA 20 16 normal
|
||||
|
||||
*/
|
||||
{0xf0, 0x30},
|
||||
{0x01, 0xff},
|
||||
{0x02, 0xe0},
|
||||
{0x30, 0x10},
|
||||
{0x3f, 0x81},
|
||||
{0xf0, 0x00},
|
||||
{0x70, 0x6b},
|
||||
{0x72, 0x30},
|
||||
{0x84, 0xb4},
|
||||
{0x8b, 0x00},
|
||||
{0x8c, 0x20},
|
||||
{0x8d, 0x02},
|
||||
{0x8e, 0xec},
|
||||
{0x9e, 0x10},
|
||||
{0xb0, 0xc1},
|
||||
{0xc8, 0x10},
|
||||
{0xc9, 0x10},
|
||||
{0xc6, 0x00},
|
||||
{0xe0, 0x0f},
|
||||
{0xb5, 0xf0},
|
||||
{0xde, 0x80},
|
||||
{0xb5, 0xf0},
|
||||
{0xde, 0x80},
|
||||
{0xb2, 0x50},
|
||||
{0xb3, 0xfc},
|
||||
{0xb4, 0x40},
|
||||
{0xb5, 0xc0},
|
||||
{0xb6, 0x50},
|
||||
{0xb7, 0xfc},
|
||||
{0xb8, 0x40},
|
||||
{0xb9, 0xc0},
|
||||
{0xba, 0xff},
|
||||
{0xbb, 0xcc},
|
||||
{0xbc, 0xa9},
|
||||
{0xbd, 0x7d},
|
||||
{0xc1, 0x77},
|
||||
{0xf0, 0x01},
|
||||
{0x70, 0x02},
|
||||
{0x71, 0x02},
|
||||
{0x72, 0x50},
|
||||
{0x73, 0x02},
|
||||
{0x74, 0xd2},
|
||||
{0x75, 0x20},
|
||||
{0x76, 0x81},
|
||||
{0x77, 0x8c},
|
||||
{0x78, 0x81},
|
||||
{0xf4, 0x01},
|
||||
{0xf5, 0x00},
|
||||
{0xf6, 0x00},
|
||||
{0xf0, 0x36},
|
||||
{0x40, 0x03},
|
||||
{0x41, 0x01},
|
||||
{0xf0, 0x39},
|
||||
{0x02, 0x70},
|
||||
{0xf0, 0x32},
|
||||
{0x41, 0x00},
|
||||
{0x43, 0x01},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x45},
|
||||
{0x09, 0x20},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x10},
|
||||
{0xf0, 0x30},
|
||||
{0x38, 0x44},
|
||||
{0xf0, 0x39},
|
||||
{0x07, 0x00},
|
||||
{0x08, 0x19},
|
||||
{0x47, 0x00},
|
||||
{0x48, 0x00},
|
||||
{0xf0, 0x37},
|
||||
{0x24, 0x31},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x02},
|
||||
{0xa6, 0x51},
|
||||
{0xa7, 0x57},
|
||||
{0xe8, 0x5f},
|
||||
{0xa8, 0x50},
|
||||
{0xa9, 0x50},
|
||||
{0xe9, 0x50},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x58},
|
||||
{0xb2, 0x78},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x03},
|
||||
{0xa6, 0x51},
|
||||
{0xa7, 0x57},
|
||||
{0xaa, 0x01},
|
||||
{0xab, 0x28},
|
||||
{0xac, 0x01},
|
||||
{0xad, 0x38},
|
||||
{0xf0, 0x33},
|
||||
{0x0a, 0x01},
|
||||
{0x0b, 0x28},
|
||||
{0xf0, 0x33},
|
||||
{0x64, 0x0f},
|
||||
{0xec, 0x51},
|
||||
{0xed, 0x57},
|
||||
{0x06, 0x58},
|
||||
{0xe9, 0x58},
|
||||
{0xeb, 0x68},
|
||||
{0xf0, 0x33},
|
||||
{0x64, 0x0f},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0xdf},
|
||||
{0xb6, 0x40},
|
||||
{0xb7, 0x51},
|
||||
{0xb8, 0x53},
|
||||
{0xb9, 0x57},
|
||||
{0xba, 0x5f},
|
||||
{0xb0, 0x84},
|
||||
{0xb1, 0x82},
|
||||
{0xb2, 0x84},
|
||||
{0xb3, 0x88},
|
||||
{0xb4, 0x90},
|
||||
{0xb5, 0x90},
|
||||
{0xf0, 0x36},
|
||||
{0x7e, 0x50},
|
||||
{0x7f, 0x51},
|
||||
{0x77, 0x81},
|
||||
{0x78, 0x86},
|
||||
{0x79, 0x89},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0xdf},
|
||||
{0x9c, 0x51},
|
||||
{0x9d, 0x57},
|
||||
{0x90, 0x54},
|
||||
{0x91, 0x54},
|
||||
{0x92, 0x56},
|
||||
{0xf0, 0x36},
|
||||
{0xa0, 0x51},
|
||||
{0xa1, 0x57},
|
||||
{0x96, 0x33},
|
||||
{0x97, 0x43},
|
||||
{0x98, 0x43},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0xdf},
|
||||
{0x7c, 0x40},
|
||||
{0x7d, 0x53},
|
||||
{0x74, 0xd0},
|
||||
{0x75, 0xf0},
|
||||
{0x76, 0xf0},
|
||||
{0xf0, 0x37},
|
||||
{0x0f, 0xd5},
|
||||
{0x7a, 0x40},
|
||||
{0x7b, 0x57},
|
||||
{0x71, 0x09},
|
||||
{0x72, 0x09},
|
||||
{0x73, 0x05},
|
||||
{0xf0, 0x33},
|
||||
{0x01, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0xfb},
|
||||
{0xf0, 0x36},
|
||||
{0x3c, 0x0d},
|
||||
{0xf0, 0x33},
|
||||
{0x14, 0x95},
|
||||
{0xf0, 0x33},
|
||||
{0x8f, 0x80},
|
||||
{0xf0, 0x37},
|
||||
{0x27, 0x14},
|
||||
{0x28, 0x03},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0xf4},
|
||||
{0xf0, 0x33},
|
||||
{0x01, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x79, 0x89},
|
||||
{0xf0, 0x34},
|
||||
{0xac, 0x01},
|
||||
{0xad, 0x40},
|
||||
{0xf0, 0x33},
|
||||
{0xeb, 0x70},
|
||||
{0xf0, 0x34},
|
||||
{0xa8, 0x50},
|
||||
{0xa9, 0x50},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x58},
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x41, 0x51},
|
||||
{0xf0, 0x3f},
|
||||
{0x03, 0x09},
|
||||
{0xf0, 0x32},
|
||||
{0x0c, 0x06},
|
||||
{0x0d, 0x82},
|
||||
{0x0e, 0x02},
|
||||
{0x0f, 0xee},
|
||||
{0xf0, 0x36},
|
||||
{0xea, 0x09},
|
||||
{0xeb, 0xf5},
|
||||
{0xec, 0x11},
|
||||
{0xed, 0x27},
|
||||
{0xe9, 0x20},
|
||||
#endif
|
||||
};
|
||||
335
code/components/esp32-camera-master/sensors/sc030iot.c
Normal file
335
code/components/esp32-camera-master/sensors/sc030iot.c
Normal file
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* SC030IOT driver.
|
||||
*
|
||||
* Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "sc030iot.h"
|
||||
#include "sc030iot_settings.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* TAG = "sc030";
|
||||
#endif
|
||||
|
||||
#define SC030_SENSOR_ID_HIGH_REG 0XF7
|
||||
#define SC030_SENSOR_ID_LOW_REG 0XF8
|
||||
#define SC030_MAX_FRAME_WIDTH (640)
|
||||
#define SC030_MAX_FRAME_HIGH (480)
|
||||
|
||||
// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int get_reg(sensor_t *sensor, int reg, int reg_value_mask)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Read(sensor->slv_addr, reg_low);
|
||||
if(ret > 0){
|
||||
ret &= reg_value_mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Write(sensor->slv_addr, reg_low, value & 0xFF);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
|
||||
{
|
||||
int i=0, res = 0;
|
||||
while (i<regs_entry_len) {
|
||||
res = SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = get_reg(sensor, reg, 0xff);
|
||||
if(ret < 0){
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
value = (ret & ~mask) | ((value << offset) & mask);
|
||||
ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
|
||||
#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
|
||||
#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // mirror on
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // mirror off
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable test pattern mode
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_sharpness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
|
||||
WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
|
||||
WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_agc_gain(sensor_t *sensor, int gain)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
|
||||
WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
|
||||
WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
|
||||
WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
|
||||
WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_aec_value(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
|
||||
WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_awb_gain(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
|
||||
WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
|
||||
WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_saturation(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
|
||||
WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
|
||||
WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_contrast(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
|
||||
WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret = set_regs(sensor, sc030iot_default_init_regs, sizeof(sc030iot_default_init_regs)/(sizeof(uint8_t) * 2));
|
||||
|
||||
// Delay
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// ESP_LOGI(TAG, "set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
|
||||
// ESP_LOGI(TAG, "0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "reset fail");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
|
||||
{
|
||||
int ret = 0;
|
||||
//sc:H_start={0x0172[1:0],0x0170},H_end={0x0172[5:4],0x0171},
|
||||
WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x03) | (((offset_x+w)>>4)&0x30));
|
||||
|
||||
//sc:V_start={0x0175[1:0],0x0173},H_end={0x0175[5:4],0x0174},
|
||||
WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x03) | (((offset_y+h)>>4)&0x30));
|
||||
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
if(w>SC030_MAX_FRAME_WIDTH || h > SC030_MAX_FRAME_HIGH) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
uint16_t offset_x = (640-w) /2;
|
||||
uint16_t offset_y = (480-h) /2;
|
||||
|
||||
if(set_window(sensor, offset_x, offset_y, w, h)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
return 0;
|
||||
err:
|
||||
ESP_LOGE(TAG, "frame size err");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret=0;
|
||||
sensor->pixformat = pixformat;
|
||||
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_RGB565:
|
||||
case PIXFORMAT_RAW:
|
||||
case PIXFORMAT_GRAYSCALE:
|
||||
ESP_LOGE(TAG, "Not support");
|
||||
break;
|
||||
case PIXFORMAT_YUV422: // For now, sc030/sc031 sensor only support YUV422.
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val){ return -1; }
|
||||
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->xclk_freq_hz = xclk * 1000000U;
|
||||
ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc030iot_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (SC030IOT_SCCB_ADDR == slv_addr) {
|
||||
uint8_t MIDL = SCCB_Read(slv_addr, SC030_SENSOR_ID_LOW_REG);
|
||||
uint8_t MIDH = SCCB_Read(slv_addr, SC030_SENSOR_ID_HIGH_REG);
|
||||
uint16_t PID = MIDH << 8 | MIDL;
|
||||
if (SC030IOT_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc030iot_init(sensor_t *sensor)
|
||||
{
|
||||
// Set function pointers
|
||||
sensor->reset = reset;
|
||||
sensor->init_status = init_status;
|
||||
sensor->set_pixformat = set_pixformat;
|
||||
sensor->set_framesize = set_framesize;
|
||||
|
||||
sensor->set_saturation= set_saturation;
|
||||
sensor->set_colorbar = set_colorbar;
|
||||
sensor->set_hmirror = set_hmirror;
|
||||
sensor->set_vflip = set_vflip;
|
||||
sensor->set_sharpness = set_sharpness;
|
||||
sensor->set_agc_gain = set_agc_gain;
|
||||
sensor->set_aec_value = set_aec_value;
|
||||
sensor->set_awb_gain = set_awb_gain;
|
||||
sensor->set_contrast = set_contrast;
|
||||
//not supported
|
||||
sensor->set_denoise = set_dummy;
|
||||
sensor->set_quality = set_dummy;
|
||||
sensor->set_special_effect = set_dummy;
|
||||
sensor->set_wb_mode = set_dummy;
|
||||
sensor->set_ae_level = set_dummy;
|
||||
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = set_reg;
|
||||
sensor->set_xclk = set_xclk;
|
||||
|
||||
ESP_LOGD(TAG, "sc030iot Attached");
|
||||
|
||||
return 0;
|
||||
}
|
||||
342
code/components/esp32-camera-master/sensors/sc101iot.c
Normal file
342
code/components/esp32-camera-master/sensors/sc101iot.c
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* SC101IOT driver.
|
||||
*
|
||||
* Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "sc101iot.h"
|
||||
#include "sc101iot_settings.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* TAG = "sc101";
|
||||
#endif
|
||||
|
||||
#define SC101_SENSOR_ID_HIGH_REG 0XF7
|
||||
#define SC101_SENSOR_ID_LOW_REG 0XF8
|
||||
#define SC101_MAX_FRAME_WIDTH (1280)
|
||||
#define SC101_MAX_FRAME_HIGH (720)
|
||||
|
||||
// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int get_reg(sensor_t *sensor, int reg, int mask)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Read(sensor->slv_addr, reg_low);
|
||||
if(ret > 0){
|
||||
ret &= mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Write(sensor->slv_addr, reg_low, value & 0xFF);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
|
||||
{
|
||||
int i=0, res = 0;
|
||||
while (i<regs_entry_len) {
|
||||
res = SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = get_reg(sensor, reg, 0xff);
|
||||
if(ret < 0){
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
value = (ret & ~mask) | ((value << offset) & mask);
|
||||
ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
|
||||
#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
|
||||
#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // enable mirror
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // disable mirror
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable colorbar mode
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_raw_gma(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 1, 1, enable & 0xff); // enable gamma compensation
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_sharpness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
|
||||
WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
|
||||
WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_agc_gain(sensor_t *sensor, int gain)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
|
||||
WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
|
||||
WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
|
||||
WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
|
||||
WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_aec_value(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
|
||||
WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_awb_gain(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
|
||||
WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
|
||||
WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_saturation(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
|
||||
WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
|
||||
WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_contrast(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
|
||||
WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret = set_regs(sensor, sc101iot_default_init_regs, sizeof(sc101iot_default_init_regs)/(sizeof(uint8_t) * 2));
|
||||
|
||||
// Delay
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// ESP_LOGI(TAG, "set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
|
||||
// ESP_LOGI(TAG, "0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "reset fail");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
|
||||
{
|
||||
int ret = 0;
|
||||
//sc:H_start={0x0172[3:0],0x0170},H_end={0x0172[7:4],0x0171},
|
||||
WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x0f) | (((offset_x+w)>>4)&0xf0));
|
||||
|
||||
//sc:V_start={0x0175[3:0],0x0173},H_end={0x0175[7:4],0x0174},
|
||||
WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x0f) | (((offset_y+h)>>4)&0xf0));
|
||||
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
if(w>SC101_MAX_FRAME_WIDTH || h > SC101_MAX_FRAME_HIGH) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
uint16_t offset_x = (SC101_MAX_FRAME_WIDTH-w) /2;
|
||||
uint16_t offset_y = (SC101_MAX_FRAME_HIGH-h) /2;
|
||||
|
||||
if(set_window(sensor, offset_x, offset_y, w, h)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
return 0;
|
||||
err:
|
||||
ESP_LOGE(TAG, "frame size err");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret=0;
|
||||
sensor->pixformat = pixformat;
|
||||
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_RGB565:
|
||||
case PIXFORMAT_RAW:
|
||||
case PIXFORMAT_GRAYSCALE:
|
||||
ESP_LOGE(TAG, "Not support");
|
||||
break;
|
||||
case PIXFORMAT_YUV422: // For now, sc101 sensor only support YUV422.
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val){ return -1; }
|
||||
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->xclk_freq_hz = xclk * 1000000U;
|
||||
ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc101iot_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (SC101IOT_SCCB_ADDR == slv_addr) {
|
||||
uint8_t MIDL = SCCB_Read(slv_addr, SC101_SENSOR_ID_LOW_REG);
|
||||
uint8_t MIDH = SCCB_Read(slv_addr, SC101_SENSOR_ID_HIGH_REG);
|
||||
uint16_t PID = MIDH << 8 | MIDL;
|
||||
if (SC101IOT_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc101iot_init(sensor_t *sensor)
|
||||
{
|
||||
// Set function pointers
|
||||
sensor->reset = reset;
|
||||
sensor->init_status = init_status;
|
||||
sensor->set_pixformat = set_pixformat;
|
||||
sensor->set_framesize = set_framesize;
|
||||
sensor->set_hmirror = set_hmirror;
|
||||
sensor->set_vflip = set_vflip;
|
||||
sensor->set_colorbar = set_colorbar;
|
||||
sensor->set_raw_gma = set_raw_gma;
|
||||
sensor->set_sharpness = set_sharpness;
|
||||
sensor->set_agc_gain = set_agc_gain;
|
||||
sensor->set_aec_value = set_aec_value;
|
||||
sensor->set_awb_gain = set_awb_gain;
|
||||
sensor->set_saturation= set_saturation;
|
||||
sensor->set_contrast = set_contrast;
|
||||
|
||||
sensor->set_denoise = set_dummy;
|
||||
sensor->set_quality = set_dummy;
|
||||
sensor->set_special_effect = set_dummy;
|
||||
sensor->set_wb_mode = set_dummy;
|
||||
sensor->set_ae_level = set_dummy;
|
||||
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = set_reg;
|
||||
sensor->set_xclk = set_xclk;
|
||||
|
||||
ESP_LOGD(TAG, "sc101iot Attached");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -34,10 +34,14 @@ static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
|
||||
#include "xclk.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR >= 3)
|
||||
#include "esp_rom_gpio.h"
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
|
||||
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "esp32 ll_cam";
|
||||
@@ -233,7 +237,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
//DBG_PIN_SET(0);
|
||||
}
|
||||
|
||||
bool ll_cam_stop(cam_obj_t *cam)
|
||||
bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
|
||||
{
|
||||
I2S0.conf.rx_start = 0;
|
||||
I2S_ISR_DISABLE(in_suc_eof);
|
||||
|
||||
@@ -21,10 +21,15 @@
|
||||
#include "xclk.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR >= 3)
|
||||
#include "esp_rom_gpio.h"
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
|
||||
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "s2 ll_cam";
|
||||
@@ -70,7 +75,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
bool ll_cam_stop(cam_obj_t *cam)
|
||||
bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
|
||||
{
|
||||
I2S0.conf.rx_start = 0;
|
||||
|
||||
|
||||
@@ -22,10 +22,15 @@
|
||||
#include "soc/gdma_reg.h"
|
||||
#include "ll_cam.h"
|
||||
#include "cam_hal.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#define gpio_matrix_out(a,b,c,d) gpio_iomux_out(a,b,c)
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/gpio_periph.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
|
||||
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "s3 ll_cam";
|
||||
@@ -74,7 +79,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
bool ll_cam_stop(cam_obj_t *cam)
|
||||
bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
|
||||
{
|
||||
if (cam->jpeg_mode || !cam->psram_mode) {
|
||||
GDMA.channel[cam->dma_num].in.int_ena.in_suc_eof = 0;
|
||||
@@ -170,6 +175,7 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
|
||||
}
|
||||
|
||||
GDMA.channel[cam->dma_num].in.conf1.in_check_owner = 0;
|
||||
// GDMA.channel[cam->dma_num].in.conf1.in_ext_mem_bk_size = 2;
|
||||
|
||||
GDMA.channel[cam->dma_num].in.peri_sel.sel = 5;
|
||||
//GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15
|
||||
@@ -178,8 +184,52 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
static esp_err_t ll_cam_converter_config(cam_obj_t *cam, const camera_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
switch (config->conv_mode) {
|
||||
case YUV422_TO_YUV420:
|
||||
if (config->pixel_format != PIXFORMAT_YUV422) {
|
||||
ret = ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "YUV422 to YUV420 mode");
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 1;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 1;
|
||||
}
|
||||
break;
|
||||
case YUV422_TO_RGB565:
|
||||
if (config->pixel_format != PIXFORMAT_YUV422) {
|
||||
ret = ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "YUV422 to RGB565 mode");
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#if CONFIG_LCD_CAM_CONV_BT709_ENABLED
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 1;
|
||||
#else
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 0;
|
||||
#endif
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_data_out_mode = 0;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_data_in_mode = 0;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_mode_8bits_on = 1;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_bypass = 1;
|
||||
cam->conv_mode = config->conv_mode;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0) {
|
||||
REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
|
||||
REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
|
||||
@@ -215,15 +265,21 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
|
||||
|
||||
LCD_CAM.cam_rgb_yuv.val = 0;
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
if (config->conv_mode) {
|
||||
ret = ll_cam_converter_config(cam, config);
|
||||
if(ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LCD_CAM.cam_ctrl.cam_update = 1;
|
||||
LCD_CAM.cam_ctrl1.cam_start = 1;
|
||||
|
||||
esp_err_t err = ll_cam_dma_init(cam);
|
||||
if(err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
ret = ll_cam_dma_init(cam);
|
||||
|
||||
return ESP_OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
|
||||
@@ -417,6 +473,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in,
|
||||
}
|
||||
return len / 2;
|
||||
}
|
||||
|
||||
|
||||
// just memcpy
|
||||
memcpy(out, in, len);
|
||||
@@ -433,8 +490,22 @@ esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_
|
||||
}
|
||||
cam->fb_bytes_per_pixel = 1; // frame buffer stores Y8
|
||||
} else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
|
||||
cam->in_bytes_per_pixel = 2; // camera sends YU/YV
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
switch (cam->conv_mode) {
|
||||
case YUV422_TO_YUV420:
|
||||
cam->in_bytes_per_pixel = 1.5; // for DMA receive
|
||||
cam->fb_bytes_per_pixel = 1.5; // frame buffer stores YUV420
|
||||
break;
|
||||
case YUV422_TO_RGB565:
|
||||
default:
|
||||
cam->in_bytes_per_pixel = 2; // for DMA receive
|
||||
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
|
||||
break;
|
||||
}
|
||||
#else
|
||||
cam->in_bytes_per_pixel = 2; // for DMA receive
|
||||
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
|
||||
#endif
|
||||
} else if (pix_format == PIXFORMAT_JPEG) {
|
||||
cam->in_bytes_per_pixel = 1;
|
||||
cam->fb_bytes_per_pixel = 1;
|
||||
|
||||
@@ -116,8 +116,14 @@ typedef struct {
|
||||
//for RGB/YUV modes
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
float in_bytes_per_pixel;
|
||||
float fb_bytes_per_pixel;
|
||||
camera_conv_mode_t conv_mode;
|
||||
#else
|
||||
uint8_t in_bytes_per_pixel;
|
||||
uint8_t fb_bytes_per_pixel;
|
||||
#endif
|
||||
uint32_t fb_size;
|
||||
|
||||
cam_state_t state;
|
||||
|
||||
Binary file not shown.
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
|
||||
REQUIRES jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ void ClassFlow::SetInitialParameter(void)
|
||||
std::vector<string> ClassFlow::ZerlegeZeile(std::string input, std::string delimiter)
|
||||
{
|
||||
std::vector<string> Output;
|
||||
// std::string delimiter = " =,";
|
||||
|
||||
input = trim(input, delimiter);
|
||||
size_t pos = findDelimiterPos(input, delimiter);
|
||||
|
||||
@@ -26,7 +26,6 @@ struct HTMLInfo
|
||||
class ClassFlow
|
||||
{
|
||||
protected:
|
||||
// std::vector<string> ZerlegeZeile(string input);
|
||||
std::vector<string> ZerlegeZeile(string input, string delimiter = " =, \t");
|
||||
bool isNewParagraph(string input);
|
||||
bool GetNextParagraph(FILE* pfile, string& aktparamgraph);
|
||||
|
||||
@@ -19,6 +19,7 @@ void ClassFlowAlignment::SetInitialParameter(void)
|
||||
initalrotate = 0;
|
||||
anz_ref = 0;
|
||||
initialmirror = false;
|
||||
use_antialiasing = false;
|
||||
initialflip = false;
|
||||
SaveAllFiles = false;
|
||||
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||
@@ -94,7 +95,12 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
if ((toUpper(zerlegt[0]) == "SEARCHFIELDY") && (zerlegt.size() > 1))
|
||||
{
|
||||
suchey = std::stod(zerlegt[1]);
|
||||
}
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "ANTIALIASING") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
use_antialiasing = true;
|
||||
}
|
||||
if ((zerlegt.size() == 3) && (anz_ref < 2))
|
||||
{
|
||||
References[anz_ref].image_file = FormatFileName("/sdcard" + zerlegt[0]);
|
||||
@@ -175,7 +181,10 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
|
||||
if ((initalrotate != 0) || initialflip)
|
||||
{
|
||||
rt.Rotate(initalrotate);
|
||||
if (use_antialiasing)
|
||||
rt.RotateAntiAliasing(initalrotate);
|
||||
else
|
||||
rt.Rotate(initalrotate);
|
||||
if (SaveAllFiles) AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ protected:
|
||||
float initalrotate;
|
||||
bool initialmirror;
|
||||
bool initialflip;
|
||||
bool use_antialiasing;
|
||||
RefInfo References[2];
|
||||
int anz_ref;
|
||||
string namerawimage;
|
||||
|
||||
@@ -17,6 +17,7 @@ ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNTy
|
||||
string cnnmodelfile = "";
|
||||
modelxsize = 1;
|
||||
modelysize = 1;
|
||||
CNNGoodThreshold = 0.0;
|
||||
ListFlowControll = NULL;
|
||||
previousElement = NULL;
|
||||
SaveAllFiles = false;
|
||||
@@ -27,20 +28,21 @@ ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNTy
|
||||
flowpostalignment = _flowalign;
|
||||
}
|
||||
|
||||
string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution = false)
|
||||
string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution, int prev)
|
||||
{
|
||||
string result = "";
|
||||
|
||||
if (GENERAL[_analog]->ROI.size() == 0)
|
||||
return result;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout _analog=" + std::to_string(_analog) + ", _extendedResolution=" + std::to_string(_extendedResolution) + ", prev=" + std::to_string(prev));
|
||||
|
||||
if (CNNType == Analogue)
|
||||
if (CNNType == Analogue || CNNType == Analogue100)
|
||||
{
|
||||
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10) + 10) % 10;
|
||||
|
||||
int prev = -1;
|
||||
|
||||
|
||||
prev = ZeigerEval(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout(analog) zahl=" + std::to_string(zahl) + ", ergebnis_nachkomma=" + std::to_string(ergebnis_nachkomma) + ", prev=" + std::to_string(prev));
|
||||
result = std::to_string(prev);
|
||||
|
||||
if (_extendedResolution && (CNNType != Digital))
|
||||
@@ -66,7 +68,58 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
return result;
|
||||
}
|
||||
|
||||
if (CNNType == DigitalHyprid)
|
||||
if ((CNNType == DoubleHyprid10) || (CNNType == Digital100))
|
||||
{
|
||||
|
||||
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
|
||||
if (zahl >= 0) // NaN?
|
||||
{
|
||||
if (_extendedResolution) // ist nur gesetzt, falls es die erste Ziffer ist (kein Analog vorher!)
|
||||
{
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
|
||||
|
||||
result = std::to_string(ergebnis_vorkomma) + std::to_string(ergebnis_nachkomma);
|
||||
prev = ergebnis_vorkomma;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout(dig100-ext) ergebnis_vorkomma=" + std::to_string(ergebnis_vorkomma) + ", ergebnis_nachkomma=" + std::to_string(ergebnis_nachkomma) + ", prev=" + std::to_string(prev));
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// prev = ZeigerEval(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev);
|
||||
prev = ZeigerEvalHybrid(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev, prev);
|
||||
result = std::to_string(prev);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout(dig100) prev=" + std::to_string(prev));
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "N";
|
||||
if (_extendedResolution && (CNNType != Digital))
|
||||
result = "NN";
|
||||
}
|
||||
|
||||
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
|
||||
{
|
||||
if (GENERAL[_analog]->ROI[i]->result_float >= 0)
|
||||
{
|
||||
prev = ZeigerEvalHybrid(GENERAL[_analog]->ROI[i]->result_float, GENERAL[_analog]->ROI[i+1]->result_float, prev);
|
||||
result = std::to_string(prev) + result;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
prev = -1;
|
||||
result = "N" + result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
if (CNNType == Digital100)
|
||||
{
|
||||
int zif_akt = -1;
|
||||
|
||||
@@ -109,6 +162,7 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
}
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -116,8 +170,10 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger)
|
||||
{
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
int ergebnis_vorkomma = ((int) floor(zahl) + 10) % 10;
|
||||
|
||||
if (zahl_vorgaenger < 0) // keine Vorzahl vorhanden !!! --> Runde die Zahl
|
||||
|
||||
if (eval_vorgaenger < 0) // keine Vorzahl vorhanden !!! --> Runde die Zahl
|
||||
{
|
||||
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||
return ((int) round(zahl) + 10) % 10;
|
||||
@@ -125,6 +181,34 @@ int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int
|
||||
return ((int) trunc(zahl) + 10) % 10;
|
||||
}
|
||||
|
||||
if ((zahl_vorgaenger >= 0.5 ) && (zahl_vorgaenger < 9.5))
|
||||
{
|
||||
// kein Ziffernwechsel, da Vorkomma weit genug weg ist (0+/-0.5) --> zahl wird gerundet
|
||||
return ((int) round(zahl) + 10) % 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (eval_vorgaenger <= 1) // Nulldurchgang hat stattgefunden (!Bewertung über Prev_value und nicht Zahl!) --> hier aufrunden (2.8 --> 3, aber auch 3.1 --> 3)
|
||||
{
|
||||
if (ergebnis_nachkomma > 5)
|
||||
return (ergebnis_vorkomma + 1) % 10;
|
||||
else
|
||||
return ergebnis_vorkomma;
|
||||
}
|
||||
else // bleibt nur >= 9.5 --> noch kein Nulldurchgang --> 2.8 --> 2, und 3.1 --> 2
|
||||
{
|
||||
// hier auf 4 reduziert, da erst ab Vorgänder 9 anfängt umzustellen. Bei 9.5 Vorgänger kann die aktuelle
|
||||
// Zahl noch x.4 - x.5 sein.
|
||||
if (ergebnis_nachkomma >= 4)
|
||||
return ergebnis_vorkomma;
|
||||
else
|
||||
return (ergebnis_vorkomma - 1 + 10) % 10;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
/*
|
||||
if (zahl_vorgaenger > 9.2) // Ziffernwechsel beginnt
|
||||
{
|
||||
if (eval_vorgaenger == 0) // Wechsel hat schon stattgefunden
|
||||
@@ -151,20 +235,30 @@ int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int
|
||||
return ((int) round(zahl) + 10) % 10;
|
||||
|
||||
return ((int) trunc(zahl) + 10) % 10;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
|
||||
{
|
||||
{
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10) + 10) % 10;
|
||||
int ergebnis_vorkomma = ((int) floor(zahl) + 10) % 10;
|
||||
int ergebnis, ergebnis_rating;
|
||||
int ergebnis;
|
||||
float ergebnis_rating;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEval erg_v=" + std::to_string(ergebnis_vorkomma) + ", erg_n=" + std::to_string(ergebnis_nachkomma) + ", ziff_v=" + std::to_string(ziffer_vorgaenger));
|
||||
|
||||
if (ziffer_vorgaenger == -1)
|
||||
return ergebnis_vorkomma % 10;
|
||||
|
||||
// Ist die aktuelle Stelle schon umgesprungen und die Vorstelle noch nicht?
|
||||
// Akt.: 2.1, Vorstelle = 0.9 => 1.9
|
||||
// Problem sind mehrere Rundungen
|
||||
// Bsp. zahl=4.5, Vorgänger= 9.6 (ziffer_vorgaenger=0)
|
||||
// Tritt nur auf bei Übergang von analog auf digit
|
||||
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
|
||||
if (ergebnis_nachkomma >= 5)
|
||||
ergebnis_rating-=5;
|
||||
ergebnis_rating-=5.1;
|
||||
else
|
||||
ergebnis_rating+=5;
|
||||
ergebnis = (int) round(zahl);
|
||||
@@ -172,7 +266,7 @@ int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
|
||||
ergebnis-=1;
|
||||
if (ergebnis == -1)
|
||||
ergebnis+=10;
|
||||
|
||||
|
||||
ergebnis = (ergebnis + 10) % 10;
|
||||
return ergebnis;
|
||||
}
|
||||
@@ -206,12 +300,12 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
||||
if ((toUpper(zerlegt[0]) == "LOGIMAGELOCATION") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->LogImageLocation = "/sdcard" + zerlegt[1];
|
||||
this->isLogImage = true;
|
||||
}
|
||||
if ((zerlegt[0] == "LogImageSelect") && (zerlegt.size() > 1))
|
||||
if ((toUpper(zerlegt[0]) == "LOGIMAGESELECT") && (zerlegt.size() > 1))
|
||||
{
|
||||
LogImageSelect = zerlegt[1];
|
||||
isLogImageSelect = true;
|
||||
@@ -221,20 +315,20 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
this->logfileRetentionInDays = std::stoi(zerlegt[1]);
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "MODELTYPE") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "DIGITHYPRID")
|
||||
CNNType = DigitalHyprid;
|
||||
}
|
||||
// if ((toUpper(zerlegt[0]) == "MODELTYPE") && (zerlegt.size() > 1))
|
||||
// {
|
||||
// if (toUpper(zerlegt[1]) == "DIGITHYPRID")
|
||||
// CNNType = DigitalHyprid;
|
||||
// }
|
||||
|
||||
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
||||
if ((toUpper(zerlegt[0]) == "MODEL") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->cnnmodelfile = zerlegt[1];
|
||||
}
|
||||
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "CNNGOODTHRESHOLD") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->modelxsize = std::stoi(zerlegt[1]);
|
||||
this->modelysize = std::stoi(zerlegt[2]);
|
||||
CNNGoodThreshold = std::stof(zerlegt[1]);
|
||||
}
|
||||
if (zerlegt.size() >= 5)
|
||||
{
|
||||
@@ -256,11 +350,14 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
}
|
||||
}
|
||||
|
||||
if (!getNetworkParameter())
|
||||
return false;
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
|
||||
GENERAL[_ana]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, modelchannel);
|
||||
GENERAL[_ana]->ROI[i]->image_org = new CImageBasis(GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3);
|
||||
}
|
||||
|
||||
@@ -398,7 +495,7 @@ bool ClassFlowCNNGeneral::doAlignAndCut(string time)
|
||||
|
||||
void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
|
||||
{
|
||||
if (CNNType == Analogue)
|
||||
if (CNNType == Analogue || CNNType == Analogue100)
|
||||
{
|
||||
int r = 0;
|
||||
int g = 255;
|
||||
@@ -408,7 +505,6 @@ void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
_zw->drawRect(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, r, g, b, 1);
|
||||
// _zw->drawCircle((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), r, g, b, 2);
|
||||
_zw->drawEllipse( (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||
_zw->drawLine((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) GENERAL[_ana]->ROI[i]->posy, (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay), r, g, b, 2);
|
||||
_zw->drawLine((int) GENERAL[_ana]->ROI[i]->posx, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||
@@ -422,6 +518,71 @@ void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::getNetworkParameter()
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
CTfLiteClass *tflite = new CTfLiteClass;
|
||||
string zwcnn = "/sdcard" + cnnmodelfile;
|
||||
zwcnn = FormatFileName(zwcnn);
|
||||
printf(zwcnn.c_str());printf("\n");
|
||||
if (!tflite->LoadModel(zwcnn)) {
|
||||
printf("Can't read model file /sdcard%s\n", cnnmodelfile.c_str());
|
||||
LogFile.WriteToFile("Cannot load model");
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
tflite->MakeAllocate();
|
||||
|
||||
if (CNNType == AutoDetect)
|
||||
{
|
||||
tflite->GetInputDimension(false);
|
||||
modelxsize = tflite->ReadInputDimenstion(0);
|
||||
modelysize = tflite->ReadInputDimenstion(1);
|
||||
modelchannel = tflite->ReadInputDimenstion(2);
|
||||
|
||||
int _anzoutputdimensions = tflite->GetAnzOutPut();
|
||||
switch (_anzoutputdimensions)
|
||||
{
|
||||
case 2:
|
||||
CNNType = Analogue;
|
||||
printf("TFlite-Type set to Analogue\n");
|
||||
break;
|
||||
case 10:
|
||||
CNNType = DoubleHyprid10;
|
||||
printf("TFlite-Type set to DoubleHyprid10\n");
|
||||
break;
|
||||
case 11:
|
||||
CNNType = Digital;
|
||||
printf("TFlite-Type set to Digital\n");
|
||||
break;
|
||||
case 20:
|
||||
CNNType = DigitalHyprid10;
|
||||
printf("TFlite-Type set to DigitalHyprid10\n");
|
||||
break;
|
||||
// case 22:
|
||||
// CNNType = DigitalHyprid;
|
||||
// printf("TFlite-Type set to DigitalHyprid\n");
|
||||
// break;
|
||||
case 100:
|
||||
if (modelxsize==32 && modelysize == 32) {
|
||||
CNNType = Analogue100;
|
||||
printf("TFlite-Type set to Analogue100\n");
|
||||
} else {
|
||||
CNNType = Digital100;
|
||||
printf("TFlite-Type set to Digital\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("ERROR ERROR ERROR - tflite passt nicht zur Firmware - ERROR ERROR ERROR\n");
|
||||
}
|
||||
}
|
||||
|
||||
delete tflite;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
{
|
||||
if (disabled)
|
||||
@@ -442,32 +603,6 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
}
|
||||
tflite->MakeAllocate();
|
||||
|
||||
if (CNNType == AutoDetect)
|
||||
{
|
||||
int _anzoutputdimensions = tflite->GetAnzOutPut();
|
||||
switch (_anzoutputdimensions)
|
||||
{
|
||||
case 2:
|
||||
CNNType = Analogue;
|
||||
printf("TFlite-Type set to Analogue\n");
|
||||
break;
|
||||
case 11:
|
||||
CNNType = Digital;
|
||||
printf("TFlite-Type set to Digital\n");
|
||||
break;
|
||||
case 20:
|
||||
CNNType = DigitalHyprid10;
|
||||
printf("TFlite-Type set to DigitalHyprid10\n");
|
||||
break;
|
||||
case 22:
|
||||
CNNType = DigitalHyprid;
|
||||
printf("TFlite-Type set to DigitalHyprid\n");
|
||||
break;
|
||||
default:
|
||||
printf("ERROR ERROR ERROR - tflite passt nicht zur Firmware - ERROR ERROR ERROR\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
{
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
@@ -492,6 +627,7 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
if (isLogImage)
|
||||
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
} break;
|
||||
|
||||
case Digital:
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->result_klasse = 0;
|
||||
@@ -500,17 +636,19 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
|
||||
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
/*
|
||||
case DigitalHyprid:
|
||||
{
|
||||
int _num, _nachkomma;
|
||||
@@ -536,8 +674,20 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
|
||||
|
||||
if (isLogImage)
|
||||
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
{
|
||||
string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
*/
|
||||
case DigitalHyprid10:
|
||||
{
|
||||
int _num, _nachkomma;
|
||||
@@ -560,8 +710,149 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
|
||||
|
||||
if (isLogImage)
|
||||
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
{
|
||||
string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case DoubleHyprid10:
|
||||
{
|
||||
int _num, _numplus, _numminus;
|
||||
float _val, _valplus, _valminus;
|
||||
float _fit;
|
||||
float _result_save_file;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
|
||||
|
||||
_num = tflite->GetOutClassification(0, 9);
|
||||
_numplus = (_num + 1) % 10;
|
||||
_numminus = (_num - 1 + 10) % 10;
|
||||
|
||||
_val = tflite->GetOutputValue(_num);
|
||||
_valplus = tflite->GetOutputValue(_numplus);
|
||||
_valminus = tflite->GetOutputValue(_numminus);
|
||||
|
||||
float result = _num;
|
||||
|
||||
if (_valplus > _valminus)
|
||||
{
|
||||
result = result + _valplus / (_valplus + _val);
|
||||
_fit = _val + _valplus;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = result - _valminus / (_val + _valminus);
|
||||
_fit = _val + _valminus;
|
||||
|
||||
}
|
||||
if (result >= 10)
|
||||
result = result - 10;
|
||||
if (result < 0)
|
||||
result = result + 10;
|
||||
|
||||
string zw = "_num (p, m): " + to_string(_num) + " " + to_string(_numplus) + " " + to_string(_numminus);
|
||||
zw = zw + " _val (p, m): " + to_string(_val) + " " + to_string(_valplus) + " " + to_string(_valminus);
|
||||
zw = zw + " result: " + to_string(result) + " _fit: " + to_string(_fit);
|
||||
printf("details cnn: %s\n", zw.c_str());
|
||||
LogFile.WriteToFile(zw);
|
||||
|
||||
|
||||
_result_save_file = result;
|
||||
|
||||
if (_fit < CNNGoodThreshold)
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->isReject = true;
|
||||
result = -1;
|
||||
_result_save_file+= 100; // Für den Fall, dass fit nicht ausreichend, soll trotzdem das Ergebnis mit "-10x.y" abgespeichert werden.
|
||||
string zw = "Value Rejected due to Threshold (Fit: " + to_string(_fit) + "Threshold: " + to_string(CNNGoodThreshold);
|
||||
printf("Value Rejected due to Threshold (Fit: %f, Threshold: %f\n", _fit, CNNGoodThreshold);
|
||||
LogFile.WriteToFile(zw);
|
||||
}
|
||||
else
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->isReject = false;
|
||||
}
|
||||
|
||||
|
||||
GENERAL[_ana]->ROI[i]->result_float = result;
|
||||
printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Digital100:
|
||||
case Analogue100:
|
||||
{
|
||||
int _num;
|
||||
float _fit;
|
||||
float _result_save_file;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
|
||||
_num = tflite->GetOutClassification();
|
||||
_fit = tflite->GetOutputValue(_num);
|
||||
|
||||
GENERAL[_ana]->ROI[i]->result_float = (float)_num / 10.0;
|
||||
|
||||
|
||||
_result_save_file = GENERAL[_ana]->ROI[i]->result_float;
|
||||
|
||||
if (_fit < CNNGoodThreshold)
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->isReject = true;
|
||||
GENERAL[_ana]->ROI[i]->result_float = -1;
|
||||
_result_save_file+= 100; // Für den Fall, dass fit nicht ausreichend, soll trotzdem das Ergebnis mit "-10x.y" abgespeichert werden.
|
||||
string zw = "Value Rejected due to Threshold (Fit: " + to_string(_fit) + "Threshold: " + to_string(CNNGoodThreshold);
|
||||
printf("Value Rejected due to Threshold (Fit: %f, Threshold: %f\n", _fit, CNNGoodThreshold);
|
||||
LogFile.WriteToFile(zw);
|
||||
}
|
||||
else
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->isReject = false;
|
||||
}
|
||||
|
||||
printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
enum t_CNNType {
|
||||
AutoDetect,
|
||||
Analogue,
|
||||
Analogue100,
|
||||
Digital,
|
||||
DigitalHyprid,
|
||||
// DigitalHyprid,
|
||||
DigitalHyprid10,
|
||||
DoubleHyprid10,
|
||||
Digital100,
|
||||
None
|
||||
};
|
||||
|
||||
@@ -20,9 +23,10 @@ class ClassFlowCNNGeneral :
|
||||
protected:
|
||||
t_CNNType CNNType;
|
||||
std::vector<general*> GENERAL;
|
||||
float CNNGoodThreshold;
|
||||
|
||||
string cnnmodelfile;
|
||||
int modelxsize, modelysize;
|
||||
int modelxsize, modelysize, modelchannel;
|
||||
bool isLogImageSelect;
|
||||
string LogImageSelect;
|
||||
ClassFlowAlignment* flowpostalignment;
|
||||
@@ -37,6 +41,8 @@ protected:
|
||||
bool doNeuralNetwork(string time);
|
||||
bool doAlignAndCut(string time);
|
||||
|
||||
bool getNetworkParameter();
|
||||
|
||||
public:
|
||||
ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype = AutoDetect);
|
||||
|
||||
@@ -44,7 +50,7 @@ public:
|
||||
bool doFlow(string time);
|
||||
|
||||
string getHTMLSingleStep(string host);
|
||||
string getReadout(int _analog, bool _extendedResolution);
|
||||
string getReadout(int _analog, bool _extendedResolution = false, int prev = -1);
|
||||
|
||||
void DrawROI(CImageBasis *_zw);
|
||||
|
||||
|
||||
@@ -49,6 +49,9 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)){
|
||||
_classname = "ClassFlowMQTT";
|
||||
}
|
||||
if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)){
|
||||
_classname = "ClassFlowInfluxDB";
|
||||
}
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare(_classname) == 0){
|
||||
@@ -67,14 +70,16 @@ std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
||||
return ("Take Image");
|
||||
if (_input.compare("ClassFlowAlignment") == 0)
|
||||
return ("Aligning");
|
||||
//if (_input.compare("ClassFlowAnalog") == 0)
|
||||
// return ("Analog ROIs");
|
||||
if (_input.compare("ClassFlowCNNGeneral") == 0)
|
||||
return ("Digitalization of ROIs");
|
||||
if (_input.compare("ClassFlowMQTT") == 0)
|
||||
return ("Sending MQTT");
|
||||
if (_input.compare("ClassFlowInfluxDB") == 0)
|
||||
return ("Sending InfluxDB");
|
||||
if (_input.compare("ClassFlowPostProcessing") == 0)
|
||||
return ("Processing");
|
||||
if (_input.compare("ClassFlowWriteList") == 0)
|
||||
return ("Processing");
|
||||
|
||||
return "Unkown Status";
|
||||
}
|
||||
@@ -180,7 +185,13 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
}
|
||||
if (toUpper(_type).compare("[MQTT]") == 0)
|
||||
cfc = new ClassFlowMQTT(&FlowControll);
|
||||
|
||||
if (toUpper(_type).compare("[INFLUXDB]") == 0)
|
||||
cfc = new ClassFlowInfluxDB(&FlowControll);
|
||||
|
||||
if (toUpper(_type).compare("[WRITELIST]") == 0)
|
||||
cfc = new ClassFlowWriteList(&FlowControll);
|
||||
|
||||
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
|
||||
{
|
||||
cfc = new ClassFlowPostProcessing(&FlowControll, flowanalog, flowdigit);
|
||||
@@ -632,35 +643,7 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getJSON()
|
||||
string ClassFlowControll::getJSON(std::string _id, std::string _mac)
|
||||
{
|
||||
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||
|
||||
std::string json="{\n";
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
json += "\"" + (*NUMBERS)[i]->name + "\":\n";
|
||||
json += " {\n";
|
||||
if ((*NUMBERS)[i]->ReturnValue.length() > 0)
|
||||
json += " \"value\": " + (*NUMBERS)[i]->ReturnValue + ",\n";
|
||||
else
|
||||
json += " \"value\": \"\",\n";
|
||||
json += " \"raw\": \"" + (*NUMBERS)[i]->ReturnRawValue + "\",\n";
|
||||
json += " \"error\": \"" + (*NUMBERS)[i]->ErrorMessageText + "\",\n";
|
||||
if ((*NUMBERS)[i]->ReturnRateValue.length() > 0)
|
||||
json += " \"rate\": " + (*NUMBERS)[i]->ReturnRateValue + ",\n";
|
||||
else
|
||||
json += " \"rate\": \"\",\n";
|
||||
|
||||
json += " \"timestamp\": \"" + (*NUMBERS)[i]->timeStamp + "\"\n";
|
||||
if ((i+1) < (*NUMBERS).size())
|
||||
json += " },\n";
|
||||
else
|
||||
json += " }\n";
|
||||
}
|
||||
json += "}";
|
||||
|
||||
return json;
|
||||
return flowpostprocessing->GetJSON(_id, _mac);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "ClassFlowMQTT.h"
|
||||
#include "ClassFlowInfluxDB.h"
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowWriteList.h"
|
||||
|
||||
|
||||
#define READOUT_TYPE_VALUE 0
|
||||
@@ -48,7 +50,7 @@ public:
|
||||
string UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern);
|
||||
string GetPrevalue(std::string _number = "");
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
string getJSON();
|
||||
string getJSON(std::string _id = "", std::string _mac = "");
|
||||
|
||||
string TranslateAktstatus(std::string _input);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ struct roi {
|
||||
int posx, posy, deltax, deltay;
|
||||
float result_float;
|
||||
int result_klasse;
|
||||
bool isReject;
|
||||
string name;
|
||||
CImageBasis *image, *image_org;
|
||||
};
|
||||
@@ -36,9 +37,10 @@ struct NumberPost {
|
||||
float PreValue; // letzter Wert, der gut ausgelesen wurde
|
||||
float Value; // letzer ausgelesener Wert, inkl. Korrekturen
|
||||
string ReturnRateValue; // RückgabewertRate
|
||||
string ReturnChangeAbsolute; // RückgabewertRate
|
||||
string ReturnRawValue; // Rohwert (mit N & führenden 0)
|
||||
string ReturnValue; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
|
||||
string ReturnPreValue; // korrigierter Rückgabewert ohne Fehlermeldung
|
||||
string ReturnPreValue; // korrigierter Rückgabewert ohne Fehlermeldung
|
||||
string ErrorMessageText; // Fehlermeldung bei Consistency Check
|
||||
int AnzahlAnalog;
|
||||
int AnzahlDigital;
|
||||
|
||||
161
code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp
Normal file
161
code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include <sstream>
|
||||
#include "ClassFlowInfluxDB.h"
|
||||
#include "Helper.h"
|
||||
#include "connect_wlan.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
#include "interface_influxdb.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
void ClassFlowInfluxDB::SetInitialParameter(void)
|
||||
{
|
||||
uri = "";
|
||||
database = "";
|
||||
measurement = "";
|
||||
|
||||
OldValue = "";
|
||||
flowpostprocessing = NULL;
|
||||
user = "";
|
||||
password = "";
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
disabled = false;
|
||||
InfluxDBenable = false;
|
||||
}
|
||||
|
||||
ClassFlowInfluxDB::ClassFlowInfluxDB()
|
||||
{
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
ListFlowControll = lfc;
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
previousElement = _prev;
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowInfluxDB::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> zerlegt;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
if (toUpper(aktparamgraph).compare("[INFLUXDB]") != 0)
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
printf("while loop reading line: %s\n", aktparamgraph.c_str());
|
||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||
if ((toUpper(zerlegt[0]) == "USER") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->user = zerlegt[1];
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "PASSWORD") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->password = zerlegt[1];
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "URI") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->uri = zerlegt[1];
|
||||
}
|
||||
if (((toUpper(zerlegt[0]) == "MEASUREMENT")) && (zerlegt.size() > 1))
|
||||
{
|
||||
this->measurement = zerlegt[1];
|
||||
}
|
||||
if (((toUpper(zerlegt[0]) == "DATABASE")) && (zerlegt.size() > 1))
|
||||
{
|
||||
this->database = zerlegt[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ((uri.length() > 0) && (database.length() > 0) && (measurement.length() > 0))
|
||||
{
|
||||
printf("Init InfluxDB with uri: %s, measurement: %s, user: %s, password: %s\n", uri.c_str(), measurement.c_str(), user.c_str(), password.c_str());
|
||||
InfluxDBInit(uri, database, measurement, user, password);
|
||||
InfluxDBenable = true;
|
||||
} else {
|
||||
printf("InfluxDB init skipped as we are missing some parameters");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowInfluxDB::GetInfluxDBMeasurement()
|
||||
{
|
||||
return measurement;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowInfluxDB::doFlow(string zwtime)
|
||||
{
|
||||
if (!InfluxDBenable)
|
||||
return true;
|
||||
|
||||
std::string result;
|
||||
std::string resulterror = "";
|
||||
std::string resultraw = "";
|
||||
std::string resultrate = "";
|
||||
std::string resulttimestamp = "";
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
result = (*NUMBERS)[i]->ReturnValue;
|
||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
if (namenumber == "default")
|
||||
namenumber = "value";
|
||||
else
|
||||
namenumber = namenumber + "/value";
|
||||
|
||||
if (result.length() > 0 && resulttimestamp.length() > 0)
|
||||
InfluxDBPublish(namenumber, result, resulttimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
OldValue = result;
|
||||
|
||||
return true;
|
||||
}
|
||||
31
code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h
Normal file
31
code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "ClassFlow.h"
|
||||
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClassFlowInfluxDB :
|
||||
public ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::string uri, database, measurement;
|
||||
std::string OldValue;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
std::string user, password;
|
||||
bool InfluxDBenable;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
|
||||
public:
|
||||
ClassFlowInfluxDB();
|
||||
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
|
||||
string GetInfluxDBMeasurement();
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string name(){return "ClassFlowInfluxDB";};
|
||||
};
|
||||
|
||||
@@ -25,7 +25,8 @@ void ClassFlowMQTT::SetInitialParameter(void)
|
||||
OldValue = "";
|
||||
flowpostprocessing = NULL;
|
||||
user = "";
|
||||
password = "";
|
||||
password = "";
|
||||
SetRetainFlag = 0;
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
disabled = false;
|
||||
@@ -99,6 +100,12 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
this->uri = zerlegt[1];
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "SETRETAINFLAG") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
SetRetainFlag = 1;
|
||||
}
|
||||
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "CLIENTID") && (zerlegt.size() > 1))
|
||||
{
|
||||
@@ -118,7 +125,7 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
mainerrortopic = maintopic + "/connection";
|
||||
printf("Init MQTT with uri: %s, clientname: %s, user: %s, password: %s, maintopic: %s\n", uri.c_str(), clientname.c_str(), user.c_str(), password.c_str(), mainerrortopic.c_str());
|
||||
MQTTInit(uri, clientname, user, password, mainerrortopic, 60);
|
||||
MQTTPublish(mainerrortopic, "connected");
|
||||
MQTTPublish(mainerrortopic, "connected", SetRetainFlag);
|
||||
MQTTenable = true;
|
||||
}
|
||||
|
||||
@@ -142,6 +149,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
std::string resultraw = "";
|
||||
std::string resultrate = "";
|
||||
std::string resulttimestamp = "";
|
||||
std::string resultchangabs = "";
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
|
||||
@@ -150,17 +158,17 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
zw = maintopic + "/" + "uptime";
|
||||
char uptimeStr[11];
|
||||
sprintf(uptimeStr, "%ld", (long)getUpTime());
|
||||
MQTTPublish(zw, uptimeStr);
|
||||
MQTTPublish(zw, uptimeStr, SetRetainFlag);
|
||||
|
||||
zw = maintopic + "/" + "freeMem";
|
||||
char freeheapmem[11];
|
||||
sprintf(freeheapmem, "%zu", esp_get_free_heap_size());
|
||||
MQTTPublish(zw, freeheapmem);
|
||||
MQTTPublish(zw, freeheapmem, SetRetainFlag);
|
||||
|
||||
zw = maintopic + "/" + "wifiRSSI";
|
||||
char rssi[11];
|
||||
sprintf(rssi, "%d", get_WIFI_RSSI());
|
||||
MQTTPublish(zw, rssi);
|
||||
MQTTPublish(zw, rssi, SetRetainFlag);
|
||||
|
||||
|
||||
if (flowpostprocessing)
|
||||
@@ -173,6 +181,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||
resultchangabs = (*NUMBERS)[i]->ReturnChangeAbsolute;
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
@@ -183,23 +192,27 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
|
||||
zw = namenumber + "value";
|
||||
if (result.length() > 0)
|
||||
MQTTPublish(zw, result);
|
||||
MQTTPublish(zw, result, SetRetainFlag);
|
||||
|
||||
zw = namenumber + "error";
|
||||
if (resulterror.length() > 0)
|
||||
MQTTPublish(zw, resulterror, 1);
|
||||
MQTTPublish(zw, resulterror, SetRetainFlag);
|
||||
|
||||
zw = namenumber + "rate";
|
||||
if (resultrate.length() > 0)
|
||||
MQTTPublish(zw, resultrate);
|
||||
MQTTPublish(zw, resultrate, SetRetainFlag);
|
||||
|
||||
zw = namenumber + "changeabsolut";
|
||||
if (resultchangabs.length() > 0)
|
||||
MQTTPublish(zw, resultchangabs, SetRetainFlag);
|
||||
|
||||
zw = namenumber + "raw";
|
||||
if (resultraw.length() > 0)
|
||||
MQTTPublish(zw, resultraw);
|
||||
MQTTPublish(zw, resultraw, SetRetainFlag);
|
||||
|
||||
zw = namenumber + "timestamp";
|
||||
if (resulttimestamp.length() > 0)
|
||||
MQTTPublish(zw, resulttimestamp);
|
||||
MQTTPublish(zw, resulttimestamp, SetRetainFlag);
|
||||
|
||||
|
||||
std::string json = "";
|
||||
@@ -218,7 +231,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
json += ",\"timestamp\":\""+resulttimestamp+"\"}";
|
||||
|
||||
zw = namenumber + "json";
|
||||
MQTTPublish(zw, json);
|
||||
MQTTPublish(zw, json, SetRetainFlag);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -234,7 +247,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
result = result + "\t" + zw;
|
||||
}
|
||||
}
|
||||
MQTTPublish(topic, result);
|
||||
MQTTPublish(topic, result, SetRetainFlag);
|
||||
}
|
||||
|
||||
OldValue = result;
|
||||
|
||||
@@ -13,6 +13,7 @@ protected:
|
||||
std::string OldValue;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
std::string user, password;
|
||||
int SetRetainFlag;
|
||||
bool MQTTenable;
|
||||
|
||||
std::string maintopic, mainerrortopic;
|
||||
|
||||
@@ -15,6 +15,42 @@
|
||||
#define PREVALUE_TIME_FORMAT_INPUT "%d-%d-%dT%d:%d:%d"
|
||||
|
||||
|
||||
std::string ClassFlowPostProcessing::GetJSON(std::string _id, std::string _mac, std::string _lineend)
|
||||
{
|
||||
std::string json="{" + _lineend;
|
||||
|
||||
for (int i = 0; i < NUMBERS.size(); ++i)
|
||||
{
|
||||
json += "\"" + NUMBERS[i]->name + "\":" + _lineend;
|
||||
json += " {" + _lineend;
|
||||
|
||||
if (_id.length() > 0)
|
||||
json += " \"ID\": \"" + _id + "\"," + _lineend;
|
||||
if (_mac.length() > 0)
|
||||
json += " \"MAC\": \"" + _mac + "\"," + _lineend;
|
||||
|
||||
if (NUMBERS[i]->ReturnValue.length() > 0)
|
||||
json += " \"value\": \"" + NUMBERS[i]->ReturnValue + "\"," + _lineend;
|
||||
else
|
||||
json += " \"value\": \"\"," + _lineend;
|
||||
json += " \"raw\": \"" + NUMBERS[i]->ReturnRawValue + "\"," + _lineend;
|
||||
json += " \"error\": \"" + NUMBERS[i]->ErrorMessageText + "\"," + _lineend;
|
||||
if (NUMBERS[i]->ReturnRateValue.length() > 0)
|
||||
json += " \"rate\": " + NUMBERS[i]->ReturnRateValue + "," + _lineend;
|
||||
else
|
||||
json += " \"rate\": \"\"," + _lineend;
|
||||
|
||||
json += " \"timestamp\": \"" + NUMBERS[i]->timeStamp + "\"" + _lineend;
|
||||
if ((i+1) < NUMBERS.size())
|
||||
json += " }," + _lineend;
|
||||
else
|
||||
json += " }" + _lineend;
|
||||
}
|
||||
json += "}";
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
string ClassFlowPostProcessing::GetPreValue(std::string _number)
|
||||
{
|
||||
std::string result;
|
||||
@@ -41,6 +77,8 @@ void ClassFlowPostProcessing::SetPreValue(float zw, string _numbers, bool _exter
|
||||
if (NUMBERS[j]->name == _numbers)
|
||||
{
|
||||
NUMBERS[j]->PreValue = zw;
|
||||
NUMBERS[j]->ReturnPreValue = std::to_string(zw);
|
||||
NUMBERS[j]->PreValueOkay = true;
|
||||
if (_extern)
|
||||
{
|
||||
time(&(NUMBERS[j]->lastvalue));
|
||||
@@ -505,7 +543,6 @@ void ClassFlowPostProcessing::InitNUMBERS()
|
||||
|
||||
_number->ReturnRawValue = ""; // Rohwert (mit N & führenden 0)
|
||||
_number->ReturnValue = ""; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
|
||||
// _number->ReturnValueNoError = ""; // korrigierter Rückgabewert ohne Fehlermeldung
|
||||
_number->ErrorMessageText = ""; // Fehlermeldung bei Consistency Check
|
||||
_number->ReturnPreValue = "";
|
||||
_number->PreValueOkay = false;
|
||||
@@ -524,7 +561,6 @@ void ClassFlowPostProcessing::InitNUMBERS()
|
||||
_number->Value = 0; // letzer ausgelesener Wert, inkl. Korrekturen
|
||||
_number->ReturnRawValue = ""; // Rohwert (mit N & führenden 0)
|
||||
_number->ReturnValue = ""; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
|
||||
// _number->ReturnValueNoError = ""; // korrigierter Rückgabewert ohne Fehlermeldung
|
||||
_number->ErrorMessageText = ""; // Fehlermeldung bei Consistency Check
|
||||
|
||||
_number->Nachkomma = _number->AnzahlAnalog;
|
||||
@@ -612,18 +648,29 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||
|
||||
UpdateNachkommaDecimalShift();
|
||||
|
||||
int previous_value = -1;
|
||||
|
||||
if (NUMBERS[j]->analog_roi)
|
||||
{
|
||||
NUMBERS[j]->ReturnRawValue = flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution);
|
||||
if (NUMBERS[j]->ReturnRawValue.length() > 0)
|
||||
{
|
||||
char zw = NUMBERS[j]->ReturnRawValue[0];
|
||||
if (zw >= 48 && zw <=57)
|
||||
previous_value = zw - 48;
|
||||
}
|
||||
}
|
||||
|
||||
if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi)
|
||||
NUMBERS[j]->ReturnRawValue = "." + NUMBERS[j]->ReturnRawValue;
|
||||
|
||||
if (NUMBERS[j]->digit_roi)
|
||||
{
|
||||
if (NUMBERS[j]->analog_roi)
|
||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false);
|
||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false, previous_value) + NUMBERS[j]->ReturnRawValue;
|
||||
else
|
||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution); // Extended Resolution nur falls es keine analogen Ziffern gibt
|
||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution, previous_value); // Extended Resolution nur falls es keine analogen Ziffern gibt
|
||||
}
|
||||
if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi)
|
||||
NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + ".";
|
||||
|
||||
if (NUMBERS[j]->analog_roi)
|
||||
NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution);
|
||||
|
||||
NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
|
||||
|
||||
@@ -675,7 +722,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||
|
||||
if (NUMBERS[j]->useMaxRateValue && PreValueUse && NUMBERS[j]->PreValueOkay)
|
||||
{
|
||||
float _ratedifference;
|
||||
float _ratedifference;
|
||||
if (NUMBERS[j]->RateType == RateChange)
|
||||
_ratedifference = NUMBERS[j]->FlowRateAct;
|
||||
else
|
||||
@@ -691,6 +738,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||
}
|
||||
}
|
||||
|
||||
NUMBERS[j]->ReturnChangeAbsolute = RundeOutput(NUMBERS[j]->Value - NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
|
||||
NUMBERS[j]->lastvalue = imagetime;
|
||||
NUMBERS[j]->PreValue = NUMBERS[j]->Value;
|
||||
NUMBERS[j]->PreValueOkay = true;
|
||||
|
||||
@@ -60,6 +60,8 @@ public:
|
||||
string GetPreValue(std::string _number = "");
|
||||
void SetPreValue(float zw, string _numbers, bool _extern = false);
|
||||
|
||||
std::string GetJSON(std::string _id = "", std::string _mac = "", std::string _lineend = "\n");
|
||||
|
||||
void UpdateNachkommaDecimalShift();
|
||||
|
||||
std::vector<NumberPost*>* GetNumbers(){return &NUMBERS;};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user