mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-07 12:06:58 +03:00
Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbe884ad63 | ||
|
|
1dd703b337 | ||
|
|
bcb1d98191 | ||
|
|
1f5486e8cc | ||
|
|
1371be6f2c | ||
|
|
b0d8ed6248 | ||
|
|
379f4585e3 | ||
|
|
45a71981c8 | ||
|
|
ac3409f644 | ||
|
|
11c33f81dd | ||
|
|
641cc860d8 | ||
|
|
2029bd6e8a | ||
|
|
1ca5e1218d | ||
|
|
887c704f63 | ||
|
|
567dc74cd7 | ||
|
|
53606d5055 | ||
|
|
19a6c21c44 | ||
|
|
3a4b11e4b3 | ||
|
|
d981574ab2 | ||
|
|
eb817739b9 | ||
|
|
8972f17638 | ||
|
|
4d997d9473 | ||
|
|
e79c86c7b6 | ||
|
|
96bb86536f | ||
|
|
2daf6c8b3f | ||
|
|
24b46158de | ||
|
|
63d336ba28 | ||
|
|
8dd3a92671 | ||
|
|
8e8897c70d | ||
|
|
eac513411e | ||
|
|
98f9274085 | ||
|
|
884dd9fc3b | ||
|
|
04c564936a | ||
|
|
957138960a | ||
|
|
58a0297915 | ||
|
|
61bf536207 | ||
|
|
4136a7b372 | ||
|
|
b3afc9961d | ||
|
|
27e2e042da | ||
|
|
cc659bad30 | ||
|
|
776931c0ad | ||
|
|
e22b4b6fe1 | ||
|
|
cad534bffe | ||
|
|
3b44adb6fa | ||
|
|
cc0bd1473f | ||
|
|
58124d27bf | ||
|
|
9ad118814a | ||
|
|
270f8dd093 | ||
|
|
fde0ae4781 | ||
|
|
b87ce8c75d | ||
|
|
e372c87836 | ||
|
|
f8478d7742 | ||
|
|
a3d6923fec | ||
|
|
7bfa22187d | ||
|
|
7a8affae32 | ||
|
|
e48dd7c4c4 | ||
|
|
6d298c1746 | ||
|
|
4fe9ab92e0 | ||
|
|
f2ac4cdc64 | ||
|
|
be902401d1 | ||
|
|
020e93bca2 | ||
|
|
61dfdc2258 | ||
|
|
a98e3c8eab | ||
|
|
58a90ff706 | ||
|
|
d0bf12f3d4 | ||
|
|
af16785bbf | ||
|
|
18f6e83a2c | ||
|
|
147d97421b | ||
|
|
dcf2feb7aa | ||
|
|
e63e940b96 | ||
|
|
68b0fb83ee | ||
|
|
f15e5f060a | ||
|
|
e2a403441f | ||
|
|
9b3665b9c6 | ||
|
|
f4c8bf9206 | ||
|
|
c033db9c31 | ||
|
|
9300526f49 | ||
|
|
b6dd1f7f2d | ||
|
|
1e6eddca04 | ||
|
|
19ca0d7dd7 | ||
|
|
7fcb5d1c0c | ||
|
|
dd995ec28a | ||
|
|
af99de3535 | ||
|
|
3567cc2fb0 | ||
|
|
5e9d9bd264 | ||
|
|
62447c1bb9 | ||
|
|
a86434c9a2 | ||
|
|
b7b70299f7 | ||
|
|
eb02e0aec1 | ||
|
|
7816e53db7 | ||
|
|
7ae08e572a | ||
|
|
47d15d8adb | ||
|
|
0dac0e87e4 | ||
|
|
b290099d5b | ||
|
|
f6b1a41a0b | ||
|
|
e529af04cf | ||
|
|
6c365dd949 | ||
|
|
32f15fc557 | ||
|
|
6f06af1d5f | ||
|
|
a91f99faab | ||
|
|
17a87b23a1 | ||
|
|
d4b5ec2ae2 | ||
|
|
1bcaf09855 | ||
|
|
fa3842b2b4 | ||
|
|
ea72256e56 | ||
|
|
be5828cb3e | ||
|
|
104b72505c | ||
|
|
23728a0686 | ||
|
|
eaaa856b13 | ||
|
|
01e81d02b5 | ||
|
|
9ae8d0a512 | ||
|
|
da16322fb8 | ||
|
|
a6d39afc26 | ||
|
|
1b6a124f54 | ||
|
|
8aff6bf8f3 |
93
Changelog.md
93
Changelog.md
@@ -2,6 +2,99 @@
|
||||
|
||||
|
||||
|
||||
##### 8.5.0 - Multi Meter Support (2021-10-07)
|
||||
|
||||
* Upgrade digital CNN to v13.1.0 (added new images)
|
||||
* bug fix: wlan password with space, double digit output
|
||||
|
||||
##### 8.4.0 - Multi Meter Support (2021-09-25)
|
||||
|
||||
* License change (remove MIT license, remark see below)
|
||||
|
||||
* html: show hostname in title and main page
|
||||
|
||||
* configuration:
|
||||
|
||||
* moved setting `ExtendedResolution` to individual number settings
|
||||
* New parameter `IgnoreLeadingNaN` (delete leading NaN's specifically)
|
||||
* **ATTENTION**: update of the `config.ini` needed (open, adjust `ExtendedResolution`, save)
|
||||
|
||||
* Bug fixing (html, images of recognized numbers)
|
||||
|
||||
|
||||
|
||||
### **ATTENTION: LICENSE CHANGE - removal of MIT License.**
|
||||
|
||||
- Currently no licence published - copyright belongs to author
|
||||
- If you are interested in a commercial usage or dedicated versions please contact the developer
|
||||
- no limits to private usage
|
||||
|
||||
|
||||
|
||||
##### 8.3.0 - Multi Meter Support (2021-09-12)
|
||||
|
||||
* Upgrade digital CNN to v12.1.0 (added new images)
|
||||
* Dedicated NaN handling, internal refactoring (CNN-Handling)
|
||||
* HTML: confirmation after config.ini update
|
||||
* Bug fixing
|
||||
|
||||
##### 8.2.0 - Multi Meter Support (2021-08-24)
|
||||
|
||||
* Improve server responsiveness
|
||||
* Flow status and prevalue status in overview
|
||||
* Improved prevalue handling
|
||||
|
||||
##### 8.1.0 - Multi Meter Support (2021-08-12)
|
||||
|
||||
* GPIO: using the general mqtt main topic for GPIO
|
||||
|
||||
* Upgrade digital CNN to v12.0.0 (added new images)
|
||||
* Update tfmicro to new master (2021-08-07)
|
||||
* Bug fix: remove text in mqtt value, remove connect limit in wlan reconnet
|
||||
|
||||
##### 8.0.5 - Multi Meter Support (2021-08-01)
|
||||
|
||||
* NEW 8.0.5: bug fix: saving prevalue
|
||||
* NEW 8.0.4: bug fix: load config.ini after upgrade
|
||||
* NEW 8.0.3: bug fix: reboot during `config.ini` handling, html error
|
||||
* NEW 8.0.2: saving roundes prevalue, bug fix html server
|
||||
* NEW 8.0.1: bug fix: html handling of parameter `FixedExposure` and `ImageSize`
|
||||
* Dual / multi meter support (more than 1 number to be recognized)
|
||||
This is implemented with the feature "number" on the ROI definition as well as selected options
|
||||
* MQTT: standardization of the naming - including new topics (`json`, `freeMem `, `uptime`)c
|
||||
* Preparation for extended GPIO support (thanks to Zwerk2k) - not tested and fully functional yet
|
||||
* Bug fixing: html server, memory leak, MQTT connect, hostname, turn of flash LED
|
||||
|
||||
<span style="color: red;">**ATTENTION: the configuration and prevalue files are modified automatically and will not be backward compatible!**</span>
|
||||
|
||||
##### 7.1.2 MQTT-Update - (2021-06-17)
|
||||
|
||||
* NEW: 7.1.2: bug fix setting hostname, Flash-LED not off during reboot
|
||||
|
||||
* NEW: 7.1.1: bug fix wlan password with "=" (again)
|
||||
|
||||
* MQTT error message: changes "no error", send retain flag
|
||||
|
||||
* Update wlan handling to esp-idf 4.1
|
||||
|
||||
* Upgrade digital CNN to v8.7.0 (added new images)
|
||||
|
||||
* Bug fix: MQTT, WLAN, LED-Controll, GPIO usage, fixed IP, calculation flow rate
|
||||
|
||||
|
||||
|
||||
##### 7.0.1 MQTT-Update - (2021-05-13)
|
||||
|
||||
* NEW: 7.0.1: bug fix wlan password with "="
|
||||
|
||||
* Upgrade digital CNN to v8.5.0 (added new images)
|
||||
|
||||
* New MQTT topics: flow rate (units/minute), time stamp (last correct read readout)
|
||||
|
||||
* Update MQTT/Error topic to " " in case no error (instead of empty string)
|
||||
|
||||
* Portrait or landscape image orientation in rotated image (avoid cropping)
|
||||
|
||||
##### 6.7.2 Image Processing in Memory - (2021-05-01)
|
||||
|
||||
* NEW 6.7.2: Updated html for setup modus - remove reboot on edit configuration)
|
||||
|
||||
@@ -11,6 +11,120 @@
|
||||
|
||||
____
|
||||
|
||||
#### #24 Show Mqtt state directly in Webserver
|
||||
|
||||
* Show MQTT log in Web page. E.g. connection established or failed to connect...
|
||||
|
||||
|
||||
|
||||
|
||||
#### #23 CPU Temp and Mqtt values
|
||||
|
||||
* Show the CPU Temp directly in Webpage. Also add the value to MQTT sending
|
||||
|
||||
|
||||
|
||||
#### #22 Direct hint to the different neural network files in the other repositories
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/644
|
||||
|
||||
|
||||
|
||||
#### #21 Extended "CheckDigitalConsistency" Logik
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/590
|
||||
|
||||
|
||||
|
||||
#### #20 Deep sleep and push mode
|
||||
|
||||
* Let the device be normally in deep sleep state, and wake it up periodically to collect data and push it via MQTT or HTTP post.
|
||||
|
||||
|
||||
|
||||
#### #19 Extended log informations
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/580
|
||||
|
||||
|
||||
|
||||
#### #18 Document WLAN-strength in web page
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/563
|
||||
|
||||
|
||||
|
||||
#### #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
|
||||
|
||||
|
||||
#### #16 Serial Communication
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/512
|
||||
* Send the readout value via RX/TX interface with a dedicated TAG
|
||||
* Make dedicated communication FlowModule
|
||||
* Modification of RX/TX communication
|
||||
* Configuration interfache
|
||||
|
||||
|
||||
#### #15 Calibration for FishEye image
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/507
|
||||
|
||||
1. The development of such a correction algorithm with the libraries, that are available for the ESP32 environment.
|
||||
2. New module for integration of the flow into the image processing flow.
|
||||
3. Extension of the configuration (config.ini) and html-pages
|
||||
4. Parameter adjustment and testing for every different fish-eye module
|
||||
5. Maintenance for further updates / modules, ...
|
||||
|
||||
|
||||
|
||||
#### #14 Backup and restore option for configuration
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/459
|
||||
|
||||
* Implement a zip file compression for store and restore
|
||||
|
||||
* Update the html to handle it
|
||||
|
||||
|
||||
|
||||
#### #13 Manage non linear gauge without CNN re-training
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/443
|
||||
|
||||
* Implement a look up table for non linear analog meters
|
||||
|
||||
|
||||
|
||||
#### #12 Less reboots due to memory leakage
|
||||
|
||||
* Issue: #414 & #425 #430
|
||||
|
||||
|
||||
|
||||
#### #11 MQTT - configurable payload
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/344
|
||||
|
||||
|
||||
|
||||
#### #10 Improve and bug fix logging of images
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/307
|
||||
|
||||
|
||||
|
||||
#### #9 Basic auth for the UI
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/283
|
||||
|
||||
* Implementation of an authentication mechanism.
|
||||
|
||||
#### #8 MQTT configurable readout intervall
|
||||
|
||||
Make the readout intervall configurable via MQTT.
|
||||
@@ -27,13 +141,13 @@ To do:
|
||||
* Implement a checking algo
|
||||
* Extend the firmware and html page for the error handling
|
||||
|
||||
#### #6 Check for double ROI names
|
||||
#### ~~#6 Check for double ROI names~~ - implemented v8.0.0
|
||||
|
||||
Check during configuration, that ROI names are unique.
|
||||
~~Check during configuration, that ROI names are unique.~~
|
||||
|
||||
To do:
|
||||
~~To do:~~
|
||||
|
||||
* Implementation of ROI name checking in html code before saving analog or digital ROIs
|
||||
* ~~Implementation of ROI name checking in html code before saving analog or digital ROIs~~
|
||||
|
||||
|
||||
|
||||
@@ -49,31 +163,31 @@ To do:
|
||||
|
||||
|
||||
|
||||
#### #4 Initial Shifting and Rotation
|
||||
#### ~~#4 Initial Shifting and Rotation~~ - implemented v7.0.0
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/123
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/123~~
|
||||
|
||||
Implementation of a shifting additional to the initial rotation of the raw camera input
|
||||
~~Implementation of a shifting additional to the initial rotation of the raw camera input~~
|
||||
|
||||
To do:
|
||||
~~To do:~~
|
||||
|
||||
* Implementation of shifting
|
||||
* Extension of configuration
|
||||
* Adaption of the html configuration to implement shifting
|
||||
* ~~Implementation of shifting~~
|
||||
* ~~Extension of configuration~~
|
||||
* ~~Adaption of the html configuration to implement shifting~~
|
||||
|
||||
|
||||
|
||||
#### #3 Allow grouping of digits to multiple reading values
|
||||
#### ~~#3 Allow grouping of digits to multiple reading values~~ - implemented v8.0.0
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/123
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/123~~
|
||||
|
||||
Implementation of two different independent readouts in one setup
|
||||
~~Implementation of two different independent readouts in one setup~~
|
||||
|
||||
To do:
|
||||
~~To do:~~
|
||||
|
||||
* ~~Extend the configuration, setting and processing flow for two independend readouts~~
|
||||
|
||||
* Extend the configuration, setting and processing flow for two independend readouts
|
||||
|
||||
https://github.com/jomjol/AI-on-the-edge-device/issues/123
|
||||
|
||||
|
||||
|
||||
@@ -96,15 +210,16 @@ To do:
|
||||
|
||||
____
|
||||
|
||||
#### #1 Optional GPIO for external flash/lighting
|
||||
#### ~~#1 Optional GPIO for external flash/lighting~~ - implemented (v8.0.0)
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/133
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/133~~
|
||||
|
||||
Implementation of an an extrnal flash / lightning through GPIOs.
|
||||
* available GPIOs: 12 & 13 (currently in use for html switching)
|
||||
~~Implementation of an an extrnal flash / lightning through GPIOs.~~
|
||||
|
||||
To do:
|
||||
* ~~available GPIOs: 12 & 13 (currently in use for html switching)~~
|
||||
|
||||
* 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
|
||||
~~To do:~~
|
||||
|
||||
* ~~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~~
|
||||
21
LICENSE
21
LICENSE
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 jomjol
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
158
README.md
158
README.md
@@ -6,6 +6,8 @@ This is an example of Artificial Intelligence (AI) calculations on a very cheap
|
||||
|
||||
A 3d-printable housing can be found here: https://www.thingiverse.com/thing:4573481
|
||||
|
||||
or here https://www.thingiverse.com/thing:5028229
|
||||
|
||||
respectively ESP32-Cam housing only: https://www.thingiverse.com/thing:4571627
|
||||
|
||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter_all.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/main.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/size.png" width="200">
|
||||
@@ -32,58 +34,147 @@ If you have any technical topics, you can file a issue in this repository.
|
||||
|
||||
In other cases you can contact the developer via email: <img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/mail.jpg" height="25">
|
||||
|
||||
## Change log
|
||||
------
|
||||
## Coming next
|
||||
|
||||
* Automated update of the neural network file (tflite) to make the learing of additional pictures much easier and automated (GitHub action)
|
||||
* New "hyprid" neural network for digital numbers --> allowing the detection of intermediate states ("ring between two numbers") as a subdigit
|
||||
|
||||
|
||||
------
|
||||
|
||||
## Change log
|
||||
### Known Issues
|
||||
|
||||
* slow response of web server during picture analysis
|
||||
* spontaneous reboots (mostly due to html access during image processing) - self recovery implemented
|
||||
|
||||
------
|
||||
|
||||
**General remark:** Beside the `firmware.bin`, typically also the content of `/html` needs to be updated!
|
||||
|
||||
|
||||
|
||||
##### 8.0.1 - Multi Meter Support (2021-07-18)
|
||||
|
||||
* NEW 8.0.1: bug fix html handling of parameter `FixedExposure` and `ImageSize`
|
||||
* Dual / multi meter support (more than 1 number to be recognized)
|
||||
This is implemented with the feature "number" on the ROI definition as well as selected options
|
||||
* MQTT: standardization of the naming - including new topics (`json`, `freeMem `, `uptime`)c
|
||||
* Preparation for extended GPIO support (thanks to Zwerk2k) - not tested and fully functional yet
|
||||
* Bug fixing: html server, memory leak, MQTT connect, hostname, turn of flash LED
|
||||
|
||||
<span style="color: red;">**ATTENTION: the configuration and prevalue files are modified automatically and will not be backward compatible!**</span>
|
||||
------
|
||||
|
||||
|
||||
|
||||
##### 7.1.2 MQTT-Update - (2021-06-17)
|
||||
##### 10.5.0 - Stability Increase (2022-02-18)
|
||||
|
||||
* NEW: 7.1.2: bug fix setting hostname, Flash-LED not off during rebootNEW: 7.1.1: bug fix wlan password with "=" (again)
|
||||
* MQTT error message: changes "no error", send retain flag
|
||||
* Update wlan handling to esp-idf 4.1
|
||||
* Upgrade digital CNN to v8.7.0 (added new images)
|
||||
* Bug fix: MQTT, WLAN, LED-Controll, GPIO usage, fixed IP, calculation flow rate
|
||||
- MQTT: changed wifi naming to "wifiRSSI"
|
||||
|
||||
- HTML: check select able values for consistency
|
||||
- Refactoring of check postprocessing consistency (e.g. max rate, negative rate, ...)
|
||||
- Bug Fix: corrected error in "Check Consistency Increase"
|
||||
|
||||
|
||||
|
||||
##### 10.4.0 - Stability Increase (2022-02-12)
|
||||
|
||||
- Graphical configuration: select available neural network files (*.tfl, *.tflite) from drop down menu
|
||||
- OTA-update: add option to upload tfl / tflite files to the correct locatioin (`/config/`)
|
||||
- in future the new files will also be copied to the `firmware` directory of the repository
|
||||
- Added Wifi RSSI to MQTT information
|
||||
- Updated analog neural network file (`ana-s3-q-20220105.tflite`)
|
||||
- Updated digital neural network file (`dig-s1-q-20220102.tflite`)
|
||||
- Updated build environment to `Espressif 3.5.0`
|
||||
|
||||
|
||||
|
||||
##### 10.3.0 - Stability Increase (2022-01-29)
|
||||
|
||||
- Implemented LED flash dimming (`LEDIntensity`).
|
||||
Remark: as auto illumination in the camera is used, this is rather for energy saving. It will not help reducing reflections
|
||||
- Additional camera parameters: saturation, contrast (although not too much impact yet)
|
||||
- Readings with not automatically removable "N"s are handled like "error" --> no return value in the field "value" anymore
|
||||
(still reported back via field "raw value")
|
||||
- Updated esp32 camera hardware driver
|
||||
- Bug fix: MQTT, html improvements
|
||||
|
||||
**ATTENTION: The new ESP32 camera hardware driver is much more stable on newer OV2640 versions (no or much less reboots) but seems to be not fully compatible with older versions.**
|
||||
|
||||
* If you have problem with stalled systems you can try the following
|
||||
- Update the parameter `ImageQuality` to `12` instead of current value `5` (manually in the `config.ini`)
|
||||
|
||||
- If this is not helping, you might need to update your hardware or stay with version 9.2
|
||||
|
||||
##### 10.2.0 - Stability Increase (2022-01-14)
|
||||
|
||||
- Due to the update camera driver, the image looks different and a new setup might be needed
|
||||
|
||||
- Update reference image
|
||||
- Update Alignment marks
|
||||
|
||||
- Reduce reboot due to camera problems
|
||||
|
||||
- Update esp32-camera to new version (master as of 2022-01-09)
|
||||
|
||||
|
||||
|
||||
##### 7.0.1 MQTT-Update - (2021-05-13)
|
||||
##### 10.1.1 - Stability Increase (2022-01-12)
|
||||
|
||||
* NEW: 7.0.1: bug fix wlan password with "="
|
||||
- Bug Fix MQTT problem
|
||||
- Issue:
|
||||
- Changing from v9.x to 10.x the MQTT-paramter "Topic" was renamed into "MainTopic" to address multiple number meters This renaming should have been done automatically in the background within the graphical configuration, but was not working. Instead the parameter "Topic" was deleted and "MainTopic" was set to disabled and "undefined".
|
||||
- ToDo
|
||||
- Update the `html.zip`
|
||||
- If old `config.ini` available: copy it to `/config`, open the graphical configuration and save it again.
|
||||
- If old `config.ini` not available: reset the parameter "MainTopic" within the `config.ini` manually
|
||||
- Reboot
|
||||
|
||||
* Upgrade digital CNN to v8.5.0 (added new images)
|
||||
##### 10.1.0 - Stability Increase (2022-01-09)
|
||||
|
||||
* New MQTT topics: flow rate (units/minute), time stamp (last correct read readout)
|
||||
- Reduce ESP32 frequency to 160MHz
|
||||
|
||||
* Update MQTT/Error topic to " " in case no error (instead of empty string)
|
||||
- Update tflite (new source: https://github.com/espressif/tflite-micro-esp-examples)
|
||||
|
||||
* Portrait or landscape image orientation in rotated image (avoid cropping)
|
||||
- Update analog neural network (ana-s3-q-20220105.tflite)
|
||||
|
||||
- Update digital neural network (dig-s1-q-20220102.tflite)
|
||||
|
||||
- Increased web-server buffers
|
||||
- bug fix: compiler compatibility
|
||||
|
||||
##### 10.0.2 - Stability Increase (2022-01-01)
|
||||
|
||||
- NEW v10.0.2: Corrected JSON error
|
||||
|
||||
- Updated compiler toolchain to ESP-IDF 4.3
|
||||
|
||||
- Removal of memory leak
|
||||
|
||||
- Improved error handling during startup (check PSRAM and camera with remark in logfile)
|
||||
|
||||
- MQTT: implemented raw value additionally, removal of regex contrain
|
||||
|
||||
- Normalized Parameter ``MaxRateValue`` to "change per minute"
|
||||
|
||||
- HTML: improved input handling
|
||||
|
||||
- Corrected error handling: in case of error the old value, rate, timestamp are not transmitted any more
|
||||
|
||||
|
||||
|
||||
##### 9.2.0 - External Illumination (2021-12-02)
|
||||
|
||||
- Direct JSON access: ``http://IP-ADRESS/json``
|
||||
- Error message in log file in case camera error during startup
|
||||
- Upgrade analog CNN to v9.1.0
|
||||
- Upgrade digital CNN to v13.3.0 (added new images)
|
||||
- html: support of different ports
|
||||
|
||||
##### 9.1.1 - External Illumination (2021-11-16)
|
||||
|
||||
- NEW 9.1.1 bug fix: LED implemenetation
|
||||
- External LEDs: change control mode (resolve bug with more than 2 LEDs)
|
||||
- Additional info into log file
|
||||
- Bug fix: decimal shift, html, log file
|
||||
|
||||
##### 9.0.0 - External Illumination (2021-10-23)
|
||||
|
||||
* Implementation of external illumination to adjust positioning, brightness and color of the illumination now individually
|
||||
* Technical details can be found in the wiki: https://github.com/jomjol/AI-on-the-edge-device/wiki/External-LED
|
||||
<img src="https://raw.githubusercontent.com/jomjol/ai-on-the-edge-device/master/images/intern_vs_external.jpg" width="500">
|
||||
* New housing published for external LEDs and small clearing: https://www.thingiverse.com/thing:5028229
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Additional ideas
|
||||
|
||||
@@ -95,6 +186,10 @@ There are some ideas and feature request, which are not followed currently - mai
|
||||
|
||||
## History
|
||||
|
||||
##### 8.5.0 - Multi Meter Support (2021-10-07)
|
||||
|
||||
##### 7.1.2 MQTT-Update - (2021-06-17)
|
||||
|
||||
##### 6.7.2 Image Processing in Memory - (2021-05-01)
|
||||
|
||||
##### 5.0.0 Setup Modus - (2020-12-06)
|
||||
@@ -117,8 +212,3 @@ There are some ideas and feature request, which are not followed currently - mai
|
||||
|
||||
#### [Full Changelog](Changelog.md)
|
||||
|
||||
|
||||
|
||||
## Solved topics
|
||||
|
||||
* n.a.
|
||||
|
||||
3
code/.helper/copy-esp-idf.bat
Normal file
3
code/.helper/copy-esp-idf.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
copy "..\..\code\build\esp32cam-server-only.bin" "..\..\firmware\firmware.bin"
|
||||
copy "..\..\code\build\bootloader\bootloader.bin" "..\..\firmware\bootloader.bin"
|
||||
copy "..\..\code\build\partition_table\partition-table.bin" "..\..\firmware\partitions.bin"
|
||||
79
code/components/esp32-camera-master/.github/workflows/build.yml
vendored
Normal file
79
code/components/esp32-camera-master/.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: Build examples
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build-master:
|
||||
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@latest
|
||||
with:
|
||||
path: 'examples'
|
||||
|
||||
build-release-v4_0:
|
||||
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-v4.0
|
||||
with:
|
||||
path: 'examples'
|
||||
|
||||
build-release-v4_1:
|
||||
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-v4.1
|
||||
with:
|
||||
path: 'examples'
|
||||
|
||||
build-release-v4_2:
|
||||
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-v4.2
|
||||
with:
|
||||
path: 'examples'
|
||||
|
||||
build-release-v4_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-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'
|
||||
27
code/components/esp32-camera-master/.github/workflows/stale.yml
vendored
Normal file
27
code/components/esp32-camera-master/.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
#
|
||||
# You can adjust the behavior by modifying this file.
|
||||
# For more information, see:
|
||||
# https://github.com/actions/stale
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '20 9 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue appears to be stale. Please close it if its no longer valid.'
|
||||
stale-pr-message: 'This pull request appears to be stale. Please close it if its no longer valid.'
|
||||
stale-issue-label: 'no-issue-activity'
|
||||
stale-pr-label: 'no-pr-activity'
|
||||
21
code/components/esp32-camera-master/.github/workflows/upload_component.yml
vendored
Normal file
21
code/components/esp32-camera-master/.github/workflows/upload_component.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Push component to https://components.espressif.com
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
jobs:
|
||||
upload_components:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- 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 }}
|
||||
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||
@@ -1 +1,5 @@
|
||||
*.DS_Store
|
||||
.vscode
|
||||
**/build
|
||||
**/sdkconfig
|
||||
**/sdkconfig.old
|
||||
@@ -1,15 +1,19 @@
|
||||
if(IDF_TARGET STREQUAL "esp32")
|
||||
if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3")
|
||||
set(COMPONENT_SRCS
|
||||
driver/camera.c
|
||||
driver/esp_camera.c
|
||||
driver/cam_hal.c
|
||||
driver/sccb.c
|
||||
driver/sensor.c
|
||||
driver/xclk.c
|
||||
sensors/ov2640.c
|
||||
sensors/ov3660.c
|
||||
sensors/ov5640.c
|
||||
sensors/ov7725.c
|
||||
sensors/ov7670.c
|
||||
sensors/nt99141.c
|
||||
sensors/gc0308.c
|
||||
sensors/gc2145.c
|
||||
sensors/gc032a.c
|
||||
sensors/bf3005.c
|
||||
conversions/yuv.c
|
||||
conversions/to_jpg.cpp
|
||||
conversions/to_bmp.c
|
||||
@@ -26,8 +30,34 @@ if(IDF_TARGET STREQUAL "esp32")
|
||||
driver/private_include
|
||||
sensors/private_include
|
||||
conversions/private_include
|
||||
target/private_include
|
||||
)
|
||||
|
||||
if(IDF_TARGET STREQUAL "esp32")
|
||||
list(APPEND COMPONENT_SRCS
|
||||
target/xclk.c
|
||||
target/esp32/ll_cam.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if(IDF_TARGET STREQUAL "esp32s2")
|
||||
list(APPEND COMPONENT_SRCS
|
||||
target/xclk.c
|
||||
target/esp32s2/ll_cam.c
|
||||
target/esp32s2/tjpgd.c
|
||||
)
|
||||
|
||||
list(APPEND COMPONENT_PRIV_INCLUDEDIRS
|
||||
target/esp32s2/private_include
|
||||
)
|
||||
endif()
|
||||
|
||||
if(IDF_TARGET STREQUAL "esp32s3")
|
||||
list(APPEND COMPONENT_SRCS
|
||||
target/esp32s3/ll_cam.c
|
||||
)
|
||||
endif()
|
||||
|
||||
set(COMPONENT_REQUIRES driver)
|
||||
set(COMPONENT_PRIV_REQUIRES freertos nvs_flash)
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ menu "Camera configuration"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the OV7670.
|
||||
Disable this option to safe memory.
|
||||
Disable this option to save memory.
|
||||
|
||||
config OV7725_SUPPORT
|
||||
bool "Support OV7725 SVGA"
|
||||
default n
|
||||
bool "Support OV7725 VGA"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the OV7725.
|
||||
Disable this option to save memory.
|
||||
@@ -42,6 +42,34 @@ menu "Camera configuration"
|
||||
Enable this option if you want to use the OV5640.
|
||||
Disable this option to save memory.
|
||||
|
||||
config GC2145_SUPPORT
|
||||
bool "Support GC2145 2MP"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the GC2145.
|
||||
Disable this option to save memory.
|
||||
|
||||
config GC032A_SUPPORT
|
||||
bool "Support GC032A VGA"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the GC032A.
|
||||
Disable this option to save memory.
|
||||
|
||||
config GC0308_SUPPORT
|
||||
bool "Support GC0308 VGA"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the GC0308.
|
||||
Disable this option to save memory.
|
||||
|
||||
config BF3005_SUPPORT
|
||||
bool "Support BF3005(BYD3005) VGA"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the BF3005.
|
||||
Disable this option to save memory.
|
||||
|
||||
choice SCCB_HARDWARE_I2C_PORT
|
||||
bool "I2C peripheral to use for SCCB"
|
||||
default SCCB_HARDWARE_I2C_PORT1
|
||||
@@ -53,6 +81,28 @@ menu "Camera configuration"
|
||||
|
||||
endchoice
|
||||
|
||||
config SCCB_CLK_FREQ
|
||||
int "SCCB clk frequency"
|
||||
default 100000
|
||||
range 100000 400000
|
||||
help
|
||||
Increasing this value can reduce the initialization time of the sensor.
|
||||
Please refer to the relevant instructions of the sensor to adjust the value.
|
||||
|
||||
choice GC_SENSOR_WINDOW_MODE
|
||||
bool "GalaxyCore Sensor Window Mode"
|
||||
depends on (GC2145_SUPPORT || GC032A_SUPPORT || GC0308_SUPPORT)
|
||||
default GC_SENSOR_SUBSAMPLE_MODE
|
||||
help
|
||||
This option determines how to reduce the output size when the resolution you set is less than the maximum resolution.
|
||||
SUBSAMPLE_MODE has a bigger perspective and WINDOWING_MODE has a higher frame rate.
|
||||
|
||||
config GC_SENSOR_WINDOWING_MODE
|
||||
bool "Windowing Mode"
|
||||
config GC_SENSOR_SUBSAMPLE_MODE
|
||||
bool "Subsample Mode"
|
||||
endchoice
|
||||
|
||||
choice CAMERA_TASK_PINNED_TO_CORE
|
||||
bool "Camera task pinned to core"
|
||||
default CAMERA_CORE0
|
||||
@@ -68,4 +118,12 @@ menu "Camera configuration"
|
||||
|
||||
endchoice
|
||||
|
||||
config CAMERA_DMA_BUFFER_SIZE_MAX
|
||||
int "DMA buffer size"
|
||||
range 8192 32768
|
||||
default 32768
|
||||
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
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -1,8 +1,30 @@
|
||||
# ESP32 Camera Driver
|
||||
|
||||
[](https://github.com/espressif/esp32-camera/actions/workflows/build.yml)
|
||||
## General Information
|
||||
|
||||
This repository hosts ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors. Additionally it provides a few tools, which allow converting the captured frame data to the more common BMP and JPEG formats.
|
||||
This repository hosts ESP32 series Soc compatible driver for image sensors. Additionally it provides a few tools, which allow converting the captured frame data to the more common BMP and JPEG formats.
|
||||
|
||||
### Supported Soc
|
||||
|
||||
- ESP32
|
||||
- ESP32-S2
|
||||
- ESP32-S3
|
||||
|
||||
### Supported Sensor
|
||||
|
||||
| model | max resolution | color type | output format | Len Size |
|
||||
| ------- | -------------- | ---------- | ------------------------------------------------------------ | -------- |
|
||||
| OV2640 | 1600 x 1200 | color | YUV(422/420)/YCbCr422<br>RGB565/555<br>8-bit compressed data<br>8/10-bit Raw RGB data | 1/4" |
|
||||
| OV3660 | 2048 x 1536 | color | raw RGB data<br/>RGB565/555/444<br/>CCIR656<br/>YCbCr422<br/>compression | 1/5" |
|
||||
| OV5640 | 2592 x 1944 | color | RAW RGB<br/>RGB565/555/444<br/>CCIR656<br/>YUV422/420<br/>YCbCr422<br/>compression | 1/4" |
|
||||
| OV7670 | 640 x 480 | color | Raw Bayer RGB<br/>Processed Bayer RGB<br>YUV/YCbCr422<br>GRB422<br>RGB565/555 | 1/6" |
|
||||
| OV7725 | 640 x 480 | color | Raw RGB<br/>GRB 422<br/>RGB565/555/444<br/>YCbCr 422 | 1/4" |
|
||||
| NT99141 | 1280 x 720 | color | YCbCr 422<br/>RGB565/555/444<br/>Raw<br/>CCIR656<br/>JPEG compression | 1/4" |
|
||||
| GC032A | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/10" |
|
||||
| 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" |
|
||||
|
||||
## Important to Remember
|
||||
|
||||
@@ -17,7 +39,7 @@ This repository hosts ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670
|
||||
### Using esp-idf
|
||||
|
||||
- Clone or download and extract the repository to the components folder of your ESP-IDF project
|
||||
- Enable PSRAM in `menuconfig`
|
||||
- Enable PSRAM in `menuconfig` (also set Flash and PSRAM frequiencies to 80MHz)
|
||||
- Include `esp_camera.h` in your code
|
||||
|
||||
### Using PlatformIO
|
||||
@@ -75,17 +97,6 @@ However with a bit of patience and experimenting you'll figure the Kconfig out.
|
||||
|
||||
If you miss-skip-ignore this critical step the camera module will compile but camera logic inside the library will be 'empty' because the Kconfig sets the proper #ifdef statements during the build process to initialize the selected cameras. It's very not optional!
|
||||
|
||||
### Kconfig options
|
||||
|
||||
| config | description | default |
|
||||
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------ |
|
||||
| CONFIG_OV2640_SUPPORT | Support for OV2640 camera | enabled |
|
||||
| CONFIG_OV7725_SUPPORT | Support for OV7725 camera | disabled |
|
||||
| CONFIG_OV3660_SUPPORT | Support for OV3660 camera | enabled |
|
||||
| CONFIG_OV5640_SUPPORT | Support for OV5640 camera | enabled |
|
||||
| CONFIG_SCCB_HARDWARE_I2C | Enable this option if you want to use hardware I2C to control the camera. Disable this option to use software I2C. | enabled |
|
||||
| CONFIG_SCCB_HARDWARE_I2C_PORT | I2C peripheral to use for SCCB. Can be I2C0 and I2C1. | CONFIG_SCCB_HARDWARE_I2C_PORT1 |
|
||||
| CONFIG_CAMERA_TASK_PINNED_TO_CORE | Pin the camera handle task to a certain core(0/1). It can also be done automatically choosing NO_AFFINITY. Can be CAMERA_CORE0, CAMERA_CORE1 or NO_AFFINITY. | CONFIG_CAMERA_CORE0 |
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -132,8 +143,7 @@ static camera_config_t camera_config = {
|
||||
.pin_href = CAM_PIN_HREF,
|
||||
.pin_pclk = CAM_PIN_PCLK,
|
||||
|
||||
//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
|
||||
.xclk_freq_hz = 20000000,
|
||||
.xclk_freq_hz = 20000000,//EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
|
||||
@@ -141,7 +151,8 @@ static camera_config_t camera_config = {
|
||||
.frame_size = FRAMESIZE_UXGA,//QQVGA-QXGA Do not use sizes above QVGA when not JPEG
|
||||
|
||||
.jpeg_quality = 12, //0-63 lower number means higher quality
|
||||
.fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
.fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
.grab_mode = CAMERA_GRAB_WHEN_EMPTY//CAMERA_GRAB_LATEST. Sets when buffers should be filled
|
||||
};
|
||||
|
||||
esp_err_t camera_init(){
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := driver/include conversions/include
|
||||
COMPONENT_PRIV_INCLUDEDIRS := driver/private_include conversions/private_include sensors/private_include
|
||||
COMPONENT_SRCDIRS := driver conversions sensors
|
||||
COMPONENT_PRIV_INCLUDEDIRS := driver/private_include conversions/private_include sensors/private_include target/private_include
|
||||
COMPONENT_SRCDIRS := driver conversions sensors target target/esp32
|
||||
CXXFLAGS += -fno-rtti
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/rom/tjpgd.h"
|
||||
#else
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "tjpgd.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/tjpgd.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
|
||||
@@ -22,6 +22,7 @@ extern "C" {
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_camera.h"
|
||||
#include "esp_jpg_decode.h"
|
||||
|
||||
typedef size_t (* jpg_out_cb)(void * arg, size_t index, const void* data, size_t len);
|
||||
|
||||
@@ -120,6 +121,8 @@ bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len);
|
||||
*/
|
||||
bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf);
|
||||
|
||||
bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
#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
|
||||
@@ -115,6 +119,54 @@ static bool _rgb_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _rgb565_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data)
|
||||
{
|
||||
rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
|
||||
if(!data){
|
||||
if(x == 0 && y == 0){
|
||||
//write start
|
||||
jpeg->width = w;
|
||||
jpeg->height = h;
|
||||
//if output is null, this is BMP
|
||||
if(!jpeg->output){
|
||||
jpeg->output = (uint8_t *)_malloc((w*h*3)+jpeg->data_offset);
|
||||
if(!jpeg->output){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//write end
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t jw = jpeg->width*3;
|
||||
size_t jw2 = jpeg->width*2;
|
||||
size_t t = y * jw;
|
||||
size_t t2 = y * jw2;
|
||||
size_t b = t + (h * jw);
|
||||
size_t l = x * 2;
|
||||
uint8_t *out = jpeg->output+jpeg->data_offset;
|
||||
uint8_t *o = out;
|
||||
size_t iy, iy2, ix, ix2;
|
||||
|
||||
w = w * 3;
|
||||
|
||||
for(iy=t, iy2=t2; iy<b; iy+=jw, iy2+=jw2) {
|
||||
o = out+iy2+l;
|
||||
for(ix2=ix=0; ix<w; ix+= 3, ix2 +=2) {
|
||||
uint16_t r = data[ix];
|
||||
uint16_t g = data[ix+1];
|
||||
uint16_t b = data[ix+2];
|
||||
uint16_t c = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
|
||||
o[ix2+1] = c>>8;
|
||||
o[ix2] = c&0xff;
|
||||
}
|
||||
data+=w;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//input buffer
|
||||
static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
|
||||
{
|
||||
@@ -140,6 +192,21 @@ static bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, jpg_sc
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale)
|
||||
{
|
||||
rgb_jpg_decoder jpeg;
|
||||
jpeg.width = 0;
|
||||
jpeg.height = 0;
|
||||
jpeg.input = src;
|
||||
jpeg.output = out;
|
||||
jpeg.data_offset = 0;
|
||||
|
||||
if(esp_jpg_decode(src_len, scale, _jpg_read, _rgb565_write, (void*)&jpeg) != ESP_OK){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_len)
|
||||
{
|
||||
|
||||
@@ -317,7 +384,7 @@ bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixf
|
||||
}
|
||||
*out = out_buf;
|
||||
*out_len = out_size;
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len)
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#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
|
||||
@@ -195,7 +199,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
if ((size_t)len > (max_len - index)) {
|
||||
ESP_LOGW(TAG, "JPG output overflow: %d bytes", len - (max_len - index));
|
||||
//ESP_LOGW(TAG, "JPG output overflow: %d bytes (%d,%d,%d)", len - (max_len - index), len, index, max_len);
|
||||
len = max_len - index;
|
||||
}
|
||||
if (len) {
|
||||
@@ -215,7 +219,7 @@ bool fmt2jpg(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixf
|
||||
{
|
||||
//todo: allocate proper buffer for holding JPEG data
|
||||
//this should be enough for CIF frame size
|
||||
int jpg_buf_len = 64*1024;
|
||||
int jpg_buf_len = 128*1024;
|
||||
|
||||
|
||||
uint8_t * jpg_buf = (uint8_t *)_malloc(jpg_buf_len);
|
||||
|
||||
483
code/components/esp32-camera-master/driver/cam_hal.c
Normal file
483
code/components/esp32-camera-master/driver/cam_hal.c
Normal file
@@ -0,0 +1,483 @@
|
||||
// Copyright 2010-2020 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 <string.h>
|
||||
#include "esp_heap_caps.h"
|
||||
#include "ll_cam.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
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
|
||||
static const uint16_t JPEG_EOI_MARKER = 0xD9FF; // written in little-endian for esp32
|
||||
|
||||
static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length)
|
||||
{
|
||||
uint32_t sig = *((uint32_t *)inbuf) & 0xFFFFFF;
|
||||
if(sig != JPEG_SOI_MARKER) {
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
sig = *((uint32_t *)(&inbuf[i])) & 0xFFFFFF;
|
||||
if (sig == JPEG_SOI_MARKER) {
|
||||
ESP_LOGW(TAG, "SOI: %d", i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
ESP_LOGW(TAG, "NO-SOI");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cam_verify_jpeg_eoi(const uint8_t *inbuf, uint32_t length)
|
||||
{
|
||||
int offset = -1;
|
||||
uint8_t *dptr = (uint8_t *)inbuf + length - 2;
|
||||
while (dptr > inbuf) {
|
||||
uint16_t sig = *((uint16_t *)dptr);
|
||||
if (JPEG_EOI_MARKER == sig) {
|
||||
offset = dptr - inbuf;
|
||||
//ESP_LOGW(TAG, "EOI: %d", length - (offset + 2));
|
||||
return offset;
|
||||
}
|
||||
dptr--;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool cam_get_next_frame(int * frame_pos)
|
||||
{
|
||||
if(!cam_obj->frames[*frame_pos].en){
|
||||
for (int x = 0; x < cam_obj->frame_cnt; x++) {
|
||||
if (cam_obj->frames[x].en) {
|
||||
*frame_pos = x;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cam_start_frame(int * frame_pos)
|
||||
{
|
||||
if (cam_get_next_frame(frame_pos)) {
|
||||
if(ll_cam_start(cam_obj, *frame_pos)){
|
||||
// Vsync the frame manually
|
||||
ll_cam_do_vsync(cam_obj);
|
||||
uint64_t us = (uint64_t)esp_timer_get_time();
|
||||
cam_obj->frames[*frame_pos].fb.timestamp.tv_sec = us / 1000000UL;
|
||||
cam_obj->frames[*frame_pos].fb.timestamp.tv_usec = us % 1000000UL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void IRAM_ATTR ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType_t * HPTaskAwoken)
|
||||
{
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
//Copy fram from DMA dma_buffer to fram dma_buffer
|
||||
static void cam_task(void *arg)
|
||||
{
|
||||
int cnt = 0;
|
||||
int frame_pos = 0;
|
||||
cam_obj->state = CAM_STATE_IDLE;
|
||||
cam_event_t cam_event = 0;
|
||||
|
||||
xQueueReset(cam_obj->event_queue);
|
||||
|
||||
while (1) {
|
||||
xQueueReceive(cam_obj->event_queue, (void *)&cam_event, portMAX_DELAY);
|
||||
DBG_PIN_SET(1);
|
||||
switch (cam_obj->state) {
|
||||
|
||||
case CAM_STATE_IDLE: {
|
||||
if (cam_event == CAM_VSYNC_EVENT) {
|
||||
//DBG_PIN_SET(1);
|
||||
if(cam_start_frame(&frame_pos)){
|
||||
cam_obj->frames[frame_pos].fb.len = 0;
|
||||
cam_obj->state = CAM_STATE_READ_BUF;
|
||||
}
|
||||
cnt = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CAM_STATE_READ_BUF: {
|
||||
camera_fb_t * frame_buffer_event = &cam_obj->frames[frame_pos].fb;
|
||||
size_t pixels_per_dma = (cam_obj->dma_half_buffer_size * cam_obj->fb_bytes_per_pixel) / (cam_obj->dma_bytes_per_item * cam_obj->in_bytes_per_pixel);
|
||||
|
||||
if (cam_event == CAM_IN_SUC_EOF_EVENT) {
|
||||
if(!cam_obj->psram_mode){
|
||||
if (cam_obj->fb_size < (frame_buffer_event->len + pixels_per_dma)) {
|
||||
ESP_LOGW(TAG, "FB-OVF");
|
||||
ll_cam_stop(cam_obj);
|
||||
DBG_PIN_SET(0);
|
||||
continue;
|
||||
}
|
||||
frame_buffer_event->len += ll_cam_memcpy(cam_obj,
|
||||
&frame_buffer_event->buf[frame_buffer_event->len],
|
||||
&cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
|
||||
cam_obj->dma_half_buffer_size);
|
||||
}
|
||||
//Check for JPEG SOI in the first buffer. stop if not found
|
||||
if (cam_obj->jpeg_mode && cnt == 0 && cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len) != 0) {
|
||||
ll_cam_stop(cam_obj);
|
||||
cam_obj->state = CAM_STATE_IDLE;
|
||||
}
|
||||
cnt++;
|
||||
|
||||
} else if (cam_event == CAM_VSYNC_EVENT) {
|
||||
//DBG_PIN_SET(1);
|
||||
ll_cam_stop(cam_obj);
|
||||
|
||||
if (cnt || !cam_obj->jpeg_mode || cam_obj->psram_mode) {
|
||||
if (cam_obj->jpeg_mode) {
|
||||
if (!cam_obj->psram_mode) {
|
||||
if (cam_obj->fb_size < (frame_buffer_event->len + pixels_per_dma)) {
|
||||
ESP_LOGW(TAG, "FB-OVF");
|
||||
cnt--;
|
||||
} else {
|
||||
frame_buffer_event->len += ll_cam_memcpy(cam_obj,
|
||||
&frame_buffer_event->buf[frame_buffer_event->len],
|
||||
&cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
|
||||
cam_obj->dma_half_buffer_size);
|
||||
}
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
|
||||
cam_obj->frames[frame_pos].en = 0;
|
||||
|
||||
if (cam_obj->psram_mode) {
|
||||
if (cam_obj->jpeg_mode) {
|
||||
frame_buffer_event->len = cnt * cam_obj->dma_half_buffer_size;
|
||||
} else {
|
||||
frame_buffer_event->len = cam_obj->recv_size;
|
||||
}
|
||||
} else if (!cam_obj->jpeg_mode) {
|
||||
if (frame_buffer_event->len != cam_obj->fb_size) {
|
||||
cam_obj->frames[frame_pos].en = 1;
|
||||
ESP_LOGE(TAG, "FB-SIZE: %u != %u", frame_buffer_event->len, cam_obj->fb_size);
|
||||
}
|
||||
}
|
||||
//send frame
|
||||
if(!cam_obj->frames[frame_pos].en && xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, 0) != pdTRUE) {
|
||||
//pop frame buffer from the queue
|
||||
camera_fb_t * fb2 = NULL;
|
||||
if(xQueueReceive(cam_obj->frame_buffer_queue, &fb2, 0) == pdTRUE) {
|
||||
//push the new frame to the end of the queue
|
||||
if (xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, 0) != pdTRUE) {
|
||||
cam_obj->frames[frame_pos].en = 1;
|
||||
ESP_LOGE(TAG, "FBQ-SND");
|
||||
}
|
||||
//free the popped buffer
|
||||
cam_give(fb2);
|
||||
} else {
|
||||
//queue is full and we could not pop a frame from it
|
||||
cam_obj->frames[frame_pos].en = 1;
|
||||
ESP_LOGE(TAG, "FBQ-RCV");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!cam_start_frame(&frame_pos)){
|
||||
cam_obj->state = CAM_STATE_IDLE;
|
||||
} else {
|
||||
cam_obj->frames[frame_pos].fb.len = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
DBG_PIN_SET(0);
|
||||
}
|
||||
}
|
||||
|
||||
static lldesc_t * allocate_dma_descriptors(uint32_t count, uint16_t size, uint8_t * buffer)
|
||||
{
|
||||
lldesc_t *dma = (lldesc_t *)heap_caps_malloc(count * sizeof(lldesc_t), MALLOC_CAP_DMA);
|
||||
if (dma == NULL) {
|
||||
return dma;
|
||||
}
|
||||
|
||||
for (int x = 0; x < count; x++) {
|
||||
dma[x].size = size;
|
||||
dma[x].length = 0;
|
||||
dma[x].sosf = 0;
|
||||
dma[x].eof = 0;
|
||||
dma[x].owner = 1;
|
||||
dma[x].buf = (buffer + size * x);
|
||||
dma[x].empty = (uint32_t)&dma[(x + 1) % count];
|
||||
}
|
||||
return dma;
|
||||
}
|
||||
|
||||
static esp_err_t cam_dma_config(const camera_config_t *config)
|
||||
{
|
||||
bool ret = ll_cam_dma_sizes(cam_obj);
|
||||
if (0 == ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
cam_obj->dma_node_cnt = (cam_obj->dma_buffer_size) / cam_obj->dma_node_buffer_size; // Number of DMA nodes
|
||||
cam_obj->frame_copy_cnt = cam_obj->recv_size / cam_obj->dma_half_buffer_size; // Number of interrupted copies, ping-pong copy
|
||||
|
||||
ESP_LOGI(TAG, "buffer_size: %d, half_buffer_size: %d, node_buffer_size: %d, node_cnt: %d, total_cnt: %d",
|
||||
cam_obj->dma_buffer_size, cam_obj->dma_half_buffer_size, cam_obj->dma_node_buffer_size, cam_obj->dma_node_cnt, cam_obj->frame_copy_cnt);
|
||||
|
||||
cam_obj->dma_buffer = NULL;
|
||||
cam_obj->dma = NULL;
|
||||
|
||||
cam_obj->frames = (cam_frame_t *)heap_caps_calloc(1, cam_obj->frame_cnt * sizeof(cam_frame_t), MALLOC_CAP_DEFAULT);
|
||||
CAM_CHECK(cam_obj->frames != NULL, "frames malloc failed", ESP_FAIL);
|
||||
|
||||
uint8_t dma_align = 0;
|
||||
size_t fb_size = cam_obj->fb_size;
|
||||
if (cam_obj->psram_mode) {
|
||||
dma_align = ll_cam_get_dma_align(cam_obj);
|
||||
if (cam_obj->fb_size < cam_obj->recv_size) {
|
||||
fb_size = cam_obj->recv_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate memory for frame buffer */
|
||||
size_t alloc_size = fb_size * sizeof(uint8_t) + dma_align;
|
||||
uint32_t _caps = MALLOC_CAP_8BIT;
|
||||
if (CAMERA_FB_IN_DRAM == config->fb_location) {
|
||||
_caps |= MALLOC_CAP_INTERNAL;
|
||||
} else {
|
||||
_caps |= MALLOC_CAP_SPIRAM;
|
||||
}
|
||||
for (int x = 0; x < cam_obj->frame_cnt; x++) {
|
||||
cam_obj->frames[x].dma = NULL;
|
||||
cam_obj->frames[x].fb_offset = 0;
|
||||
cam_obj->frames[x].en = 0;
|
||||
ESP_LOGI(TAG, "Allocating %d Byte frame buffer in %s", alloc_size, _caps & MALLOC_CAP_SPIRAM ? "PSRAM" : "OnBoard RAM");
|
||||
cam_obj->frames[x].fb.buf = (uint8_t *)heap_caps_malloc(alloc_size, _caps);
|
||||
CAM_CHECK(cam_obj->frames[x].fb.buf != NULL, "frame buffer malloc failed", ESP_FAIL);
|
||||
if (cam_obj->psram_mode) {
|
||||
//align PSRAM buffer. TODO: save the offset so proper address can be freed later
|
||||
cam_obj->frames[x].fb_offset = dma_align - ((uint32_t)cam_obj->frames[x].fb.buf & (dma_align - 1));
|
||||
cam_obj->frames[x].fb.buf += cam_obj->frames[x].fb_offset;
|
||||
ESP_LOGI(TAG, "Frame[%d]: Offset: %u, Addr: 0x%08X", x, cam_obj->frames[x].fb_offset, (uint32_t)cam_obj->frames[x].fb.buf);
|
||||
cam_obj->frames[x].dma = allocate_dma_descriptors(cam_obj->dma_node_cnt, cam_obj->dma_node_buffer_size, cam_obj->frames[x].fb.buf);
|
||||
CAM_CHECK(cam_obj->frames[x].dma != NULL, "frame dma malloc failed", ESP_FAIL);
|
||||
}
|
||||
cam_obj->frames[x].en = 1;
|
||||
}
|
||||
|
||||
if (!cam_obj->psram_mode) {
|
||||
cam_obj->dma_buffer = (uint8_t *)heap_caps_malloc(cam_obj->dma_buffer_size * sizeof(uint8_t), MALLOC_CAP_DMA);
|
||||
if(NULL == cam_obj->dma_buffer) {
|
||||
ESP_LOGE(TAG,"%s(%d): DMA buffer %d Byte malloc failed, the current largest free block:%d Byte", __FUNCTION__, __LINE__,
|
||||
cam_obj->dma_buffer_size, heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
cam_obj->dma = allocate_dma_descriptors(cam_obj->dma_node_cnt, cam_obj->dma_node_buffer_size, cam_obj->dma_buffer);
|
||||
CAM_CHECK(cam_obj->dma != NULL, "dma malloc failed", ESP_FAIL);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t cam_init(const camera_config_t *config)
|
||||
{
|
||||
CAM_CHECK(NULL != config, "config pointer is invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
cam_obj = (cam_obj_t *)heap_caps_calloc(1, sizeof(cam_obj_t), MALLOC_CAP_DMA);
|
||||
CAM_CHECK(NULL != cam_obj, "lcd_cam object malloc error", ESP_ERR_NO_MEM);
|
||||
|
||||
cam_obj->swap_data = 0;
|
||||
cam_obj->vsync_pin = config->pin_vsync;
|
||||
cam_obj->vsync_invert = true;
|
||||
|
||||
ll_cam_set_pin(cam_obj, config);
|
||||
ret = ll_cam_config(cam_obj, config);
|
||||
CAM_CHECK_GOTO(ret == ESP_OK, "ll_cam initialize failed", err);
|
||||
|
||||
#if CAMERA_DBG_PIN_ENABLE
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[DBG_PIN_NUM], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(DBG_PIN_NUM, GPIO_MODE_OUTPUT);
|
||||
gpio_set_pull_mode(DBG_PIN_NUM, GPIO_FLOATING);
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "cam init ok");
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
free(cam_obj);
|
||||
cam_obj = NULL;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint16_t sensor_pid)
|
||||
{
|
||||
CAM_CHECK(NULL != config, "config pointer is invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
ret = ll_cam_set_sample_mode(cam_obj, (pixformat_t)config->pixel_format, config->xclk_freq_hz, sensor_pid);
|
||||
|
||||
cam_obj->jpeg_mode = config->pixel_format == PIXFORMAT_JPEG;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
cam_obj->psram_mode = false;
|
||||
#else
|
||||
cam_obj->psram_mode = (config->xclk_freq_hz == 16000000);
|
||||
#endif
|
||||
cam_obj->frame_cnt = config->fb_count;
|
||||
cam_obj->width = resolution[frame_size].width;
|
||||
cam_obj->height = resolution[frame_size].height;
|
||||
|
||||
if(cam_obj->jpeg_mode){
|
||||
cam_obj->recv_size = cam_obj->width * cam_obj->height / 5;
|
||||
cam_obj->fb_size = cam_obj->recv_size;
|
||||
} else {
|
||||
cam_obj->recv_size = cam_obj->width * cam_obj->height * cam_obj->in_bytes_per_pixel;
|
||||
cam_obj->fb_size = cam_obj->width * cam_obj->height * cam_obj->fb_bytes_per_pixel;
|
||||
}
|
||||
|
||||
ret = cam_dma_config(config);
|
||||
CAM_CHECK_GOTO(ret == ESP_OK, "cam_dma_config failed", err);
|
||||
|
||||
cam_obj->event_queue = xQueueCreate(cam_obj->dma_half_buffer_cnt - 1, sizeof(cam_event_t));
|
||||
CAM_CHECK_GOTO(cam_obj->event_queue != NULL, "event_queue create failed", err);
|
||||
|
||||
size_t frame_buffer_queue_len = cam_obj->frame_cnt;
|
||||
if (config->grab_mode == CAMERA_GRAB_LATEST && cam_obj->frame_cnt > 1) {
|
||||
frame_buffer_queue_len = cam_obj->frame_cnt - 1;
|
||||
}
|
||||
cam_obj->frame_buffer_queue = xQueueCreate(frame_buffer_queue_len, sizeof(camera_fb_t*));
|
||||
CAM_CHECK_GOTO(cam_obj->frame_buffer_queue != NULL, "frame_buffer_queue create failed", err);
|
||||
|
||||
ret = ll_cam_init_isr(cam_obj);
|
||||
CAM_CHECK_GOTO(ret == ESP_OK, "cam intr alloc failed", err);
|
||||
|
||||
|
||||
#if CONFIG_CAMERA_CORE0
|
||||
xTaskCreatePinnedToCore(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 0);
|
||||
#elif CONFIG_CAMERA_CORE1
|
||||
xTaskCreatePinnedToCore(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 1);
|
||||
#else
|
||||
xTaskCreate(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle);
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "cam config ok");
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
cam_deinit();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t cam_deinit(void)
|
||||
{
|
||||
if (!cam_obj) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
cam_stop();
|
||||
if (cam_obj->task_handle) {
|
||||
vTaskDelete(cam_obj->task_handle);
|
||||
}
|
||||
if (cam_obj->event_queue) {
|
||||
vQueueDelete(cam_obj->event_queue);
|
||||
}
|
||||
if (cam_obj->frame_buffer_queue) {
|
||||
vQueueDelete(cam_obj->frame_buffer_queue);
|
||||
}
|
||||
if (cam_obj->dma) {
|
||||
free(cam_obj->dma);
|
||||
}
|
||||
if (cam_obj->dma_buffer) {
|
||||
free(cam_obj->dma_buffer);
|
||||
}
|
||||
if (cam_obj->frames) {
|
||||
for (int x = 0; x < cam_obj->frame_cnt; x++) {
|
||||
free(cam_obj->frames[x].fb.buf - cam_obj->frames[x].fb_offset);
|
||||
if (cam_obj->frames[x].dma) {
|
||||
free(cam_obj->frames[x].dma);
|
||||
}
|
||||
}
|
||||
free(cam_obj->frames);
|
||||
}
|
||||
|
||||
ll_cam_deinit(cam_obj);
|
||||
|
||||
free(cam_obj);
|
||||
cam_obj = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void cam_stop(void)
|
||||
{
|
||||
ll_cam_vsync_intr_enable(cam_obj, false);
|
||||
ll_cam_stop(cam_obj);
|
||||
}
|
||||
|
||||
void cam_start(void)
|
||||
{
|
||||
ll_cam_vsync_intr_enable(cam_obj, true);
|
||||
}
|
||||
|
||||
camera_fb_t *cam_take(TickType_t timeout)
|
||||
{
|
||||
camera_fb_t *dma_buffer = NULL;
|
||||
TickType_t start = xTaskGetTickCount();
|
||||
xQueueReceive(cam_obj->frame_buffer_queue, (void *)&dma_buffer, timeout);
|
||||
if (dma_buffer) {
|
||||
if(cam_obj->jpeg_mode){
|
||||
// find the end marker for JPEG. Data after that can be discarded
|
||||
int offset_e = cam_verify_jpeg_eoi(dma_buffer->buf, dma_buffer->len);
|
||||
if (offset_e >= 0) {
|
||||
// adjust buffer length
|
||||
dma_buffer->len = offset_e + sizeof(JPEG_EOI_MARKER);
|
||||
return dma_buffer;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "NO-EOI");
|
||||
cam_give(dma_buffer);
|
||||
return cam_take(timeout - (xTaskGetTickCount() - start));//recurse!!!!
|
||||
}
|
||||
} else if(cam_obj->psram_mode && cam_obj->in_bytes_per_pixel != cam_obj->fb_bytes_per_pixel){
|
||||
//currently this is used only for YUV to GRAYSCALE
|
||||
dma_buffer->len = ll_cam_memcpy(cam_obj, dma_buffer->buf, dma_buffer->buf, dma_buffer->len);
|
||||
}
|
||||
return dma_buffer;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Failed to get the frame on time!");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cam_give(camera_fb_t *dma_buffer)
|
||||
{
|
||||
for (int x = 0; x < cam_obj->frame_cnt; x++) {
|
||||
if (&cam_obj->frames[x].fb == dma_buffer) {
|
||||
cam_obj->frames[x].en = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
421
code/components/esp32-camera-master/driver/esp_camera.c
Normal file
421
code/components/esp32-camera-master/driver/esp_camera.c
Normal file
@@ -0,0 +1,421 @@
|
||||
// Copyright 2015-2016 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "time.h"
|
||||
#include "sys/time.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include "sensor.h"
|
||||
#include "sccb.h"
|
||||
#include "cam_hal.h"
|
||||
#include "esp_camera.h"
|
||||
#include "xclk.h"
|
||||
#if CONFIG_OV2640_SUPPORT
|
||||
#include "ov2640.h"
|
||||
#endif
|
||||
#if CONFIG_OV7725_SUPPORT
|
||||
#include "ov7725.h"
|
||||
#endif
|
||||
#if CONFIG_OV3660_SUPPORT
|
||||
#include "ov3660.h"
|
||||
#endif
|
||||
#if CONFIG_OV5640_SUPPORT
|
||||
#include "ov5640.h"
|
||||
#endif
|
||||
#if CONFIG_NT99141_SUPPORT
|
||||
#include "nt99141.h"
|
||||
#endif
|
||||
#if CONFIG_OV7670_SUPPORT
|
||||
#include "ov7670.h"
|
||||
#endif
|
||||
#if CONFIG_GC2145_SUPPORT
|
||||
#include "gc2145.h"
|
||||
#endif
|
||||
#if CONFIG_GC032A_SUPPORT
|
||||
#include "gc032a.h"
|
||||
#endif
|
||||
#if CONFIG_GC0308_SUPPORT
|
||||
#include "gc0308.h"
|
||||
#endif
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
#include "bf3005.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "camera";
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
sensor_t sensor;
|
||||
camera_fb_t fb;
|
||||
} camera_state_t;
|
||||
|
||||
static const char *CAMERA_SENSOR_NVS_KEY = "sensor";
|
||||
static const char *CAMERA_PIXFORMAT_NVS_KEY = "pixformat";
|
||||
static camera_state_t *s_state = NULL;
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 // LCD_CAM module of ESP32-S3 will generate xclk
|
||||
#define CAMERA_ENABLE_OUT_CLOCK(v)
|
||||
#define CAMERA_DISABLE_OUT_CLOCK()
|
||||
#else
|
||||
#define CAMERA_ENABLE_OUT_CLOCK(v) camera_enable_out_clock((v))
|
||||
#define CAMERA_DISABLE_OUT_CLOCK() camera_disable_out_clock()
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int (*detect)(int slv_addr, sensor_id_t *id);
|
||||
int (*init)(sensor_t *sensor);
|
||||
} sensor_func_t;
|
||||
|
||||
static const sensor_func_t g_sensors[] = {
|
||||
#if CONFIG_OV7725_SUPPORT
|
||||
{ov7725_detect, ov7725_init},
|
||||
#endif
|
||||
#if CONFIG_OV7670_SUPPORT
|
||||
{ov7670_detect, ov7670_init},
|
||||
#endif
|
||||
#if CONFIG_OV2640_SUPPORT
|
||||
{ov2640_detect, ov2640_init},
|
||||
#endif
|
||||
#if CONFIG_OV3660_SUPPORT
|
||||
{ov3660_detect, ov3660_init},
|
||||
#endif
|
||||
#if CONFIG_OV5640_SUPPORT
|
||||
{ov5640_detect, ov5640_init},
|
||||
#endif
|
||||
#if CONFIG_NT99141_SUPPORT
|
||||
{nt99141_detect, nt99141_init},
|
||||
#endif
|
||||
#if CONFIG_GC2145_SUPPORT
|
||||
{gc2145_detect, gc2145_init},
|
||||
#endif
|
||||
#if CONFIG_GC032A_SUPPORT
|
||||
{gc032a_detect, gc032a_init},
|
||||
#endif
|
||||
#if CONFIG_GC0308_SUPPORT
|
||||
{gc0308_detect, gc0308_init},
|
||||
#endif
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
{bf3005_detect, bf3005_init},
|
||||
#endif
|
||||
};
|
||||
|
||||
static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
|
||||
{
|
||||
*out_camera_model = CAMERA_NONE;
|
||||
if (s_state != NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
s_state = (camera_state_t *) calloc(sizeof(camera_state_t), 1);
|
||||
if (!s_state) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (config->pin_xclk >= 0) {
|
||||
ESP_LOGD(TAG, "Enabling XCLK output");
|
||||
CAMERA_ENABLE_OUT_CLOCK(config);
|
||||
}
|
||||
|
||||
if (config->pin_sscb_sda != -1) {
|
||||
ESP_LOGD(TAG, "Initializing SSCB");
|
||||
SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl);
|
||||
}
|
||||
|
||||
if (config->pin_pwdn >= 0) {
|
||||
ESP_LOGD(TAG, "Resetting camera by power down line");
|
||||
gpio_config_t conf = { 0 };
|
||||
conf.pin_bit_mask = 1LL << config->pin_pwdn;
|
||||
conf.mode = GPIO_MODE_OUTPUT;
|
||||
gpio_config(&conf);
|
||||
|
||||
// carefull, logic is inverted compared to reset pin
|
||||
gpio_set_level(config->pin_pwdn, 1);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(config->pin_pwdn, 0);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
if (config->pin_reset >= 0) {
|
||||
ESP_LOGD(TAG, "Resetting camera");
|
||||
gpio_config_t conf = { 0 };
|
||||
conf.pin_bit_mask = 1LL << config->pin_reset;
|
||||
conf.mode = GPIO_MODE_OUTPUT;
|
||||
gpio_config(&conf);
|
||||
|
||||
gpio_set_level(config->pin_reset, 0);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(config->pin_reset, 1);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
|
||||
ESP_LOGD(TAG, "Searching for camera address");
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
uint8_t slv_addr = SCCB_Probe();
|
||||
|
||||
if (slv_addr == 0) {
|
||||
CAMERA_DISABLE_OUT_CLOCK();
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr);
|
||||
s_state->sensor.slv_addr = slv_addr;
|
||||
s_state->sensor.xclk_freq_hz = config->xclk_freq_hz;
|
||||
|
||||
/**
|
||||
* Read sensor ID and then initialize sensor
|
||||
* Attention: Some sensors have the same SCCB address. Therefore, several attempts may be made in the detection process
|
||||
*/
|
||||
sensor_id_t *id = &s_state->sensor.id;
|
||||
for (size_t i = 0; i < sizeof(g_sensors) / sizeof(sensor_func_t); i++) {
|
||||
if (g_sensors[i].detect(slv_addr, id)) {
|
||||
camera_sensor_info_t *info = esp_camera_sensor_get_info(id);
|
||||
if (NULL != info) {
|
||||
*out_camera_model = info->model;
|
||||
ESP_LOGI(TAG, "Detected %s camera", info->name);
|
||||
g_sensors[i].init(&s_state->sensor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CAMERA_NONE == *out_camera_model) { //If no supported sensors are detected
|
||||
CAMERA_DISABLE_OUT_CLOCK();
|
||||
ESP_LOGE(TAG, "Detected camera not supported.");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x",
|
||||
id->PID, id->VER, id->MIDH, id->MIDL);
|
||||
|
||||
ESP_LOGD(TAG, "Doing SW reset of sensor");
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
s_state->sensor.reset(&s_state->sensor);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_camera_init(const camera_config_t *config)
|
||||
{
|
||||
esp_err_t err;
|
||||
err = cam_init(config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
camera_model_t camera_model = CAMERA_NONE;
|
||||
err = camera_probe(config, &camera_model);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Camera probe failed with error 0x%x(%s)", err, esp_err_to_name(err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
framesize_t frame_size = (framesize_t) config->frame_size;
|
||||
pixformat_t pix_format = (pixformat_t) config->pixel_format;
|
||||
|
||||
if (PIXFORMAT_JPEG == pix_format && (!camera_sensor[camera_model].support_jpeg)) {
|
||||
ESP_LOGE(TAG, "JPEG format is not supported on this sensor");
|
||||
err = ESP_ERR_NOT_SUPPORTED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (frame_size > camera_sensor[camera_model].max_size) {
|
||||
ESP_LOGW(TAG, "The frame size exceeds the maximum for this sensor, it will be forced to the maximum possible value");
|
||||
frame_size = camera_sensor[camera_model].max_size;
|
||||
}
|
||||
|
||||
err = cam_config(config, frame_size, s_state->sensor.id.PID);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Camera config failed with error 0x%x", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
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");
|
||||
err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE;
|
||||
goto fail;
|
||||
}
|
||||
s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
|
||||
|
||||
if (s_state->sensor.id.PID == OV2640_PID) {
|
||||
s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);
|
||||
s_state->sensor.set_bpc(&s_state->sensor, false);
|
||||
s_state->sensor.set_wpc(&s_state->sensor, true);
|
||||
s_state->sensor.set_lenc(&s_state->sensor, true);
|
||||
}
|
||||
|
||||
if (pix_format == PIXFORMAT_JPEG) {
|
||||
s_state->sensor.set_quality(&s_state->sensor, config->jpeg_quality);
|
||||
}
|
||||
s_state->sensor.init_status(&s_state->sensor);
|
||||
|
||||
cam_start();
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
esp_camera_deinit();
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_camera_deinit()
|
||||
{
|
||||
esp_err_t ret = cam_deinit();
|
||||
CAMERA_DISABLE_OUT_CLOCK();
|
||||
if (s_state) {
|
||||
SCCB_Deinit();
|
||||
|
||||
free(s_state);
|
||||
s_state = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define FB_GET_TIMEOUT (4000 / portTICK_PERIOD_MS)
|
||||
|
||||
camera_fb_t *esp_camera_fb_get()
|
||||
{
|
||||
if (s_state == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
camera_fb_t *fb = cam_take(FB_GET_TIMEOUT);
|
||||
//set the frame properties
|
||||
if (fb) {
|
||||
fb->width = resolution[s_state->sensor.status.framesize].width;
|
||||
fb->height = resolution[s_state->sensor.status.framesize].height;
|
||||
fb->format = s_state->sensor.pixformat;
|
||||
}
|
||||
return fb;
|
||||
}
|
||||
|
||||
void esp_camera_fb_return(camera_fb_t *fb)
|
||||
{
|
||||
if (s_state == NULL) {
|
||||
return;
|
||||
}
|
||||
cam_give(fb);
|
||||
}
|
||||
|
||||
sensor_t *esp_camera_sensor_get()
|
||||
{
|
||||
if (s_state == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return &s_state->sensor;
|
||||
}
|
||||
|
||||
esp_err_t esp_camera_save_to_nvs(const char *key)
|
||||
{
|
||||
#if ESP_IDF_VERSION_MAJOR > 3
|
||||
nvs_handle_t handle;
|
||||
#else
|
||||
nvs_handle handle;
|
||||
#endif
|
||||
esp_err_t ret = nvs_open(key, NVS_READWRITE, &handle);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
if (s != NULL) {
|
||||
ret = nvs_set_blob(handle, CAMERA_SENSOR_NVS_KEY, &s->status, sizeof(camera_status_t));
|
||||
if (ret == ESP_OK) {
|
||||
uint8_t pf = s->pixformat;
|
||||
ret = nvs_set_u8(handle, CAMERA_PIXFORMAT_NVS_KEY, pf);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return ESP_ERR_CAMERA_NOT_DETECTED;
|
||||
}
|
||||
nvs_close(handle);
|
||||
return ret;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_camera_load_from_nvs(const char *key)
|
||||
{
|
||||
#if ESP_IDF_VERSION_MAJOR > 3
|
||||
nvs_handle_t handle;
|
||||
#else
|
||||
nvs_handle handle;
|
||||
#endif
|
||||
uint8_t pf;
|
||||
|
||||
esp_err_t ret = nvs_open(key, NVS_READWRITE, &handle);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
camera_status_t st;
|
||||
if (s != NULL) {
|
||||
size_t size = sizeof(camera_status_t);
|
||||
ret = nvs_get_blob(handle, CAMERA_SENSOR_NVS_KEY, &st, &size);
|
||||
if (ret == ESP_OK) {
|
||||
s->set_ae_level(s, st.ae_level);
|
||||
s->set_aec2(s, st.aec2);
|
||||
s->set_aec_value(s, st.aec_value);
|
||||
s->set_agc_gain(s, st.agc_gain);
|
||||
s->set_awb_gain(s, st.awb_gain);
|
||||
s->set_bpc(s, st.bpc);
|
||||
s->set_brightness(s, st.brightness);
|
||||
s->set_colorbar(s, st.colorbar);
|
||||
s->set_contrast(s, st.contrast);
|
||||
s->set_dcw(s, st.dcw);
|
||||
s->set_denoise(s, st.denoise);
|
||||
s->set_exposure_ctrl(s, st.aec);
|
||||
s->set_framesize(s, st.framesize);
|
||||
s->set_gain_ctrl(s, st.agc);
|
||||
s->set_gainceiling(s, st.gainceiling);
|
||||
s->set_hmirror(s, st.hmirror);
|
||||
s->set_lenc(s, st.lenc);
|
||||
s->set_quality(s, st.quality);
|
||||
s->set_raw_gma(s, st.raw_gma);
|
||||
s->set_saturation(s, st.saturation);
|
||||
s->set_sharpness(s, st.sharpness);
|
||||
s->set_special_effect(s, st.special_effect);
|
||||
s->set_vflip(s, st.vflip);
|
||||
s->set_wb_mode(s, st.wb_mode);
|
||||
s->set_whitebal(s, st.awb);
|
||||
s->set_wpc(s, st.wpc);
|
||||
}
|
||||
ret = nvs_get_u8(handle, CAMERA_PIXFORMAT_NVS_KEY, &pf);
|
||||
if (ret == ESP_OK) {
|
||||
s->set_pixformat(s, pf);
|
||||
}
|
||||
} else {
|
||||
return ESP_ERR_CAMERA_NOT_DETECTED;
|
||||
}
|
||||
nvs_close(handle);
|
||||
return ret;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Error (%d) opening nvs key \"%s\"", ret, key);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,8 @@
|
||||
.pixel_format = PIXFORMAT_JPEG,
|
||||
.frame_size = FRAMESIZE_SVGA,
|
||||
.jpeg_quality = 10,
|
||||
.fb_count = 2
|
||||
.fb_count = 2,
|
||||
.grab_mode = CAMERA_GRAB_WHEN_EMPTY
|
||||
};
|
||||
|
||||
esp_err_t camera_example_init(){
|
||||
@@ -74,6 +75,22 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for camera initialization
|
||||
*/
|
||||
typedef enum {
|
||||
CAMERA_GRAB_WHEN_EMPTY, /*!< Fills buffers when they are empty. Less resources but first 'fb_count' frames might be old */
|
||||
CAMERA_GRAB_LATEST /*!< Except when 1 frame buffer is used, queue will always contain the last 'fb_count' frames */
|
||||
} camera_grab_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Camera frame buffer location
|
||||
*/
|
||||
typedef enum {
|
||||
CAMERA_FB_IN_PSRAM, /*!< Frame buffer is placed in external PSRAM */
|
||||
CAMERA_FB_IN_DRAM /*!< Frame buffer is placed in internal DRAM */
|
||||
} camera_fb_location_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for camera initialization
|
||||
*/
|
||||
@@ -95,7 +112,7 @@ typedef struct {
|
||||
int pin_href; /*!< GPIO pin for camera HREF line */
|
||||
int pin_pclk; /*!< GPIO pin for camera PCLK line */
|
||||
|
||||
int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental) */
|
||||
int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode */
|
||||
|
||||
ledc_timer_t ledc_timer; /*!< LEDC timer to be used for generating XCLK */
|
||||
ledc_channel_t ledc_channel; /*!< LEDC channel to be used for generating XCLK */
|
||||
@@ -105,6 +122,8 @@ typedef struct {
|
||||
|
||||
int jpeg_quality; /*!< Quality of JPEG output. 0-63 lower means higher quality */
|
||||
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 */
|
||||
} camera_config_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,13 +11,51 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define NT99141_PID (0x14)
|
||||
#define OV9650_PID (0x96)
|
||||
#define OV7725_PID (0x77)
|
||||
#define OV2640_PID (0x26)
|
||||
#define OV3660_PID (0x36)
|
||||
#define OV5640_PID (0x56)
|
||||
#define OV7670_PID (0x76)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
OV9650_PID = 0x96,
|
||||
OV7725_PID = 0x77,
|
||||
OV2640_PID = 0x26,
|
||||
OV3660_PID = 0x3660,
|
||||
OV5640_PID = 0x5640,
|
||||
OV7670_PID = 0x76,
|
||||
NT99141_PID = 0x1410,
|
||||
GC2145_PID = 0x2145,
|
||||
GC032A_PID = 0x232a,
|
||||
GC0308_PID = 0x9b,
|
||||
BF3005_PID = 0x30,
|
||||
} camera_pid_t;
|
||||
|
||||
typedef enum {
|
||||
CAMERA_OV7725,
|
||||
CAMERA_OV2640,
|
||||
CAMERA_OV3660,
|
||||
CAMERA_OV5640,
|
||||
CAMERA_OV7670,
|
||||
CAMERA_NT99141,
|
||||
CAMERA_GC2145,
|
||||
CAMERA_GC032A,
|
||||
CAMERA_GC0308,
|
||||
CAMERA_BF3005,
|
||||
CAMERA_MODEL_MAX,
|
||||
CAMERA_NONE,
|
||||
} camera_model_t;
|
||||
|
||||
typedef enum {
|
||||
OV2640_SCCB_ADDR = 0x30,// 0x60 >> 1
|
||||
OV5640_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
OV3660_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
OV7725_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
OV7670_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
NT99141_SCCB_ADDR = 0x2A,// 0x54 >> 1
|
||||
GC2145_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
BF3005_SCCB_ADDR = 0x6E,
|
||||
} camera_sccb_addr_t;
|
||||
|
||||
typedef enum {
|
||||
PIXFORMAT_RGB565, // 2BPP/RGB565
|
||||
@@ -58,6 +96,15 @@ typedef enum {
|
||||
FRAMESIZE_INVALID
|
||||
} framesize_t;
|
||||
|
||||
typedef struct {
|
||||
const camera_model_t model;
|
||||
const char *name;
|
||||
const camera_sccb_addr_t sccb_addr;
|
||||
const camera_pid_t pid;
|
||||
const framesize_t max_size;
|
||||
const bool support_jpeg;
|
||||
} camera_sensor_info_t;
|
||||
|
||||
typedef enum {
|
||||
ASPECT_RATIO_4X3,
|
||||
ASPECT_RATIO_3X2,
|
||||
@@ -101,11 +148,13 @@ typedef struct {
|
||||
|
||||
// Resolution table (in sensor.c)
|
||||
extern const resolution_info_t resolution[];
|
||||
// camera sensor table (in sensor.c)
|
||||
extern const camera_sensor_info_t camera_sensor[];
|
||||
|
||||
typedef struct {
|
||||
uint8_t MIDH;
|
||||
uint8_t MIDL;
|
||||
uint8_t PID;
|
||||
uint16_t PID;
|
||||
uint8_t VER;
|
||||
} sensor_id_t;
|
||||
|
||||
@@ -190,4 +239,10 @@ typedef struct _sensor {
|
||||
int (*set_xclk) (sensor_t *sensor, int timer, int xclk);
|
||||
} sensor_t;
|
||||
|
||||
camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SENSOR_H__ */
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2010-2020 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 "esp_camera.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Uninitialize the lcd_cam module
|
||||
*
|
||||
* @param handle Provide handle pointer to release resources
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Uninitialize fail
|
||||
*/
|
||||
esp_err_t cam_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize the lcd_cam module
|
||||
*
|
||||
* @param config Configurations - see lcd_cam_config_t struct
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_ERR_NO_MEM No memory to initialize lcd_cam
|
||||
* - ESP_FAIL Initialize fail
|
||||
*/
|
||||
esp_err_t cam_init(const camera_config_t *config);
|
||||
|
||||
esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint16_t sensor_pid);
|
||||
|
||||
void cam_stop(void);
|
||||
|
||||
void cam_start(void);
|
||||
|
||||
camera_fb_t *cam_take(TickType_t timeout);
|
||||
|
||||
void cam_give(camera_fb_t *dma_buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_camera.h"
|
||||
#include "sensor.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/rom/lldesc.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "rom/lldesc.h"
|
||||
#endif
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t sample2;
|
||||
uint8_t unused2;
|
||||
uint8_t sample1;
|
||||
uint8_t unused1;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma_elem_t;
|
||||
|
||||
typedef enum {
|
||||
/* camera sends byte sequence: s1, s2, s3, s4, ...
|
||||
* fifo receives: 00 s1 00 s2, 00 s2 00 s3, 00 s3 00 s4, ...
|
||||
*/
|
||||
SM_0A0B_0B0C = 0,
|
||||
/* camera sends byte sequence: s1, s2, s3, s4, ...
|
||||
* fifo receives: 00 s1 00 s2, 00 s3 00 s4, ...
|
||||
*/
|
||||
SM_0A0B_0C0D = 1,
|
||||
/* camera sends byte sequence: s1, s2, s3, s4, ...
|
||||
* fifo receives: 00 s1 00 00, 00 s2 00 00, 00 s3 00 00, ...
|
||||
*/
|
||||
SM_0A00_0B00 = 3,
|
||||
} i2s_sampling_mode_t;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#define __SCCB_H__
|
||||
#include <stdint.h>
|
||||
int SCCB_Init(int pin_sda, int pin_scl);
|
||||
int SCCB_Deinit(void);
|
||||
uint8_t SCCB_Probe();
|
||||
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg);
|
||||
uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "camera_common.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
|
||||
|
||||
esp_err_t camera_enable_out_clock();
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include "sccb.h"
|
||||
#include "sensor.h"
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
@@ -24,24 +25,22 @@ static const char* TAG = "sccb";
|
||||
|
||||
#include "driver/i2c.h"
|
||||
|
||||
#define SCCB_FREQ 100000 /*!< I2C master frequency*/
|
||||
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||
#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 */
|
||||
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||
#if CONFIG_SCCB_HARDWARE_I2C_PORT1
|
||||
const int SCCB_I2C_PORT = 1;
|
||||
#else
|
||||
const int SCCB_I2C_PORT = 0;
|
||||
#endif
|
||||
static uint8_t ESP_SLAVE_ADDR = 0x3c;
|
||||
|
||||
int SCCB_Init(int pin_sda, int pin_scl)
|
||||
{
|
||||
ESP_LOGI(TAG, "pin_sda %d pin_scl %d\n", pin_sda, pin_scl);
|
||||
//log_i("SCCB_Init start");
|
||||
ESP_LOGI(TAG, "pin_sda %d pin_scl %d", pin_sda, pin_scl);
|
||||
i2c_config_t conf;
|
||||
memset(&conf, 0, sizeof(i2c_config_t));
|
||||
conf.mode = I2C_MODE_MASTER;
|
||||
@@ -56,10 +55,30 @@ int SCCB_Init(int pin_sda, int pin_scl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t SCCB_Probe()
|
||||
int SCCB_Deinit(void)
|
||||
{
|
||||
return i2c_driver_delete(SCCB_I2C_PORT);
|
||||
}
|
||||
|
||||
uint8_t SCCB_Probe(void)
|
||||
{
|
||||
uint8_t slave_addr = 0x0;
|
||||
while(slave_addr < 0x7f) {
|
||||
// for (size_t i = 1; i < 0x80; i++) {
|
||||
// i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
// i2c_master_start(cmd);
|
||||
// i2c_master_write_byte(cmd, ( i << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||
// i2c_master_stop(cmd);
|
||||
// esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||
// i2c_cmd_link_delete(cmd);
|
||||
// if( ret == ESP_OK) {
|
||||
// ESP_LOGW(TAG, "Found I2C Device at 0x%02X", i);
|
||||
// }
|
||||
// }
|
||||
for (size_t i = 0; i < CAMERA_MODEL_MAX; i++) {
|
||||
if (slave_addr == camera_sensor[i].sccb_addr) {
|
||||
continue;
|
||||
}
|
||||
slave_addr = camera_sensor[i].sccb_addr;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, ( slave_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||
@@ -67,12 +86,10 @@ uint8_t SCCB_Probe()
|
||||
esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if( ret == ESP_OK) {
|
||||
ESP_SLAVE_ADDR = slave_addr;
|
||||
return ESP_SLAVE_ADDR;
|
||||
return slave_addr;
|
||||
}
|
||||
slave_addr++;
|
||||
}
|
||||
return ESP_SLAVE_ADDR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
#include <stdio.h>
|
||||
#include "sensor.h"
|
||||
|
||||
const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
|
||||
// The sequence must be consistent with camera_model_t
|
||||
{CAMERA_OV7725, "OV7725", OV7725_SCCB_ADDR, OV7725_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_OV2640, "OV2640", OV2640_SCCB_ADDR, OV2640_PID, FRAMESIZE_UXGA, true},
|
||||
{CAMERA_OV3660, "OV3660", OV3660_SCCB_ADDR, OV3660_PID, FRAMESIZE_QXGA, true},
|
||||
{CAMERA_OV5640, "OV5640", OV5640_SCCB_ADDR, OV5640_PID, FRAMESIZE_QSXGA, true},
|
||||
{CAMERA_OV7670, "OV7670", OV7670_SCCB_ADDR, OV7670_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_NT99141, "NT99141", NT99141_SCCB_ADDR, NT99141_PID, FRAMESIZE_HD, true},
|
||||
{CAMERA_GC2145, "GC2145", GC2145_SCCB_ADDR, GC2145_PID, FRAMESIZE_UXGA, false},
|
||||
{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},
|
||||
};
|
||||
|
||||
const resolution_info_t resolution[FRAMESIZE_INVALID] = {
|
||||
{ 96, 96, ASPECT_RATIO_1X1 }, /* 96x96 */
|
||||
{ 160, 120, ASPECT_RATIO_4X3 }, /* QQVGA */
|
||||
@@ -26,3 +41,13 @@ const resolution_info_t resolution[FRAMESIZE_INVALID] = {
|
||||
{ 1088, 1920, ASPECT_RATIO_9X16 }, /* Portrait FHD */
|
||||
{ 2560, 1920, ASPECT_RATIO_4X3 }, /* QSXGA */
|
||||
};
|
||||
|
||||
camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id)
|
||||
{
|
||||
for (int i = 0; i < CAMERA_MODEL_MAX; i++) {
|
||||
if (id->PID == camera_sensor[i].pid) {
|
||||
return (camera_sensor_info_t *)&camera_sensor[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -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 "../")
|
||||
|
||||
add_compile_options(-fdiagnostics-color=always)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(camera_example)
|
||||
@@ -0,0 +1,3 @@
|
||||
set(COMPONENT_SRCS take_picture.c)
|
||||
set(COMPONENT_ADD_INCLUDEDIRS .)
|
||||
register_component()
|
||||
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
|
||||
// ================================ CODE ======================================
|
||||
|
||||
#include <esp_event.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_system.h>
|
||||
#include <nvs_flash.h>
|
||||
@@ -41,6 +40,8 @@
|
||||
|
||||
#include "esp_camera.h"
|
||||
|
||||
#define BOARD_WROVER_KIT 1
|
||||
|
||||
// WROVER-KIT PIN Map
|
||||
#ifdef BOARD_WROVER_KIT
|
||||
|
||||
@@ -113,11 +114,12 @@ static camera_config_t camera_config = {
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
|
||||
.pixel_format = PIXFORMAT_JPEG, //YUV422,GRAYSCALE,RGB565,JPEG
|
||||
.frame_size = FRAMESIZE_VGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
|
||||
.pixel_format = PIXFORMAT_RGB565, //YUV422,GRAYSCALE,RGB565,JPEG
|
||||
.frame_size = FRAMESIZE_QVGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
|
||||
|
||||
.jpeg_quality = 12, //0-63 lower number means higher quality
|
||||
.fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
.fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
.grab_mode = CAMERA_GRAB_WHEN_EMPTY,
|
||||
};
|
||||
|
||||
static esp_err_t init_camera()
|
||||
@@ -135,7 +137,9 @@ static esp_err_t init_camera()
|
||||
|
||||
void app_main()
|
||||
{
|
||||
init_camera();
|
||||
if(ESP_OK != init_camera()) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
@@ -144,7 +148,8 @@ void app_main()
|
||||
|
||||
// use pic->buf to access the image
|
||||
ESP_LOGI(TAG, "Picture taken! Its size was: %zu bytes", pic->len);
|
||||
esp_camera_fb_return(pic);
|
||||
|
||||
vTaskDelay(5000 / portTICK_RATE_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x10000
|
||||
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
|
||||
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||
|
||||
CONFIG_SPIRAM_SUPPORT=y
|
||||
CONFIG_ESP32_SPIRAM_SUPPORT=y
|
||||
CONFIG_ESP32S2_SPIRAM_SUPPORT=y
|
||||
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
||||
CONFIG_SPIRAM_SPEED_80M=y
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
version: "1.0.0"
|
||||
description: This package hosts ESP32 compatible driver for OV2640 image sensors. Additionally it provides a few tools, which allow converting the captured frame data to the more common BMP and JPEG formats.
|
||||
url: https://github.com/espressif/esp32-camera
|
||||
description: ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors.
|
||||
targets:
|
||||
- esp32
|
||||
- esp32s2
|
||||
- esp32s3
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "esp32-camera",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.0",
|
||||
"keywords": "esp32, camera, espressif, esp32-cam",
|
||||
"description": "ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors.",
|
||||
"repository": {
|
||||
@@ -16,6 +16,7 @@
|
||||
"-Idriver/private_include",
|
||||
"-Iconversions/private_include",
|
||||
"-Isensors/private_include",
|
||||
"-Itarget/private_include",
|
||||
"-fno-rtti"
|
||||
],
|
||||
"includeDir": ".",
|
||||
|
||||
541
code/components/esp32-camera-master/sensors/bf3005.c
Normal file
541
code/components/esp32-camera-master/sensors/bf3005.c
Normal file
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* BF3005 driver.
|
||||
*
|
||||
* 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 "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "bf3005.h"
|
||||
#include "bf3005_regs.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.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 = "bf3005";
|
||||
#endif
|
||||
|
||||
static const uint8_t default_regs[][2] = {
|
||||
{0x12, 0x40}, //soft reset
|
||||
{0xff, 0xff}, //delay
|
||||
{0xff, 0xff}, //delay
|
||||
{0xff, 0xff}, //delay
|
||||
{0xff, 0xff}, //delay
|
||||
{0x13, 0x10},
|
||||
{0x8c, 0x00},
|
||||
{0x8d, 0x64},
|
||||
{0x87, 0x10},
|
||||
{0x13, 0x17},
|
||||
{0x00, 0x20},
|
||||
{0x01, 0x1a},
|
||||
{0x02, 0x22},
|
||||
{0x09, 0x03},
|
||||
{0x0c, 0x80},
|
||||
{0x0d, 0x24},
|
||||
{0x0e, 0x21},
|
||||
{0x0f, 0x28},
|
||||
{0x11, 0x08},
|
||||
{0x15, 0x10}, // 0X10
|
||||
{0x16, 0x03},
|
||||
{0x1e, 0x30},
|
||||
{0x20, 0x8a},
|
||||
{0x21, 0x03},
|
||||
{0x23, 0x55},
|
||||
{0x24, 0x68},
|
||||
{0x25, 0x78},
|
||||
{0x2a, 0x00},
|
||||
{0x2b, 0x00},
|
||||
{0x2d, 0x4f},
|
||||
{0x2e, 0x98},
|
||||
{0x2f, 0x04},
|
||||
{0x30, 0xad},
|
||||
{0x31, 0x17},
|
||||
{0x32, 0x6e},
|
||||
{0x33, 0x20},
|
||||
{0x35, 0xa6},
|
||||
{0x3b, 0x00},
|
||||
{0x3e, 0x00},
|
||||
{0x3f, 0xA8},
|
||||
{0x40, 0x38},
|
||||
{0x41, 0x32},
|
||||
{0x42, 0x2b},
|
||||
{0x43, 0x26},
|
||||
{0x44, 0x1a},
|
||||
{0x45, 0x16},
|
||||
{0x46, 0x10},
|
||||
{0x47, 0x0f},
|
||||
{0x48, 0x0c},
|
||||
{0x49, 0x0a},
|
||||
{0x4b, 0x09},
|
||||
{0x4c, 0x08},
|
||||
{0x4d, 0x3c},
|
||||
{0x4e, 0x06},
|
||||
{0x4f, 0x05},
|
||||
{0x50, 0x03},
|
||||
{0x51, 0x25},
|
||||
{0x52, 0x88},
|
||||
{0x53, 0x03},
|
||||
{0x63, 0x20},
|
||||
{0x64, 0x02},
|
||||
{0x65, 0xa6},
|
||||
{0x66, 0xb6},
|
||||
{0x69, 0x00},
|
||||
{0x70, 0xFF},
|
||||
{0x71, 0xa6},
|
||||
{0x72, 0x2f},
|
||||
{0x73, 0x2f},
|
||||
{0x74, 0x2F},
|
||||
{0x75, 0x0e},
|
||||
{0x76, 0x1e},
|
||||
{0x77, 0x00},
|
||||
{0x78, 0x1e},
|
||||
{0x79, 0x8a},
|
||||
{0x7d, 0xe2},
|
||||
{0x80, 0x44},
|
||||
{0x81, 0x00},
|
||||
{0x82, 0x18},
|
||||
{0x83, 0x1b},
|
||||
{0x84, 0x24},
|
||||
{0x85, 0x2a},
|
||||
{0x86, 0x4f},
|
||||
{0x89, 0x82}, //0x82
|
||||
{0x8b, 0x02},
|
||||
{0x8e, 0x03},
|
||||
{0x8f, 0xFC},
|
||||
{0x9d, 0x4d},
|
||||
{0x9e, 0x41},
|
||||
{0xa1, 0x21},
|
||||
{0xa2, 0x12},
|
||||
{0xa3, 0x32},
|
||||
{0xa4, 0x05},
|
||||
{0xa5, 0x32},
|
||||
{0xa6, 0x04},
|
||||
{0xa7, 0x7f},
|
||||
{0xa8, 0x7f},
|
||||
{0xa9, 0x21},
|
||||
{0xaa, 0x21},
|
||||
{0xab, 0x21},
|
||||
{0xac, 0x0a},
|
||||
{0xad, 0xf0},
|
||||
{0xae, 0xff},
|
||||
{0xaf, 0x1d},
|
||||
{0xb0, 0x94},
|
||||
{0xb1, 0xc0},
|
||||
{0xb2, 0xc0},
|
||||
{0xd2, 0x30},
|
||||
{0xe0, 0x0d},
|
||||
{0xe1, 0x44},
|
||||
{0xe7, 0x7c},
|
||||
{0xe8, 0x89},
|
||||
{0xe9, 0x01},
|
||||
{0xea, 0x01},
|
||||
{0xf0, 0x01},
|
||||
{0xf3, 0x49},
|
||||
{0xf4, 0xff},
|
||||
{0xf5, 0x01},
|
||||
{0xf6, 0xf2},
|
||||
{0xf7, 0x6f},
|
||||
{0x1b, 0x80},
|
||||
{0x00, 0x00},
|
||||
};
|
||||
|
||||
static int get_reg(sensor_t *sensor, int reg, int mask)
|
||||
{
|
||||
int ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
|
||||
if(ret > 0){
|
||||
ret &= mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
|
||||
if(ret < 0){
|
||||
return ret;
|
||||
}
|
||||
value = (ret & ~mask) | (value & mask);
|
||||
ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 get_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Read(sensor->slv_addr, reg);
|
||||
if(ret < 0){
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
return (ret & mask) >> offset;
|
||||
}
|
||||
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int i=0;
|
||||
const uint8_t (*regs)[2];
|
||||
|
||||
// Write default regsiters
|
||||
for (i=0, regs = default_regs; regs[i][0]; i++) {
|
||||
SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
|
||||
}
|
||||
|
||||
// Delay
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret=0;
|
||||
sensor->pixformat = pixformat;
|
||||
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_RGB565:
|
||||
set_reg_bits(sensor, 0x12, 2, 1, 1);
|
||||
break;
|
||||
case PIXFORMAT_RAW:
|
||||
set_reg_bits(sensor, 0x12, 0, 3, 0x4);
|
||||
break;
|
||||
case PIXFORMAT_YUV422:
|
||||
case PIXFORMAT_GRAYSCALE:
|
||||
set_reg_bits(sensor, 0x12, 2, 1, 0);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Delay
|
||||
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||
|
||||
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;
|
||||
// uint8_t reg = SCCB_Read(sensor->slv_addr, COM7);
|
||||
|
||||
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, 0x03, 0);
|
||||
printf("%s %d\r\n", __func__, __LINE__);
|
||||
if((w<=320)&&(h<=240))
|
||||
{
|
||||
printf("%s %d\r\n", __func__, __LINE__);
|
||||
// Enable auto-scaling/zooming factors
|
||||
//ret |= SCCB_Write(sensor->slv_addr, 0x12, 0x50);
|
||||
set_reg_bits(sensor, 0x12, 4, 1, 1);
|
||||
|
||||
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));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x03, 0);
|
||||
|
||||
} else if((w<=640)&&(h<=480))
|
||||
{
|
||||
// Enable auto-scaling/zooming factors
|
||||
//ret |= SCCB_Write(sensor->slv_addr, 0x12, 0x40);
|
||||
set_reg_bits(sensor, 0x12, 4, 1, 0);
|
||||
|
||||
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));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x03, 0);
|
||||
}
|
||||
|
||||
// Delay
|
||||
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret=0;
|
||||
sensor->status.colorbar = value;
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0xb9, value);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_whitebal(sensor_t *sensor, int enable)
|
||||
{
|
||||
if(set_reg_bits(sensor, 0x13, 1, 1, enable) >= 0){
|
||||
sensor->status.awb = !!enable;
|
||||
}
|
||||
return sensor->status.awb;
|
||||
}
|
||||
|
||||
|
||||
static int set_gain_ctrl(sensor_t *sensor, int enable)
|
||||
{
|
||||
if(set_reg_bits(sensor, 0x13, 2, 1, enable) >= 0){
|
||||
sensor->status.agc = !!enable;
|
||||
}
|
||||
return sensor->status.agc;
|
||||
}
|
||||
|
||||
|
||||
static int set_exposure_ctrl(sensor_t *sensor, int enable)
|
||||
{
|
||||
if(set_reg_bits(sensor, 0x13, 0, 1, enable) >= 0){
|
||||
sensor->status.aec = !!enable;
|
||||
}
|
||||
return sensor->status.aec;
|
||||
}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
{
|
||||
if(set_reg_bits(sensor, 0x1e, 5, 1, enable) >= 0){
|
||||
sensor->status.hmirror = !!enable;
|
||||
}
|
||||
return sensor->status.hmirror;
|
||||
}
|
||||
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
if(set_reg_bits(sensor, 0x1e, 4, 1, enable) >= 0){
|
||||
sensor->status.vflip = !!enable;
|
||||
}
|
||||
return sensor->status.vflip;
|
||||
}
|
||||
|
||||
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = set_reg_bits(sensor, 0xf1, 1, 1, !enable);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set raw_gma to: %d", !enable);
|
||||
sensor->status.raw_gma = !enable;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int set_lenc_dsp(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = set_reg_bits(sensor, 0xf1, 0, 1, !enable);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set lenc to: %d", !enable);
|
||||
sensor->status.lenc = !enable;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_agc_gain(sensor_t *sensor, int option)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = set_reg_bits(sensor, 0x13, 4, 1, !!option);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set gain to: %d", !!option);
|
||||
sensor->status.agc_gain = !!option;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_awb_gain_dsp(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Write(sensor->slv_addr, 0xa6, value);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set awb gain threthold to: %d", value);
|
||||
sensor->status.awb_gain = value;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_brightness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Write(sensor->slv_addr, 0x55, level);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set brightness to: %d", level);
|
||||
sensor->status.brightness = level;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_contrast(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Write(sensor->slv_addr, 0x56, level);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set contrast to: %d", level);
|
||||
sensor->status.contrast = level;
|
||||
}
|
||||
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 init_status(sensor_t *sensor)
|
||||
{
|
||||
sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x55);
|
||||
sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0x56);
|
||||
sensor->status.saturation = 0;
|
||||
sensor->status.ae_level = 0;
|
||||
|
||||
sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x87);
|
||||
sensor->status.awb = get_reg_bits(sensor, 0x13, 1, 1);
|
||||
sensor->status.awb_gain = SCCB_Read(sensor->slv_addr, 0xa6);
|
||||
sensor->status.aec = get_reg_bits(sensor, 0x13, 0, 1);
|
||||
|
||||
sensor->status.agc = get_reg_bits(sensor, 0x13, 2, 1);
|
||||
|
||||
sensor->status.raw_gma = get_reg_bits(sensor, 0xf1, 1, 1);
|
||||
sensor->status.lenc = get_reg_bits(sensor, 0xf1, 0, 1);
|
||||
sensor->status.hmirror = get_reg_bits(sensor, 0x1e, 5, 1);
|
||||
sensor->status.vflip = get_reg_bits(sensor, 0x1e, 4, 1);
|
||||
|
||||
sensor->status.colorbar = SCCB_Read(sensor->slv_addr, 0xb9);
|
||||
sensor->status.sharpness = SCCB_Read(sensor->slv_addr, 0x70);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val){ return -1; }
|
||||
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
|
||||
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning){return -1;}
|
||||
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div){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 bf3005_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (BF3005_SCCB_ADDR == slv_addr) {
|
||||
uint16_t PID = SCCB_Read(slv_addr, 0xFC);
|
||||
if (BF3005_PID == PID) {
|
||||
id->PID = PID;
|
||||
id->VER = SCCB_Read(slv_addr, 0xFD);
|
||||
id->MIDL = SCCB_Read(slv_addr, 0xFC);
|
||||
id->MIDH = SCCB_Read(slv_addr, 0xFD);
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bf3005_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_brightness = set_brightness;
|
||||
sensor->set_contrast = set_contrast;
|
||||
|
||||
sensor->set_colorbar = set_colorbar;
|
||||
|
||||
sensor->set_gain_ctrl = set_gain_ctrl;
|
||||
sensor->set_exposure_ctrl = set_exposure_ctrl;
|
||||
sensor->set_hmirror = set_hmirror;
|
||||
sensor->set_vflip = set_vflip;
|
||||
|
||||
sensor->set_whitebal = set_whitebal;
|
||||
|
||||
sensor->set_awb_gain = set_awb_gain_dsp;
|
||||
sensor->set_agc_gain = set_agc_gain;
|
||||
|
||||
sensor->set_raw_gma = set_raw_gma_dsp;
|
||||
sensor->set_lenc = set_lenc_dsp;
|
||||
|
||||
sensor->set_sharpness = set_sharpness;
|
||||
//not supported
|
||||
sensor->set_saturation= set_dummy;
|
||||
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->set_gainceiling = set_gainceiling_dummy;
|
||||
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = set_reg;
|
||||
sensor->set_res_raw = set_res_raw;
|
||||
sensor->set_pll = _set_pll;
|
||||
sensor->set_xclk = set_xclk;
|
||||
|
||||
ESP_LOGD(TAG, "BF3005 Attached");
|
||||
|
||||
return 0;
|
||||
}
|
||||
467
code/components/esp32-camera-master/sensors/gc0308.c
Normal file
467
code/components/esp32-camera-master/sensors/gc0308.c
Normal file
@@ -0,0 +1,467 @@
|
||||
// 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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sccb.h"
|
||||
#include "gc0308.h"
|
||||
#include "gc0308_regs.h"
|
||||
#include "gc0308_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 = "gc0308";
|
||||
#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);
|
||||
#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 = 0;
|
||||
#ifndef REG_DEBUG_ON
|
||||
ret = SCCB_Write(slv_addr, reg, value);
|
||||
#else
|
||||
int old_value = read_reg(slv_addr, reg);
|
||||
if (old_value < 0) {
|
||||
return old_value;
|
||||
}
|
||||
if ((uint8_t)old_value != value) {
|
||||
ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
|
||||
ret = SCCB_Write(slv_addr, reg, value);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
|
||||
ret = SCCB_Write(slv_addr, reg, value);//maybe not?
|
||||
}
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
|
||||
{
|
||||
return (read_reg(slv_addr, reg) & mask) == mask;
|
||||
}
|
||||
|
||||
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t c_value, new_value;
|
||||
ret = read_reg(slv_addr, reg);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
c_value = ret;
|
||||
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
|
||||
ret = write_reg(slv_addr, reg, new_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 void print_regs(uint8_t slv_addr)
|
||||
{
|
||||
#ifdef DEBUG_PRINT_REG
|
||||
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 <= 0xa2; 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));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret = 0;
|
||||
// Software Reset: clear all registers and reset them to their default values
|
||||
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xf0);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Software Reset FAILED!");
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Camera defaults loaded");
|
||||
vTaskDelay(100 / 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
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_RGB565:
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret = set_reg_bits(sensor->slv_addr, 0x24, 0, 0x0f, 6); //RGB565
|
||||
break;
|
||||
|
||||
case PIXFORMAT_YUV422:
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret = set_reg_bits(sensor->slv_addr, 0x24, 0, 0x0f, 2); //yuv422 Y Cb Y Cr
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "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) {
|
||||
ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
|
||||
framesize = FRAMESIZE_VGA;
|
||||
}
|
||||
sensor->status.framesize = framesize;
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
uint16_t row_s = (resolution[FRAMESIZE_VGA].height - h) / 2;
|
||||
uint16_t col_s = (resolution[FRAMESIZE_VGA].width - w) / 2;
|
||||
(void)row_s;
|
||||
(void)col_s;
|
||||
|
||||
#if CONFIG_GC_SENSOR_SUBSAMPLE_MODE
|
||||
struct subsample_cfg {
|
||||
uint16_t ratio_numerator;
|
||||
uint16_t ratio_denominator;
|
||||
uint8_t reg0x54;
|
||||
uint8_t reg0x56;
|
||||
uint8_t reg0x57;
|
||||
uint8_t reg0x58;
|
||||
uint8_t reg0x59;
|
||||
};
|
||||
const struct subsample_cfg subsample_cfgs[] = { // define some subsample ratio
|
||||
{84, 420, 0x55, 0x00, 0x00, 0x00, 0x00}, //1/5
|
||||
{105, 420, 0x44, 0x00, 0x00, 0x00, 0x00},//1/4
|
||||
{140, 420, 0x33, 0x00, 0x00, 0x00, 0x00},//1/3
|
||||
{210, 420, 0x22, 0x00, 0x00, 0x00, 0x00},//1/2
|
||||
{240, 420, 0x77, 0x02, 0x46, 0x02, 0x46},//4/7
|
||||
{252, 420, 0x55, 0x02, 0x04, 0x02, 0x04},//3/5
|
||||
{280, 420, 0x33, 0x02, 0x00, 0x02, 0x00},//2/3
|
||||
{420, 420, 0x11, 0x00, 0x00, 0x00, 0x00},//1/1
|
||||
};
|
||||
uint16_t win_w = 640;
|
||||
uint16_t win_h = 480;
|
||||
const struct subsample_cfg *cfg = NULL;
|
||||
/**
|
||||
* Strategy: try to keep the maximum perspective
|
||||
*/
|
||||
for (size_t i = 0; i < sizeof(subsample_cfgs) / sizeof(struct subsample_cfg); i++) {
|
||||
cfg = &subsample_cfgs[i];
|
||||
if ((win_w * cfg->ratio_numerator / cfg->ratio_denominator >= w) && (win_h * cfg->ratio_numerator / cfg->ratio_denominator >= h)) {
|
||||
win_w = w * cfg->ratio_denominator / cfg->ratio_numerator;
|
||||
win_h = h * cfg->ratio_denominator / cfg->ratio_numerator;
|
||||
row_s = (resolution[FRAMESIZE_VGA].height - win_h) / 2;
|
||||
col_s = (resolution[FRAMESIZE_VGA].width - win_w) / 2;
|
||||
ESP_LOGI(TAG, "subsample win:%dx%d, ratio:%f", win_w, win_h, (float)cfg->ratio_numerator / (float)cfg->ratio_denominator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
|
||||
write_reg(sensor->slv_addr, 0x05, H8(row_s));
|
||||
write_reg(sensor->slv_addr, 0x06, L8(row_s));
|
||||
write_reg(sensor->slv_addr, 0x07, H8(col_s));
|
||||
write_reg(sensor->slv_addr, 0x08, L8(col_s));
|
||||
write_reg(sensor->slv_addr, 0x09, H8(win_h + 8));
|
||||
write_reg(sensor->slv_addr, 0x0a, L8(win_h + 8));
|
||||
write_reg(sensor->slv_addr, 0x0b, H8(win_w + 8));
|
||||
write_reg(sensor->slv_addr, 0x0c, L8(win_w + 8));
|
||||
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x01);
|
||||
set_reg_bits(sensor->slv_addr, 0x53, 7, 0x01, 1);
|
||||
set_reg_bits(sensor->slv_addr, 0x55, 0, 0x01, 1);
|
||||
write_reg(sensor->slv_addr, 0x54, cfg->reg0x54);
|
||||
write_reg(sensor->slv_addr, 0x56, cfg->reg0x56);
|
||||
write_reg(sensor->slv_addr, 0x57, cfg->reg0x57);
|
||||
write_reg(sensor->slv_addr, 0x58, cfg->reg0x58);
|
||||
write_reg(sensor->slv_addr, 0x59, cfg->reg0x59);
|
||||
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
|
||||
#elif CONFIG_GC_SENSOR_WINDOWING_MODE
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
|
||||
write_reg(sensor->slv_addr, 0xf7, col_s / 4);
|
||||
write_reg(sensor->slv_addr, 0xf8, row_s / 4);
|
||||
write_reg(sensor->slv_addr, 0xf9, (col_s + h) / 4);
|
||||
write_reg(sensor->slv_addr, 0xfa, (row_s + w) / 4);
|
||||
|
||||
write_reg(sensor->slv_addr, 0x05, H8(row_s));
|
||||
write_reg(sensor->slv_addr, 0x06, L8(row_s));
|
||||
write_reg(sensor->slv_addr, 0x07, H8(col_s));
|
||||
write_reg(sensor->slv_addr, 0x08, L8(col_s));
|
||||
|
||||
write_reg(sensor->slv_addr, 0x09, H8(h + 8));
|
||||
write_reg(sensor->slv_addr, 0x0a, L8(h + 8));
|
||||
write_reg(sensor->slv_addr, 0x0b, H8(w + 8));
|
||||
write_reg(sensor->slv_addr, 0x0c, L8(w + 8));
|
||||
|
||||
#endif
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_contrast(sensor_t *sensor, int contrast)
|
||||
{
|
||||
if (contrast != 0) {
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
write_reg(sensor->slv_addr, 0xb3, contrast);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_global_gain(sensor_t *sensor, int gain_level)
|
||||
{
|
||||
if (gain_level != 0) {
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
write_reg(sensor->slv_addr, 0x50, gain_level);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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->slv_addr, 0x14, 0, 0x01, enable != 0);
|
||||
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->slv_addr, 0x14, 1, 0x01, enable != 0);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set v-flip to: %d", enable);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret |= set_reg_bits(sensor->slv_addr, 0x2e, 0, 0x01, enable);
|
||||
if (ret == 0) {
|
||||
sensor->status.colorbar = enable;
|
||||
ESP_LOGD(TAG, "Set colorbar to: %d", enable);
|
||||
}
|
||||
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 = 0;
|
||||
sensor->status.contrast = 0;
|
||||
sensor->status.saturation = 0;
|
||||
sensor->status.sharpness = 0;
|
||||
sensor->status.denoise = 0;
|
||||
sensor->status.ae_level = 0;
|
||||
sensor->status.gainceiling = 0;
|
||||
sensor->status.awb = 0;
|
||||
sensor->status.dcw = 0;
|
||||
sensor->status.agc = 0;
|
||||
sensor->status.aec = 0;
|
||||
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, 0x14, 0x01);
|
||||
sensor->status.vflip = check_reg_mask(sensor->slv_addr, 0x14, 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;
|
||||
|
||||
print_regs(sensor->slv_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val)
|
||||
{
|
||||
ESP_LOGW(TAG, "Unsupported");
|
||||
return -1;
|
||||
}
|
||||
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
|
||||
{
|
||||
ESP_LOGW(TAG, "Unsupported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int gc0308_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (GC0308_SCCB_ADDR == slv_addr) {
|
||||
write_reg(slv_addr, 0xfe, 0x00);
|
||||
uint8_t PID = SCCB_Read(slv_addr, 0x00);
|
||||
if (GC0308_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gc0308_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_contrast;
|
||||
sensor->set_brightness = set_dummy;
|
||||
sensor->set_saturation = set_dummy;
|
||||
sensor->set_sharpness = set_dummy;
|
||||
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_global_gain;
|
||||
sensor->set_exposure_ctrl = set_dummy;
|
||||
sensor->set_hmirror = set_hmirror;
|
||||
sensor->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, "GC0308 Attached");
|
||||
return 0;
|
||||
}
|
||||
391
code/components/esp32-camera-master/sensors/gc032a.c
Normal file
391
code/components/esp32-camera-master/sensors/gc032a.c
Normal file
@@ -0,0 +1,391 @@
|
||||
// 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 "gc032a.h"
|
||||
#include "gc032a_regs.h"
|
||||
#include "gc032a_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 = "gc032a";
|
||||
#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);
|
||||
#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 = 0;
|
||||
#ifndef REG_DEBUG_ON
|
||||
ret = SCCB_Write(slv_addr, reg, value);
|
||||
#else
|
||||
int old_value = read_reg(slv_addr, reg);
|
||||
if (old_value < 0) {
|
||||
return old_value;
|
||||
}
|
||||
if ((uint8_t)old_value != value) {
|
||||
ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
|
||||
ret = SCCB_Write(slv_addr, reg, value);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
|
||||
ret = SCCB_Write(slv_addr, reg, value);//maybe not?
|
||||
}
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
#ifdef DEBUG_PRINT_REG
|
||||
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));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t c_value, new_value;
|
||||
ret = read_reg(slv_addr, reg);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
c_value = ret;
|
||||
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
|
||||
ret = write_reg(slv_addr, reg, new_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, 0xf0);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Software Reset FAILED!");
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
ret = write_regs(sensor->slv_addr, gc032a_default_regs);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Camera defaults loaded");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
set_reg_bits(sensor->slv_addr, 0xf7, 1, 0x01, 1); // PLL_mode1:div2en
|
||||
set_reg_bits(sensor->slv_addr, 0xf7, 7, 0x01, 1); // PLL_mode1:dvp mode
|
||||
set_reg_bits(sensor->slv_addr, 0xf8, 0, 0x3f, 8); //PLL_mode2 :divx4
|
||||
set_reg_bits(sensor->slv_addr, 0xfa, 4, 0x0f, 2); //vlk div mode :divide_by
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret = 0;
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_RGB565:
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 6); //RGB565
|
||||
break;
|
||||
|
||||
case PIXFORMAT_YUV422:
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 3);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "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)
|
||||
{
|
||||
ESP_LOGI(TAG, "set_framesize");
|
||||
int ret = 0;
|
||||
if (framesize > FRAMESIZE_VGA) {
|
||||
ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
|
||||
framesize = FRAMESIZE_VGA;
|
||||
}
|
||||
sensor->status.framesize = framesize;
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
uint16_t row_s = (resolution[FRAMESIZE_VGA].height - h) / 2;
|
||||
uint16_t col_s = (resolution[FRAMESIZE_VGA].width - w) / 2;
|
||||
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
write_reg(sensor->slv_addr, P0_ROW_START_HIGH, H8(row_s)); // Row_start[8]
|
||||
write_reg(sensor->slv_addr, P0_ROW_START_LOW, L8(row_s)); // Row_start[7:0]
|
||||
write_reg(sensor->slv_addr, P0_COLUMN_START_HIGH, H8(col_s)); // Column_start[9:8]
|
||||
write_reg(sensor->slv_addr, P0_COLUMN_START_LOW, L8(col_s)); // Column_start[7:0]
|
||||
write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_HIGH, H8(h + 8)); //window_height [8]
|
||||
write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_LOW, L8(h + 8)); //window_height [7:0]
|
||||
write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_HIGH, H8(w + 8)); //window_width [9:8]
|
||||
write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_LOW, L8(w + 8)); //window_width [7:0]
|
||||
|
||||
write_reg(sensor->slv_addr, P0_WIN_MODE, 0x01);
|
||||
write_reg(sensor->slv_addr, P0_OUT_WIN_HEIGHT_HIGH, H8(h));
|
||||
write_reg(sensor->slv_addr, P0_OUT_WIN_HEIGHT_LOW, L8(h));
|
||||
write_reg(sensor->slv_addr, P0_OUT_WIN_WIDTH_HIGH, H8(w));
|
||||
write_reg(sensor->slv_addr, P0_OUT_WIN_WIDTH_LOW, L8(w));
|
||||
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
|
||||
}
|
||||
print_regs(sensor->slv_addr);
|
||||
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->slv_addr, P0_CISCTL_MODE1, 0, 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->slv_addr, P0_CISCTL_MODE1, 1, 0x01, enable);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set v-flip to: %d", enable);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret |= set_reg_bits(sensor->slv_addr, P0_DEBUG_MODE2, 3, 0x01, enable);
|
||||
if (ret == 0) {
|
||||
sensor->status.colorbar = enable;
|
||||
ESP_LOGD(TAG, "Set colorbar to: %d", enable);
|
||||
}
|
||||
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 = 0;
|
||||
sensor->status.contrast = 0;
|
||||
sensor->status.saturation = 0;
|
||||
sensor->status.sharpness = 0;
|
||||
sensor->status.denoise = 0;
|
||||
sensor->status.ae_level = 0;
|
||||
sensor->status.gainceiling = 0;
|
||||
sensor->status.awb = 0;
|
||||
sensor->status.dcw = 0;
|
||||
sensor->status.agc = 0;
|
||||
sensor->status.aec = 0;
|
||||
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x01);
|
||||
sensor->status.vflip = 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, "Unsupported");
|
||||
return -1;
|
||||
}
|
||||
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
|
||||
{
|
||||
ESP_LOGW(TAG, "Unsupported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int gc032a_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (GC032A_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 (GC032A_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gc032a_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_dummy;
|
||||
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;
|
||||
sensor->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, "GC032A Attached");
|
||||
return 0;
|
||||
}
|
||||
477
code/components/esp32-camera-master/sensors/gc2145.c
Normal file
477
code/components/esp32-camera-master/sensors/gc2145.c
Normal file
@@ -0,0 +1,477 @@
|
||||
// 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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sccb.h"
|
||||
#include "gc2145.h"
|
||||
#include "gc2145_regs.h"
|
||||
#include "gc2145_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 = "gc2145";
|
||||
#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);
|
||||
#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 = 0;
|
||||
#ifndef REG_DEBUG_ON
|
||||
ret = SCCB_Write(slv_addr, reg, value);
|
||||
#else
|
||||
int old_value = read_reg(slv_addr, reg);
|
||||
if (old_value < 0) {
|
||||
return old_value;
|
||||
}
|
||||
if ((uint8_t)old_value != value) {
|
||||
ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
|
||||
ret = SCCB_Write(slv_addr, reg, value);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
|
||||
ret = SCCB_Write(slv_addr, reg, value);//maybe not?
|
||||
}
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
|
||||
{
|
||||
return (read_reg(slv_addr, reg) & mask) == mask;
|
||||
}
|
||||
|
||||
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t c_value, new_value;
|
||||
ret = read_reg(slv_addr, reg);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
c_value = ret;
|
||||
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
|
||||
ret = write_reg(slv_addr, reg, new_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 void print_regs(uint8_t slv_addr)
|
||||
{
|
||||
#ifdef DEBUG_PRINT_REG
|
||||
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 = 0x80; i <= 0xa2; 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));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret = 0;
|
||||
// Software Reset: clear all registers and reset them to their default values
|
||||
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xe0);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Software Reset FAILED!");
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
ret = write_regs(sensor->slv_addr, gc2145_default_init_regs);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Camera defaults loaded");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
//ensure pclk <= 15MHz for esp32
|
||||
set_reg_bits(sensor->slv_addr, 0xf8, 0, 0x3f, 2); // divx4
|
||||
set_reg_bits(sensor->slv_addr, 0xfa, 4, 0x0f, 2); // divide_by
|
||||
#endif
|
||||
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_RGB565:
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret = set_reg_bits(sensor->slv_addr, P0_OUTPUT_FORMAT, 0, 0x1f, 6); //RGB565
|
||||
break;
|
||||
|
||||
case PIXFORMAT_YUV422:
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret = set_reg_bits(sensor->slv_addr, P0_OUTPUT_FORMAT, 0, 0x1f, 2); //yuv422
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "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_UXGA) {
|
||||
ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
|
||||
framesize = FRAMESIZE_UXGA;
|
||||
}
|
||||
sensor->status.framesize = framesize;
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
uint16_t row_s = (resolution[FRAMESIZE_UXGA].height - h) / 2;
|
||||
uint16_t col_s = (resolution[FRAMESIZE_UXGA].width - w) / 2;
|
||||
(void)row_s;
|
||||
(void)col_s;
|
||||
|
||||
#if CONFIG_GC_SENSOR_SUBSAMPLE_MODE
|
||||
struct subsample_cfg {
|
||||
uint16_t ratio_numerator;
|
||||
uint16_t ratio_denominator;
|
||||
uint8_t reg0x99;
|
||||
uint8_t reg0x9b;
|
||||
uint8_t reg0x9c;
|
||||
uint8_t reg0x9d;
|
||||
uint8_t reg0x9e;
|
||||
uint8_t reg0x9f;
|
||||
uint8_t reg0xa0;
|
||||
uint8_t reg0xa1;
|
||||
uint8_t reg0xa2;
|
||||
};
|
||||
const struct subsample_cfg subsample_cfgs[] = { // define some subsample ratio
|
||||
// {60, 420, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //1/7 // A smaller ratio brings a larger view, but it reduces the frame rate
|
||||
// {84, 420, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //1/5
|
||||
// {105, 420, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/4
|
||||
{140, 420, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/3
|
||||
{210, 420, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/2
|
||||
{240, 420, 0x77, 0x02, 0x46, 0x02, 0x46, 0x02, 0x46, 0x02, 0x46},//4/7
|
||||
{252, 420, 0x55, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04},//3/5
|
||||
{280, 420, 0x33, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00},//2/3
|
||||
{420, 420, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/1
|
||||
};
|
||||
uint16_t win_w = resolution[FRAMESIZE_UXGA].width;
|
||||
uint16_t win_h = resolution[FRAMESIZE_UXGA].height;
|
||||
const struct subsample_cfg *cfg = NULL;
|
||||
/**
|
||||
* Strategy: try to keep the maximum perspective
|
||||
*/
|
||||
uint8_t i = 0;
|
||||
if (framesize >= FRAMESIZE_QVGA) {
|
||||
i = 1;
|
||||
}
|
||||
for (; i < sizeof(subsample_cfgs) / sizeof(struct subsample_cfg); i++) {
|
||||
cfg = &subsample_cfgs[i];
|
||||
if ((win_w * cfg->ratio_numerator / cfg->ratio_denominator >= w) && (win_h * cfg->ratio_numerator / cfg->ratio_denominator >= h)) {
|
||||
win_w = w * cfg->ratio_denominator / cfg->ratio_numerator;
|
||||
win_h = h * cfg->ratio_denominator / cfg->ratio_numerator;
|
||||
row_s = (resolution[FRAMESIZE_UXGA].height - win_h) / 2;
|
||||
col_s = (resolution[FRAMESIZE_UXGA].width - win_w) / 2;
|
||||
ESP_LOGI(TAG, "subsample win:%dx%d, ratio:%f", win_w, win_h, (float)cfg->ratio_numerator / (float)cfg->ratio_denominator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
write_reg(sensor->slv_addr, P0_CROP_ENABLE, 0x01);
|
||||
write_reg(sensor->slv_addr, 0x09, H8(row_s));
|
||||
write_reg(sensor->slv_addr, 0x0a, L8(row_s));
|
||||
write_reg(sensor->slv_addr, 0x0b, H8(col_s));
|
||||
write_reg(sensor->slv_addr, 0x0c, L8(col_s));
|
||||
write_reg(sensor->slv_addr, 0x0d, H8(win_h + 8));
|
||||
write_reg(sensor->slv_addr, 0x0e, L8(win_h + 8));
|
||||
write_reg(sensor->slv_addr, 0x0f, H8(win_w + 16));
|
||||
write_reg(sensor->slv_addr, 0x10, L8(win_w + 16));
|
||||
|
||||
write_reg(sensor->slv_addr, 0x99, cfg->reg0x99);
|
||||
write_reg(sensor->slv_addr, 0x9b, cfg->reg0x9b);
|
||||
write_reg(sensor->slv_addr, 0x9c, cfg->reg0x9c);
|
||||
write_reg(sensor->slv_addr, 0x9d, cfg->reg0x9d);
|
||||
write_reg(sensor->slv_addr, 0x9e, cfg->reg0x9e);
|
||||
write_reg(sensor->slv_addr, 0x9f, cfg->reg0x9f);
|
||||
write_reg(sensor->slv_addr, 0xa0, cfg->reg0xa0);
|
||||
write_reg(sensor->slv_addr, 0xa1, cfg->reg0xa1);
|
||||
write_reg(sensor->slv_addr, 0xa2, cfg->reg0xa2);
|
||||
|
||||
write_reg(sensor->slv_addr, 0x95, H8(h));
|
||||
write_reg(sensor->slv_addr, 0x96, L8(h));
|
||||
write_reg(sensor->slv_addr, 0x97, H8(w));
|
||||
write_reg(sensor->slv_addr, 0x98, L8(w));
|
||||
|
||||
|
||||
#elif CONFIG_GC_SENSOR_WINDOWING_MODE
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
|
||||
write_reg(sensor->slv_addr, P0_CROP_ENABLE, 0x01);
|
||||
// write_reg(sensor->slv_addr, 0xec, col_s / 8); //measure window
|
||||
// write_reg(sensor->slv_addr, 0xed, row_s / 8);
|
||||
// write_reg(sensor->slv_addr, 0xee, (col_s + h) / 8);
|
||||
// write_reg(sensor->slv_addr, 0xef, (row_s + w) / 8);
|
||||
|
||||
write_reg(sensor->slv_addr, 0x09, H8(row_s));
|
||||
write_reg(sensor->slv_addr, 0x0a, L8(row_s));
|
||||
write_reg(sensor->slv_addr, 0x0b, H8(col_s));
|
||||
write_reg(sensor->slv_addr, 0x0c, L8(col_s));
|
||||
write_reg(sensor->slv_addr, 0x0d, H8(h + 8));
|
||||
write_reg(sensor->slv_addr, 0x0e, L8(h + 8));
|
||||
write_reg(sensor->slv_addr, 0x0f, H8(w + 8));
|
||||
write_reg(sensor->slv_addr, 0x10, L8(w + 8));
|
||||
|
||||
write_reg(sensor->slv_addr, 0x95, H8(h));
|
||||
write_reg(sensor->slv_addr, 0x96, L8(h));
|
||||
write_reg(sensor->slv_addr, 0x97, H8(w));
|
||||
write_reg(sensor->slv_addr, 0x98, L8(w));
|
||||
|
||||
#endif
|
||||
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
|
||||
}
|
||||
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->slv_addr, P0_ANALOG_MODE1, 0, 0x01, enable != 0);
|
||||
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->slv_addr, P0_ANALOG_MODE1, 1, 0x01, enable != 0);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set v-flip to: %d", enable);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
// ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
// ret |= set_reg_bits(sensor->slv_addr, P0_DEBUG_MODE3, 3, 0x01, enable);
|
||||
if (ret == 0) {
|
||||
sensor->status.colorbar = enable;
|
||||
ESP_LOGD(TAG, "Set colorbar to: %d", enable);
|
||||
}
|
||||
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 = 0;
|
||||
sensor->status.contrast = 0;
|
||||
sensor->status.saturation = 0;
|
||||
sensor->status.sharpness = 0;
|
||||
sensor->status.denoise = 0;
|
||||
sensor->status.ae_level = 0;
|
||||
sensor->status.gainceiling = 0;
|
||||
sensor->status.awb = 0;
|
||||
sensor->status.dcw = 0;
|
||||
sensor->status.agc = 0;
|
||||
sensor->status.aec = 0;
|
||||
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, P0_ANALOG_MODE1, 0x01);
|
||||
sensor->status.vflip = check_reg_mask(sensor->slv_addr, P0_ANALOG_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;
|
||||
|
||||
print_regs(sensor->slv_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val)
|
||||
{
|
||||
ESP_LOGW(TAG, "Unsupported");
|
||||
return -1;
|
||||
}
|
||||
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
|
||||
{
|
||||
ESP_LOGW(TAG, "Unsupported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int gc2145_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (GC2145_SCCB_ADDR == slv_addr) {
|
||||
uint8_t MIDL = SCCB_Read(slv_addr, CHIP_ID_LOW);
|
||||
uint8_t MIDH = SCCB_Read(slv_addr, CHIP_ID_HIGH);
|
||||
uint16_t PID = MIDH << 8 | MIDL;
|
||||
if (GC2145_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gc2145_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_dummy;
|
||||
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;
|
||||
sensor->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, "GC2145 Attached");
|
||||
return 0;
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "nt99141.h"
|
||||
#include "nt99141_regs.h"
|
||||
#include "nt99141_settings.h"
|
||||
@@ -144,28 +145,6 @@ static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value
|
||||
|
||||
#define write_reg_bits(slv_addr, reg, mask, enable) set_reg_bits(slv_addr, reg, 0, mask, enable?mask:0)
|
||||
|
||||
static int calc_sysclk(int xclk, bool pll_bypass, int pll_multiplier, int pll_sys_div, int pll_pre_div, bool pll_root_2x, int pll_seld5, bool pclk_manual, int pclk_div)
|
||||
{
|
||||
const int pll_pre_div2x_map[] = { 2, 3, 4, 6 };//values are multiplied by two to avoid floats
|
||||
const int pll_seld52x_map[] = { 2, 2, 4, 5 };
|
||||
|
||||
if (!pll_sys_div) {
|
||||
pll_sys_div = 1;
|
||||
}
|
||||
|
||||
int pll_pre_div2x = pll_pre_div2x_map[pll_pre_div];
|
||||
int pll_root_div = pll_root_2x ? 2 : 1;
|
||||
int pll_seld52x = pll_seld52x_map[pll_seld5];
|
||||
|
||||
int VCO = (xclk / 1000) * pll_multiplier * pll_root_div * 2 / pll_pre_div2x;
|
||||
int PLLCLK = pll_bypass ? (xclk) : (VCO * 1000 * 2 / pll_sys_div / pll_seld52x);
|
||||
int PCLK = PLLCLK / 2 / ((pclk_manual && pclk_div) ? pclk_div : 1);
|
||||
int SYSCLK = PLLCLK / 4;
|
||||
|
||||
ESP_LOGD(TAG, "Calculated VCO: %d Hz, PLLCLK: %d Hz, SYSCLK: %d Hz, PCLK: %d Hz", VCO * 1000, PLLCLK, SYSCLK, PCLK);
|
||||
return SYSCLK;
|
||||
}
|
||||
|
||||
static int set_pll(sensor_t *sensor, bool bypass, uint8_t multiplier, uint8_t sys_div, uint8_t pre_div, bool root_2x, uint8_t seld5, bool pclk_manual, uint8_t pclk_div)
|
||||
{
|
||||
return -1;
|
||||
@@ -309,7 +288,7 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
ret = write_regs(sensor->slv_addr, sensor_framesize_VGA);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
@@ -682,7 +661,6 @@ static int set_brightness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t value = 0;
|
||||
bool negative = false;
|
||||
|
||||
switch (level) {
|
||||
case 3:
|
||||
@@ -699,17 +677,14 @@ static int set_brightness(sensor_t *sensor, int level)
|
||||
|
||||
case -1:
|
||||
value = 0x78;
|
||||
negative = true;
|
||||
break;
|
||||
|
||||
case -2:
|
||||
value = 0x70;
|
||||
negative = true;
|
||||
break;
|
||||
|
||||
case -3:
|
||||
value = 0x60;
|
||||
negative = true;
|
||||
break;
|
||||
|
||||
default: // 0
|
||||
@@ -730,7 +705,6 @@ static int set_contrast(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t value1 = 0, value2 = 0 ;
|
||||
bool negative = false;
|
||||
|
||||
switch (level) {
|
||||
case 3:
|
||||
@@ -947,7 +921,6 @@ static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, i
|
||||
return set_pll(sensor, bypass > 0, multiplier, sys_div, pre_div, root_2x > 0, seld5, pclk_manual > 0, pclk_div);
|
||||
}
|
||||
|
||||
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -961,6 +934,23 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nt99141_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (NT99141_SCCB_ADDR == slv_addr) {
|
||||
SCCB_Write16(slv_addr, 0x3008, 0x01);//bank sensor
|
||||
uint16_t h = SCCB_Read16(slv_addr, 0x3000);
|
||||
uint16_t l = SCCB_Read16(slv_addr, 0x3001);
|
||||
uint16_t PID = (h<<8) | l;
|
||||
if (NT99141_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
sensor->status.brightness = 0;
|
||||
@@ -991,7 +981,7 @@ static int init_status(sensor_t *sensor)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NT99141_init(sensor_t *sensor)
|
||||
int nt99141_init(sensor_t *sensor)
|
||||
{
|
||||
sensor->reset = reset;
|
||||
sensor->set_pixformat = set_pixformat;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "ov2640.h"
|
||||
#include "ov2640_regs.h"
|
||||
#include "ov2640_settings.h"
|
||||
@@ -149,7 +150,7 @@ static int set_window(sensor_t *sensor, ov2640_sensor_mode_t mode, int offset_x,
|
||||
{VSIZE, max_y & 0xFF},
|
||||
{XOFFL, offset_x & 0xFF},
|
||||
{YOFFL, offset_y & 0xFF},
|
||||
{VHYX, ((max_y >> 1) & 0X80) | ((offset_y >> 4) & 0X70) | ((max_x >> 5) & 0X08) | ((offset_y >> 8) & 0X07)},
|
||||
{VHYX, ((max_y >> 1) & 0X80) | ((offset_y >> 4) & 0X70) | ((max_x >> 5) & 0X08) | ((offset_x >> 8) & 0X07)},
|
||||
{TEST, (max_x >> 2) & 0X80},
|
||||
{ZMOW, (w)&0xFF},
|
||||
{ZMOH, (h)&0xFF},
|
||||
@@ -157,26 +158,40 @@ static int set_window(sensor_t *sensor, ov2640_sensor_mode_t mode, int offset_x,
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
c.pclk_auto = 0;
|
||||
c.pclk_div = 8;
|
||||
c.clk_2x = 0;
|
||||
c.clk_div = 0;
|
||||
|
||||
if(sensor->pixformat != PIXFORMAT_JPEG){
|
||||
c.pclk_auto = 1;
|
||||
if (sensor->pixformat == PIXFORMAT_JPEG) {
|
||||
c.clk_2x = 0;
|
||||
c.clk_div = 0;
|
||||
c.pclk_auto = 0;
|
||||
c.pclk_div = 8;
|
||||
if(mode == OV2640_MODE_UXGA) {
|
||||
c.pclk_div = 12;
|
||||
}
|
||||
// if (sensor->xclk_freq_hz == 16000000) {
|
||||
// c.pclk_div = c.pclk_div / 2;
|
||||
// }
|
||||
} else {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
c.clk_2x = 0;
|
||||
#else
|
||||
c.clk_2x = 1;
|
||||
#endif
|
||||
c.clk_div = 7;
|
||||
c.pclk_auto = 1;
|
||||
c.pclk_div = 8;
|
||||
if (mode == OV2640_MODE_CIF) {
|
||||
c.clk_div = 3;
|
||||
} else if(mode == OV2640_MODE_UXGA) {
|
||||
c.pclk_div = 12;
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "Set PLL: clk_2x: %u, clk_div: %u, pclk_auto: %u, pclk_div: %u", c.clk_2x, c.clk_div, c.pclk_auto, c.pclk_div);
|
||||
|
||||
if (mode == OV2640_MODE_CIF) {
|
||||
regs = ov2640_settings_to_cif;
|
||||
if(sensor->pixformat != PIXFORMAT_JPEG){
|
||||
c.clk_div = 3;
|
||||
}
|
||||
} else if (mode == OV2640_MODE_SVGA) {
|
||||
regs = ov2640_settings_to_svga;
|
||||
} else {
|
||||
regs = ov2640_settings_to_uxga;
|
||||
c.pclk_div = 12;
|
||||
}
|
||||
|
||||
WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_BYPAS);
|
||||
@@ -480,7 +495,6 @@ static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, i
|
||||
return -1;
|
||||
}
|
||||
|
||||
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -531,6 +545,24 @@ static int init_status(sensor_t *sensor){
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ov2640_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (OV2640_SCCB_ADDR == slv_addr) {
|
||||
SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
|
||||
uint16_t PID = SCCB_Read(slv_addr, 0x0A);
|
||||
if (OV2640_PID == PID) {
|
||||
id->PID = PID;
|
||||
id->VER = SCCB_Read(slv_addr, REG_VER);
|
||||
id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
|
||||
id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ov2640_init(sensor_t *sensor)
|
||||
{
|
||||
sensor->reset = reset;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "ov3660.h"
|
||||
#include "ov3660_regs.h"
|
||||
#include "ov3660_settings.h"
|
||||
@@ -142,7 +143,7 @@ static int calc_sysclk(int xclk, bool pll_bypass, int pll_multiplier, int pll_sy
|
||||
int PCLK = PLLCLK / 2 / ((pclk_manual && pclk_div)?pclk_div:1);
|
||||
int SYSCLK = PLLCLK / 4;
|
||||
|
||||
ESP_LOGD(TAG, "Calculated VCO: %d Hz, PLLCLK: %d Hz, SYSCLK: %d Hz, PCLK: %d Hz", VCO*1000, PLLCLK, SYSCLK, PCLK);
|
||||
ESP_LOGI(TAG, "Calculated VCO: %d Hz, PLLCLK: %d Hz, SYSCLK: %d Hz, PCLK: %d Hz", VCO*1000, PLLCLK, SYSCLK, PCLK);
|
||||
return SYSCLK;
|
||||
}
|
||||
|
||||
@@ -310,13 +311,13 @@ static int set_image_options(sensor_t *sensor)
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
int ret = 0;
|
||||
framesize_t old_framesize = sensor->status.framesize;
|
||||
sensor->status.framesize = framesize;
|
||||
|
||||
if(framesize > FRAMESIZE_QXGA){
|
||||
ESP_LOGE(TAG, "Invalid framesize: %u", framesize);
|
||||
return -1;
|
||||
ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
|
||||
framesize = FRAMESIZE_QXGA;
|
||||
}
|
||||
framesize_t old_framesize = sensor->status.framesize;
|
||||
sensor->status.framesize = framesize;
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
aspect_ratio_t ratio = resolution[sensor->status.framesize].aspect_ratio;
|
||||
@@ -355,7 +356,7 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
}
|
||||
|
||||
if (sensor->pixformat == PIXFORMAT_JPEG) {
|
||||
if (framesize == FRAMESIZE_QXGA) {
|
||||
if (framesize == FRAMESIZE_QXGA || sensor->xclk_freq_hz == 16000000) {
|
||||
//40MHz SYSCLK and 10MHz PCLK
|
||||
ret = set_pll(sensor, false, 24, 1, 3, false, 0, true, 8);
|
||||
} else {
|
||||
@@ -363,12 +364,16 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
ret = set_pll(sensor, false, 30, 1, 3, false, 0, true, 10);
|
||||
}
|
||||
} else {
|
||||
if (framesize > FRAMESIZE_CIF) {
|
||||
//10MHz SYSCLK and 10MHz PCLK (6.19 FPS)
|
||||
ret = set_pll(sensor, false, 2, 1, 0, false, 0, true, 2);
|
||||
//tuned for 16MHz XCLK and 8MHz PCLK
|
||||
if (framesize > FRAMESIZE_HVGA) {
|
||||
//8MHz SYSCLK and 8MHz PCLK (4.44 FPS)
|
||||
ret = set_pll(sensor, false, 4, 1, 0, false, 2, true, 2);
|
||||
} else if (framesize >= FRAMESIZE_QVGA) {
|
||||
//16MHz SYSCLK and 8MHz PCLK (10.25 FPS)
|
||||
ret = set_pll(sensor, false, 8, 1, 0, false, 2, true, 4);
|
||||
} else {
|
||||
//25MHz SYSCLK and 10MHz PCLK (15.45 FPS)
|
||||
ret = set_pll(sensor, false, 5, 1, 0, false, 0, true, 5);
|
||||
//32MHz SYSCLK and 8MHz PCLK (17.77 FPS)
|
||||
ret = set_pll(sensor, false, 8, 1, 0, false, 0, true, 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -953,7 +958,6 @@ static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, i
|
||||
return set_pll(sensor, bypass > 0, multiplier, sys_div, pre_div, root_2x > 0, seld5, pclk_manual > 0, pclk_div);
|
||||
}
|
||||
|
||||
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -992,6 +996,22 @@ static int init_status(sensor_t *sensor)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ov3660_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (OV3660_SCCB_ADDR == slv_addr) {
|
||||
uint8_t h = SCCB_Read16(slv_addr, 0x300A);
|
||||
uint8_t l = SCCB_Read16(slv_addr, 0x300B);
|
||||
uint16_t PID = (h<<8) | l;
|
||||
if (OV3660_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ov3660_init(sensor_t *sensor)
|
||||
{
|
||||
sensor->reset = reset;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "ov5640.h"
|
||||
#include "ov5640_regs.h"
|
||||
#include "ov5640_settings.h"
|
||||
@@ -196,7 +197,7 @@ static int calc_sysclk(int xclk, bool pll_bypass, int pll_multiplier, int pll_sy
|
||||
|
||||
unsigned int SYSCLK = PLL_CLK / 4;
|
||||
|
||||
ESP_LOGD(TAG, "Calculated XVCLK: %d Hz, REFIN: %u Hz, VCO: %u Hz, PLL_CLK: %u Hz, SYSCLK: %u Hz, PCLK: %u Hz", xclk, REFIN, VCO, PLL_CLK, SYSCLK, PCLK);
|
||||
ESP_LOGI(TAG, "Calculated XVCLK: %d Hz, REFIN: %u Hz, VCO: %u Hz, PLL_CLK: %u Hz, SYSCLK: %u Hz, PCLK: %u Hz", xclk, REFIN, VCO, PLL_CLK, SYSCLK, PCLK);
|
||||
return SYSCLK;
|
||||
}
|
||||
|
||||
@@ -209,6 +210,7 @@ static int set_pll(sensor_t *sensor, bool bypass, uint8_t multiplier, uint8_t sy
|
||||
if(multiplier > 127){
|
||||
multiplier &= 0xFE;//only even integers above 127
|
||||
}
|
||||
ESP_LOGI(TAG, "Set PLL: bypass: %u, multiplier: %u, sys_div: %u, pre_div: %u, root_2x: %u, pclk_root_div: %u, pclk_manual: %u, pclk_div: %u", bypass, multiplier, sys_div, pre_div, root_2x, pclk_root_div, pclk_manual, pclk_div);
|
||||
|
||||
calc_sysclk(sensor->xclk_freq_hz, bypass, multiplier, sys_div, pre_div, root_2x, pclk_root_div, pclk_manual, pclk_div);
|
||||
|
||||
@@ -432,14 +434,22 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
if (sensor->pixformat == PIXFORMAT_JPEG) {
|
||||
//10MHz PCLK
|
||||
uint8_t sys_mul = 200;
|
||||
if(framesize < FRAMESIZE_QVGA){
|
||||
if(framesize < FRAMESIZE_QVGA || sensor->xclk_freq_hz == 16000000){
|
||||
sys_mul = 160;
|
||||
} else if(framesize < FRAMESIZE_XGA){
|
||||
sys_mul = 180;
|
||||
}
|
||||
ret = set_pll(sensor, false, sys_mul, 4, 2, false, 2, true, 4);
|
||||
//Set PLL: bypass: 0, multiplier: sys_mul, sys_div: 4, pre_div: 2, root_2x: 0, pclk_root_div: 2, pclk_manual: 1, pclk_div: 4
|
||||
} else {
|
||||
ret = set_pll(sensor, false, 10, 1, 1, false, 1, true, 4);
|
||||
//ret = set_pll(sensor, false, 8, 1, 1, false, 1, true, 4);
|
||||
if (framesize > FRAMESIZE_HVGA) {
|
||||
ret = set_pll(sensor, false, 10, 1, 2, false, 1, true, 2);
|
||||
} else if (framesize >= FRAMESIZE_QVGA) {
|
||||
ret = set_pll(sensor, false, 8, 1, 1, false, 1, true, 4);
|
||||
} else {
|
||||
ret = set_pll(sensor, false, 20, 1, 1, false, 1, true, 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
@@ -1025,7 +1035,6 @@ static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, i
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -1064,6 +1073,22 @@ static int init_status(sensor_t *sensor)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ov5640_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (OV5640_SCCB_ADDR == slv_addr) {
|
||||
uint8_t h = SCCB_Read16(slv_addr, 0x300A);
|
||||
uint8_t l = SCCB_Read16(slv_addr, 0x300B);
|
||||
uint16_t PID = (h<<8) | l;
|
||||
if (OV5640_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ov5640_init(sensor_t *sensor)
|
||||
{
|
||||
sensor->reset = reset;
|
||||
|
||||
@@ -45,7 +45,7 @@ static struct regval_list ov7670_default_regs[] = {
|
||||
{CLKRC, 0x00},
|
||||
{DBLV, 0x4A},
|
||||
|
||||
{COM10, COM10_VSYNC_NEG | COM10_PCLK_MASK},
|
||||
{COM10, COM10_VSYNC_NEG | COM10_PCLK_FREE},
|
||||
|
||||
/* Improve white balance */
|
||||
{COM4, 0x40},
|
||||
@@ -393,6 +393,24 @@ static int init_status(sensor_t *sensor)
|
||||
static int set_dummy(sensor_t *sensor, int val){ return -1; }
|
||||
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
|
||||
|
||||
int ov7670_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (OV7670_SCCB_ADDR == slv_addr) {
|
||||
SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
|
||||
uint16_t PID = SCCB_Read(slv_addr, 0x0A);
|
||||
if (OV7670_PID == PID) {
|
||||
id->PID = PID;
|
||||
id->VER = SCCB_Read(slv_addr, REG_VER);
|
||||
id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
|
||||
id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ov7670_init(sensor_t *sensor)
|
||||
{
|
||||
// Set function pointers
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "ov7725.h"
|
||||
#include "ov7725_regs.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -58,10 +59,10 @@ static const uint8_t default_regs[][2] = {
|
||||
{COM8, 0xF0},
|
||||
{COM6, 0xC5},
|
||||
{COM9, 0x11},
|
||||
{COM10, COM10_VSYNC_NEG | COM10_PCLK_MASK}, //Invert VSYNC and MASK PCLK
|
||||
{COM10, COM10_VSYNC_NEG | COM10_PCLK_FREE}, //Invert VSYNC and MASK PCLK
|
||||
{BDBASE, 0x7F},
|
||||
{DBSTEP, 0x03},
|
||||
{AEW, 0x96},
|
||||
{AEW, 0x75},
|
||||
{AEB, 0x64},
|
||||
{VPT, 0xA1},
|
||||
{EXHCL, 0x00},
|
||||
@@ -493,7 +494,6 @@ static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1
|
||||
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning){return -1;}
|
||||
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div){return -1;}
|
||||
|
||||
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -502,6 +502,24 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ov7725_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (OV7725_SCCB_ADDR == slv_addr) {
|
||||
SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
|
||||
uint16_t PID = SCCB_Read(slv_addr, 0x0A);
|
||||
if (OV7725_PID == PID) {
|
||||
id->PID = PID;
|
||||
id->VER = SCCB_Read(slv_addr, REG_VER);
|
||||
id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
|
||||
id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ov7725_init(sensor_t *sensor)
|
||||
{
|
||||
// Set function pointers
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* BF3005 driver.
|
||||
*
|
||||
*/
|
||||
#ifndef __BF3005_H__
|
||||
#define __BF3005_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 bf3005_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int bf3005_init(sensor_t *sensor);
|
||||
|
||||
#endif // __BF3005_H__
|
||||
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* BF3005 register definitions.
|
||||
*/
|
||||
#ifndef __REG_REGS_H__
|
||||
#define __REG_REGS_H__
|
||||
#if 0
|
||||
#define GAIN 0x00 /* AGC <20>C Gain control gain setting */
|
||||
#define BLUE 0x01 /* AWB <20>C Blue channel gain setting */
|
||||
#define RED 0x02 /* AWB <20>C Red channel gain setting */
|
||||
#define GREEN 0x03 /* AWB <20>C Green channel gain setting */
|
||||
#define BAVG 0x05 /* U/B Average Level */
|
||||
#define GAVG 0x06 /* Y/Gb Average Level */
|
||||
#define RAVG 0x07 /* V/R Average Level */
|
||||
#define AECH 0x08 /* Exposure Value <20>C AEC MSBs */
|
||||
|
||||
#define COM2 0x09 /* Common Control 2 */
|
||||
#define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */
|
||||
#define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */
|
||||
#define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */
|
||||
#define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */
|
||||
#define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */
|
||||
|
||||
#define REG_PID 0x0A /* Product ID Number MSB */
|
||||
#define REG_VER 0x0B /* Product ID Number LSB */
|
||||
|
||||
#define COM3 0x0C /* Common Control 3 */
|
||||
#define COM3_VFLIP 0x80 /* Vertical flip image ON/OFF selection */
|
||||
#define COM3_MIRROR 0x40 /* Horizontal mirror image ON/OFF selection */
|
||||
#define COM3_SWAP_BR 0x20 /* Swap B/R output sequence in RGB output mode */
|
||||
#define COM3_SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV output mode */
|
||||
#define COM3_SWAP_MSB 0x08 /* Swap output MSB/LSB */
|
||||
#define COM3_TRI_CLOCK 0x04 /* Tri-state option for output clock at power-down period */
|
||||
#define COM3_TRI_DATA 0x02 /* Tri-state option for output data at power-down period */
|
||||
#define COM3_COLOR_BAR 0x01 /* Sensor color bar test pattern output enable */
|
||||
#define COM3_SET_CBAR(r, x) ((r&0xFE)|((x&1)<<0))
|
||||
#define COM3_SET_MIRROR(r, x) ((r&0xBF)|((x&1)<<6))
|
||||
#define COM3_SET_FLIP(r, x) ((r&0x7F)|((x&1)<<7))
|
||||
|
||||
#define COM4 0x0D /* Common Control 4 */
|
||||
#define COM4_PLL_BYPASS 0x00 /* Bypass PLL */
|
||||
#define COM4_PLL_4x 0x40 /* PLL frequency 4x */
|
||||
#define COM4_PLL_6x 0x80 /* PLL frequency 6x */
|
||||
#define COM4_PLL_8x 0xc0 /* PLL frequency 8x */
|
||||
#define COM4_AEC_FULL 0x00 /* AEC evaluate full window */
|
||||
#define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */
|
||||
#define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */
|
||||
#define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */
|
||||
|
||||
#define COM5 0x0E /* Common Control 5 */
|
||||
#define COM5_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */
|
||||
#define COM5_AFR_SPEED 0x40 /* Auto frame rate control speed selection */
|
||||
#define COM5_AFR_0 0x00 /* No reduction of frame rate */
|
||||
#define COM5_AFR_1_2 0x10 /* Max reduction to 1/2 frame rate */
|
||||
#define COM5_AFR_1_4 0x20 /* Max reduction to 1/4 frame rate */
|
||||
#define COM5_AFR_1_8 0x30 /* Max reduction to 1/8 frame rate */
|
||||
#define COM5_AFR_4x 0x04 /* Add frame when AGC reaches 4x gain */
|
||||
#define COM5_AFR_8x 0x08 /* Add frame when AGC reaches 8x gain */
|
||||
#define COM5_AFR_16x 0x0c /* Add frame when AGC reaches 16x gain */
|
||||
#define COM5_AEC_NO_LIMIT 0x01 /* No limit to AEC increase step */
|
||||
|
||||
#define COM6 0x0F /* Common Control 6 */
|
||||
#define COM6_AUTO_WINDOW 0x01 /* Auto window setting ON/OFF selection when format changes */
|
||||
|
||||
#define AEC 0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */
|
||||
#define CLKRC 0x11 /* Internal Clock */
|
||||
|
||||
#define COM7 0x12 /* Common Control 7 */
|
||||
#define COM7_RESET 0x80 /* SCCB Register Reset */
|
||||
#define COM7_RES_VGA 0x00 /* Resolution VGA */
|
||||
#define COM7_RES_QVGA 0x40 /* Resolution QVGA */
|
||||
#define COM7_BT656 0x20 /* BT.656 protocol ON/OFF */
|
||||
#define COM7_SENSOR_RAW 0x10 /* Sensor RAW */
|
||||
#define COM7_FMT_GBR422 0x00 /* RGB output format GBR422 */
|
||||
#define COM7_FMT_RGB565 0x04 /* RGB output format RGB565 */
|
||||
#define COM7_FMT_RGB555 0x08 /* RGB output format RGB555 */
|
||||
#define COM7_FMT_RGB444 0x0C /* RGB output format RGB444 */
|
||||
#define COM7_FMT_YUV 0x00 /* Output format YUV */
|
||||
#define COM7_FMT_P_BAYER 0x01 /* Output format Processed Bayer RAW */
|
||||
#define COM7_FMT_RGB 0x02 /* Output format RGB */
|
||||
#define COM7_FMT_R_BAYER 0x03 /* Output format Bayer RAW */
|
||||
#define COM7_SET_FMT(r, x) ((r&0xFC)|((x&0x3)<<0))
|
||||
#define COM7_SET_RGB(r, x) ((r&0xF0)|(x&0x0C)|COM7_FMT_RGB)
|
||||
|
||||
#define COM8 0x13 /* Common Control 8 */
|
||||
#define COM8_FAST_AUTO 0x80 /* Enable fast AGC/AEC algorithm */
|
||||
#define COM8_STEP_VSYNC 0x00 /* AEC - Step size limited to vertical blank */
|
||||
#define COM8_STEP_UNLIMIT 0x40 /* AEC - Step size unlimited step size */
|
||||
#define COM8_BANDF_EN 0x20 /* Banding filter ON/OFF */
|
||||
#define COM8_AEC_BANDF 0x10 /* Enable AEC below banding value */
|
||||
#define COM8_AEC_FINE_EN 0x08 /* Fine AEC ON/OFF control */
|
||||
#define COM8_AGC_EN 0x04 /* AGC Enable */
|
||||
#define COM8_AWB_EN 0x02 /* AWB Enable */
|
||||
#define COM8_AEC_EN 0x01 /* AEC Enable */
|
||||
#define COM8_SET_AGC(r, x) ((r&0xFB)|((x&0x1)<<2))
|
||||
#define COM8_SET_AWB(r, x) ((r&0xFD)|((x&0x1)<<1))
|
||||
#define COM8_SET_AEC(r, x) ((r&0xFE)|((x&0x1)<<0))
|
||||
|
||||
#define COM9 0x14 /* Common Control 9 */
|
||||
#define COM9_HISTO_AVG 0x80 /* Histogram or average based AEC/AGC selection */
|
||||
#define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */
|
||||
#define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */
|
||||
#define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */
|
||||
#define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */
|
||||
#define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */
|
||||
#define COM9_DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
|
||||
#define COM9_DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
|
||||
#define COM9_SET_AGC(r, x) ((r&0x8F)|((x&0x07)<<4))
|
||||
|
||||
#define COM10 0x15 /* Common Control 10 */
|
||||
#define COM10_NEGATIVE 0x80 /* Output negative data */
|
||||
#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */
|
||||
#define COM10_PCLK_FREE 0x00 /* PCLK output option: free running PCLK */
|
||||
#define COM10_PCLK_MASK 0x20 /* PCLK output option: masked during horizontal blank */
|
||||
#define COM10_PCLK_REV 0x10 /* PCLK reverse */
|
||||
#define COM10_HREF_REV 0x08 /* HREF reverse */
|
||||
#define COM10_VSYNC_FALLING 0x00 /* VSYNC changes on falling edge of PCLK */
|
||||
#define COM10_VSYNC_RISING 0x04 /* VSYNC changes on rising edge of PCLK */
|
||||
#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */
|
||||
#define COM10_OUT_RANGE_8 0x01 /* Output data range: Full range */
|
||||
#define COM10_OUT_RANGE_10 0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */
|
||||
|
||||
#define REG16 0x16 /* Register 16 */
|
||||
#define REG16_BIT_SHIFT 0x80 /* Bit shift test pattern options */
|
||||
#define HSTART 0x17 /* Horizontal Frame (HREF column) Start 8 MSBs (2 LSBs are at HREF[5:4]) */
|
||||
#define HSIZE 0x18 /* Horizontal Sensor Size (2 LSBs are at HREF[1:0]) */
|
||||
#define VSTART 0x19 /* Vertical Frame (row) Start 8 MSBs (1 LSB is at HREF[6]) */
|
||||
#define VSIZE 0x1A /* Vertical Sensor Size (1 LSB is at HREF[2]) */
|
||||
#define PSHFT 0x1B /* Data Format - Pixel Delay Select */
|
||||
#define REG_MIDH 0x1C /* Manufacturer ID Byte <20>C High */
|
||||
#define REG_MIDL 0x1D /* Manufacturer ID Byte <20>C Low */
|
||||
#define LAEC 0x1F /* Fine AEC Value - defines exposure value less than one row period */
|
||||
|
||||
#define COM11 0x20 /* Common Control 11 */
|
||||
#define COM11_SNGL_FRAME_EN 0x02 /* Single frame ON/OFF selection */
|
||||
#define COM11_SNGL_XFR_TRIG 0x01 /* Single frame transfer trigger */
|
||||
|
||||
#define BDBASE 0x22 /* Banding Filter Minimum AEC Value */
|
||||
#define DBSTEP 0x23 /* Banding Filter Maximum Step */
|
||||
#define AEW 0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */
|
||||
#define AEB 0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */
|
||||
#define VPT 0x26 /* AGC/AEC Fast Mode Operating Region */
|
||||
#define REG28 0x28 /* Selection on the number of dummy rows, N */
|
||||
#define HOUTSIZE 0x29 /* Horizontal Data Output Size MSBs (2 LSBs at register EXHCH[1:0]) */
|
||||
#define EXHCH 0x2A /* Dummy Pixel Insert MSB */
|
||||
#define EXHCL 0x2B /* Dummy Pixel Insert LSB */
|
||||
#define VOUTSIZE 0x2C /* Vertical Data Output Size MSBs (LSB at register EXHCH[2]) */
|
||||
#define ADVFL 0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row) */
|
||||
#define ADVFH 0x2E /* MSB of Insert Dummy Rows in Vertical Sync */
|
||||
#define YAVE 0x2F /* Y/G Channel Average Value */
|
||||
#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance High Level Threshold */
|
||||
#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance Low Level Threshold */
|
||||
#define HREF 0x32 /* Image Start and Size Control */
|
||||
#define DM_LNL 0x33 /* Dummy Row Low 8 Bits */
|
||||
#define DM_LNH 0x34 /* Dummy Row High 8 Bits */
|
||||
#define ADOFF_B 0x35 /* AD Offset Compensation Value for B Channel */
|
||||
#define ADOFF_R 0x36 /* AD Offset Compensation Value for R Channel */
|
||||
#define ADOFF_GB 0x37 /* AD Offset Compensation Value for GB Channel */
|
||||
#define ADOFF_GR 0x38 /* AD Offset Compensation Value for GR Channel */
|
||||
#define OFF_B 0x39 /* AD Offset Compensation Value for B Channel */
|
||||
#define OFF_R 0x3A /* AD Offset Compensation Value for R Channel */
|
||||
#define OFF_GB 0x3B /* AD Offset Compensation Value for GB Channel */
|
||||
#define OFF_GR 0x3C /* AD Offset Compensation Value for GR Channel */
|
||||
#define COM12 0x3D /* DC offset compensation for analog process */
|
||||
|
||||
#define COM13 0x3E /* Common Control 13 */
|
||||
#define COM13_BLC_EN 0x80 /* BLC enable */
|
||||
#define COM13_ADC_EN 0x40 /* ADC channel BLC ON/OFF control */
|
||||
#define COM13_ANALOG_BLC 0x20 /* Analog processing channel BLC ON/OFF control */
|
||||
#define COM13_ABLC_GAIN_EN 0x04 /* ABLC gain trigger enable */
|
||||
|
||||
#define COM14 0x3F /* Common Control 14 */
|
||||
#define COM15 0x40 /* Common Control 15 */
|
||||
#define COM16 0x41 /* Common Control 16 */
|
||||
#define TGT_B 0x42 /* BLC Blue Channel Target Value */
|
||||
#define TGT_R 0x43 /* BLC Red Channel Target Value */
|
||||
#define TGT_GB 0x44 /* BLC Gb Channel Target Value */
|
||||
#define TGT_GR 0x45 /* BLC Gr Channel Target Value */
|
||||
|
||||
#define LC_CTR 0x46 /* Lens Correction Control */
|
||||
#define LC_CTR_RGB_COMP_1 0x00 /* R, G, and B channel compensation coefficient is set by LC_COEF (0x49) */
|
||||
#define LC_CTR_RGB_COMP_3 0x04 /* R, G, and B channel compensation coefficient is set by registers
|
||||
LC_COEFB (0x4B), LC_COEF (0x49), and LC_COEFR (0x4C), respectively */
|
||||
#define LC_CTR_EN 0x01 /* Lens correction enable */
|
||||
#define LC_XC 0x47 /* X Coordinate of Lens Correction Center Relative to Array Center */
|
||||
#define LC_YC 0x48 /* Y Coordinate of Lens Correction Center Relative to Array Center */
|
||||
#define LC_COEF 0x49 /* Lens Correction Coefficient */
|
||||
#define LC_RADI 0x4A /* Lens Correction Radius */
|
||||
#define LC_COEFB 0x4B /* Lens Correction B Channel Compensation Coefficient */
|
||||
#define LC_COEFR 0x4C /* Lens Correction R Channel Compensation Coefficient */
|
||||
|
||||
#define FIXGAIN 0x4D /* Analog Fix Gain Amplifier */
|
||||
#define AREF0 0x4E /* Sensor Reference Control */
|
||||
#define AREF1 0x4F /* Sensor Reference Current Control */
|
||||
#define AREF2 0x50 /* Analog Reference Control */
|
||||
#define AREF3 0x51 /* ADC Reference Control */
|
||||
#define AREF4 0x52 /* ADC Reference Control */
|
||||
#define AREF5 0x53 /* ADC Reference Control */
|
||||
#define AREF6 0x54 /* Analog Reference Control */
|
||||
#define AREF7 0x55 /* Analog Reference Control */
|
||||
#define UFIX 0x60 /* U Channel Fixed Value Output */
|
||||
#define VFIX 0x61 /* V Channel Fixed Value Output */
|
||||
#define AWBB_BLK 0x62 /* AWB Option for Advanced AWB */
|
||||
|
||||
#define AWB_CTRL0 0x63 /* AWB Control Byte 0 */
|
||||
#define AWB_CTRL0_GAIN_EN 0x80 /* AWB gain enable */
|
||||
#define AWB_CTRL0_CALC_EN 0x40 /* AWB calculate enable */
|
||||
#define AWB_CTRL0_WBC_MASK 0x0F /* WBC threshold 2 */
|
||||
|
||||
#define DSP_CTRL1 0x64 /* DSP Control Byte 1 */
|
||||
#define DSP_CTRL1_FIFO_EN 0x80 /* FIFO enable/disable selection */
|
||||
#define DSP_CTRL1_UV_EN 0x40 /* UV adjust function ON/OFF selection */
|
||||
#define DSP_CTRL1_SDE_EN 0x20 /* SDE enable */
|
||||
#define DSP_CTRL1_MTRX_EN 0x10 /* Color matrix ON/OFF selection */
|
||||
#define DSP_CTRL1_INTRP_EN 0x08 /* Interpolation ON/OFF selection */
|
||||
#define DSP_CTRL1_GAMMA_EN 0x04 /* Gamma function ON/OFF selection */
|
||||
#define DSP_CTRL1_BLACK_EN 0x02 /* Black defect auto correction ON/OFF */
|
||||
#define DSP_CTRL1_WHITE_EN 0x01 /* White defect auto correction ON/OFF */
|
||||
|
||||
#define DSP_CTRL2 0x65 /* DSP Control Byte 2 */
|
||||
#define DSP_CTRL2_VDCW_EN 0x08 /* Vertical DCW enable */
|
||||
#define DSP_CTRL2_HDCW_EN 0x04 /* Horizontal DCW enable */
|
||||
#define DSP_CTRL2_VZOOM_EN 0x02 /* Vertical zoom out enable */
|
||||
#define DSP_CTRL2_HZOOM_EN 0x01 /* Horizontal zoom out enable */
|
||||
|
||||
#define DSP_CTRL3 0x66 /* DSP Control Byte 3 */
|
||||
#define DSP_CTRL3_UV_EN 0x80 /* UV output sequence option */
|
||||
#define DSP_CTRL3_CBAR_EN 0x20 /* DSP color bar ON/OFF selection */
|
||||
#define DSP_CTRL3_FIFO_EN 0x08 /* FIFO power down ON/OFF selection */
|
||||
#define DSP_CTRL3_SCAL1_PWDN 0x04 /* Scaling module power down control 1 */
|
||||
#define DSP_CTRL3_SCAL2_PWDN 0x02 /* Scaling module power down control 2 */
|
||||
#define DSP_CTRL3_INTRP_PWDN 0x01 /* Interpolation module power down control */
|
||||
#define DSP_CTRL3_SET_CBAR(r, x) ((r&0xDF)|((x&1)<<5))
|
||||
|
||||
|
||||
#define DSP_CTRL4 0x67 /* DSP Control Byte 4 */
|
||||
#define DSP_CTRL4_YUV_RGB 0x00 /* Output selection YUV or RGB */
|
||||
#define DSP_CTRL4_RAW8 0x02 /* Output selection RAW8 */
|
||||
#define DSP_CTRL4_RAW10 0x03 /* Output selection RAW10 */
|
||||
|
||||
|
||||
#define AWB_BIAS 0x68 /* AWB BLC Level Clip */
|
||||
#define AWB_CTRL1 0x69 /* AWB Control 1 */
|
||||
#define AWB_CTRL2 0x6A /* AWB Control 2 */
|
||||
|
||||
#define AWB_CTRL3 0x6B /* AWB Control 3 */
|
||||
#define AWB_CTRL3_ADVANCED 0x80 /* AWB mode select - Advanced AWB */
|
||||
#define AWB_CTRL3_SIMPLE 0x00 /* AWB mode select - Simple AWB */
|
||||
|
||||
#define AWB_CTRL4 0x6C /* AWB Control 4 */
|
||||
#define AWB_CTRL5 0x6D /* AWB Control 5 */
|
||||
#define AWB_CTRL6 0x6E /* AWB Control 6 */
|
||||
#define AWB_CTRL7 0x6F /* AWB Control 7 */
|
||||
#define AWB_CTRL8 0x70 /* AWB Control 8 */
|
||||
#define AWB_CTRL9 0x71 /* AWB Control 9 */
|
||||
#define AWB_CTRL10 0x72 /* AWB Control 10 */
|
||||
#define AWB_CTRL11 0x73 /* AWB Control 11 */
|
||||
#define AWB_CTRL12 0x74 /* AWB Control 12 */
|
||||
#define AWB_CTRL13 0x75 /* AWB Control 13 */
|
||||
#define AWB_CTRL14 0x76 /* AWB Control 14 */
|
||||
#define AWB_CTRL15 0x77 /* AWB Control 15 */
|
||||
#define AWB_CTRL16 0x78 /* AWB Control 16 */
|
||||
#define AWB_CTRL17 0x79 /* AWB Control 17 */
|
||||
#define AWB_CTRL18 0x7A /* AWB Control 18 */
|
||||
#define AWB_CTRL19 0x7B /* AWB Control 19 */
|
||||
#define AWB_CTRL20 0x7C /* AWB Control 20 */
|
||||
#define AWB_CTRL21 0x7D /* AWB Control 21 */
|
||||
#define GAM1 0x7E /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */
|
||||
#define GAM2 0x7F /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */
|
||||
#define GAM3 0x80 /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */
|
||||
#define GAM4 0x81 /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */
|
||||
#define GAM5 0x82 /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */
|
||||
#define GAM6 0x83 /* Gamma Curve 6th Segment Input End Point 0x30 Output Value */
|
||||
#define GAM7 0x84 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */
|
||||
#define GAM8 0x85 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */
|
||||
#define GAM9 0x86 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */
|
||||
#define GAM10 0x87 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */
|
||||
#define GAM11 0x88 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */
|
||||
#define GAM12 0x89 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */
|
||||
#define GAM13 0x8A /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */
|
||||
#define GAM14 0x8B /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */
|
||||
#define GAM15 0x8C /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */
|
||||
#define SLOP 0x8D /* Gamma Curve Highest Segment Slope */
|
||||
#define DNSTH 0x8E /* De-noise Threshold */
|
||||
#define EDGE0 0x8F /* Edge Enhancement Strength Control */
|
||||
#define EDGE1 0x90 /* Edge Enhancement Threshold Control */
|
||||
#define DNSOFF 0x91 /* Auto De-noise Threshold Control */
|
||||
#define EDGE2 0x92 /* Edge Enhancement Strength Upper Limit */
|
||||
#define EDGE3 0x93 /* Edge Enhancement Strength Upper Limit */
|
||||
#define MTX1 0x94 /* Matrix Coefficient 1 */
|
||||
#define MTX2 0x95 /* Matrix Coefficient 2 */
|
||||
#define MTX3 0x96 /* Matrix Coefficient 3 */
|
||||
#define MTX4 0x97 /* Matrix Coefficient 4 */
|
||||
#define MTX5 0x98 /* Matrix Coefficient 5 */
|
||||
#define MTX6 0x99 /* Matrix Coefficient 6 */
|
||||
|
||||
#define MTX_CTRL 0x9A /* Matrix Control */
|
||||
#define MTX_CTRL_DBL_EN 0x80 /* Matrix double ON/OFF selection */
|
||||
|
||||
#define BRIGHTNESS 0x9B /* Brightness Control */
|
||||
#define CONTRAST 0x9C /* Contrast Gain */
|
||||
#define UVADJ0 0x9E /* Auto UV Adjust Control 0 */
|
||||
#define UVADJ1 0x9F /* Auto UV Adjust Control 1 */
|
||||
#define SCAL0 0xA0 /* DCW Ratio Control */
|
||||
#define SCAL1 0xA1 /* Horizontal Zoom Out Control */
|
||||
#define SCAL2 0xA2 /* Vertical Zoom Out Control */
|
||||
#define FIFODLYM 0xA3 /* FIFO Manual Mode Delay Control */
|
||||
#define FIFODLYA 0xA4 /* FIFO Auto Mode Delay Control */
|
||||
|
||||
#define SDE 0xA6 /* Special Digital Effect Control */
|
||||
#define SDE_NEGATIVE_EN 0x40 /* Negative image enable */
|
||||
#define SDE_GRAYSCALE_EN 0x20 /* Gray scale image enable */
|
||||
#define SDE_V_FIXED_EN 0x10 /* V fixed value enable */
|
||||
#define SDE_U_FIXED_EN 0x08 /* U fixed value enable */
|
||||
#define SDE_CONT_BRIGHT_EN 0x04 /* Contrast/Brightness enable */
|
||||
#define SDE_SATURATION_EN 0x02 /* Saturation enable */
|
||||
#define SDE_HUE_EN 0x01 /* Hue enable */
|
||||
|
||||
#define USAT 0xA7 /* U Component Saturation Gain */
|
||||
#define VSAT 0xA8 /* V Component Saturation Gain */
|
||||
#define HUECOS 0xA9 /* Cosine value <20><> 0x80 */
|
||||
#define HUESIN 0xAA /* Sine value <20><> 0x80 */
|
||||
#define SIGN_BIT 0xAB /* Sign Bit for Hue and Brightness */
|
||||
|
||||
#define DSPAUTO 0xAC /* DSP Auto Function ON/OFF Control */
|
||||
#define DSPAUTO_AWB_EN 0x80 /* AWB auto threshold control */
|
||||
#define DSPAUTO_DENOISE_EN 0x40 /* De-noise auto threshold control */
|
||||
#define DSPAUTO_EDGE_EN 0x20 /* Sharpness (edge enhancement) auto strength control */
|
||||
#define DSPAUTO_UV_EN 0x10 /* UV adjust auto slope control */
|
||||
#define DSPAUTO_SCAL0_EN 0x08 /* Auto scaling factor control (register SCAL0 (0xA0)) */
|
||||
#define DSPAUTO_SCAL1_EN 0x04 /* Auto scaling factor control (registers SCAL1 (0xA1 and SCAL2 (0xA2))*/
|
||||
#define SET_REG(reg, x) (##reg_DEFAULT|x)
|
||||
#endif //__REG_REGS_H__
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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 gc0308_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int gc0308_init(sensor_t *sensor);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* GC0308 register definitions.
|
||||
*/
|
||||
#ifndef __GC0308_REG_REGS_H__
|
||||
#define __GC0308_REG_REGS_H__
|
||||
|
||||
#define RESET_RELATED 0xfe // Bit[7]: Software reset
|
||||
// Bit[6:5]: NA
|
||||
// Bit[4]: CISCTL_restart_n
|
||||
// Bit[3:1]: NA
|
||||
// Bit[0]: page select
|
||||
// 0:page0
|
||||
// 1:page1
|
||||
|
||||
|
||||
// page0:
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief register value
|
||||
*/
|
||||
|
||||
|
||||
#endif // __GC0308_REG_REGS_H__
|
||||
@@ -0,0 +1,245 @@
|
||||
#ifndef _GC0308_SETTINGS_H_
|
||||
#define _GC0308_SETTINGS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define REG_DLY 0xffff
|
||||
#define REGLIST_TAIL 0x0000 /* Array end token */
|
||||
|
||||
static const uint16_t gc0308_sensor_default_regs[][2] = {
|
||||
{0xfe, 0x00},
|
||||
{0xec, 0x20},
|
||||
{0x05, 0x00},
|
||||
{0x06, 0x00},
|
||||
{0x07, 0x00},
|
||||
{0x08, 0x00},
|
||||
{0x09, 0x01},
|
||||
{0x0a, 0xe8},
|
||||
{0x0b, 0x02},
|
||||
{0x0c, 0x88},
|
||||
{0x0d, 0x02},
|
||||
{0x0e, 0x02},
|
||||
{0x10, 0x26},
|
||||
{0x11, 0x0d},
|
||||
{0x12, 0x2a},
|
||||
{0x13, 0x00},
|
||||
{0x14, 0x11},
|
||||
{0x15, 0x0a},
|
||||
{0x16, 0x05},
|
||||
{0x17, 0x01},
|
||||
{0x18, 0x44},
|
||||
{0x19, 0x44},
|
||||
{0x1a, 0x2a},
|
||||
{0x1b, 0x00},
|
||||
{0x1c, 0x49},
|
||||
{0x1d, 0x9a},
|
||||
{0x1e, 0x61},
|
||||
{0x1f, 0x00}, //pad drv <=24MHz, use 0x00 is ok
|
||||
{0x20, 0x7f},
|
||||
{0x21, 0xfa},
|
||||
{0x22, 0x57},
|
||||
{0x24, 0xa2}, //YCbYCr
|
||||
{0x25, 0x0f},
|
||||
{0x26, 0x03}, // 0x01
|
||||
{0x28, 0x00},
|
||||
{0x2d, 0x0a},
|
||||
{0x2f, 0x01},
|
||||
{0x30, 0xf7},
|
||||
{0x31, 0x50},
|
||||
{0x32, 0x00},
|
||||
{0x33, 0x28},
|
||||
{0x34, 0x2a},
|
||||
{0x35, 0x28},
|
||||
{0x39, 0x04},
|
||||
{0x3a, 0x20},
|
||||
{0x3b, 0x20},
|
||||
{0x3c, 0x00},
|
||||
{0x3d, 0x00},
|
||||
{0x3e, 0x00},
|
||||
{0x3f, 0x00},
|
||||
{0x50, 0x14}, // 0x14
|
||||
{0x52, 0x41},
|
||||
{0x53, 0x80},
|
||||
{0x54, 0x80},
|
||||
{0x55, 0x80},
|
||||
{0x56, 0x80},
|
||||
{0x8b, 0x20},
|
||||
{0x8c, 0x20},
|
||||
{0x8d, 0x20},
|
||||
{0x8e, 0x14},
|
||||
{0x8f, 0x10},
|
||||
{0x90, 0x14},
|
||||
{0x91, 0x3c},
|
||||
{0x92, 0x50},
|
||||
//{0x8b,0x10},
|
||||
//{0x8c,0x10},
|
||||
//{0x8d,0x10},
|
||||
//{0x8e,0x10},
|
||||
//{0x8f,0x10},
|
||||
//{0x90,0x10},
|
||||
//{0x91,0x3c},
|
||||
//{0x92,0x50},
|
||||
{0x5d, 0x12},
|
||||
{0x5e, 0x1a},
|
||||
{0x5f, 0x24},
|
||||
{0x60, 0x07},
|
||||
{0x61, 0x15},
|
||||
{0x62, 0x08}, // 0x08
|
||||
{0x64, 0x03}, // 0x03
|
||||
{0x66, 0xe8},
|
||||
{0x67, 0x86},
|
||||
{0x68, 0x82},
|
||||
{0x69, 0x18},
|
||||
{0x6a, 0x0f},
|
||||
{0x6b, 0x00},
|
||||
{0x6c, 0x5f},
|
||||
{0x6d, 0x8f},
|
||||
{0x6e, 0x55},
|
||||
{0x6f, 0x38},
|
||||
{0x70, 0x15},
|
||||
{0x71, 0x33},
|
||||
{0x72, 0xdc},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0x02},
|
||||
{0x75, 0x3f},
|
||||
{0x76, 0x02},
|
||||
{0x77, 0x38}, // 0x47
|
||||
{0x78, 0x88},
|
||||
{0x79, 0x81},
|
||||
{0x7a, 0x81},
|
||||
{0x7b, 0x22},
|
||||
{0x7c, 0xff},
|
||||
{0x93, 0x48}, //color matrix default
|
||||
{0x94, 0x02},
|
||||
{0x95, 0x07},
|
||||
{0x96, 0xe0},
|
||||
{0x97, 0x40},
|
||||
{0x98, 0xf0},
|
||||
{0xb1, 0x40},
|
||||
{0xb2, 0x40},
|
||||
{0xb3, 0x40}, //0x40
|
||||
{0xb6, 0xe0},
|
||||
{0xbd, 0x38},
|
||||
{0xbe, 0x36},
|
||||
{0xd0, 0xCB},
|
||||
{0xd1, 0x10},
|
||||
{0xd2, 0x90},
|
||||
{0xd3, 0x48},
|
||||
{0xd5, 0xF2},
|
||||
{0xd6, 0x16},
|
||||
{0xdb, 0x92},
|
||||
{0xdc, 0xA5},
|
||||
{0xdf, 0x23},
|
||||
{0xd9, 0x00},
|
||||
{0xda, 0x00},
|
||||
{0xe0, 0x09},
|
||||
{0xed, 0x04},
|
||||
{0xee, 0xa0},
|
||||
{0xef, 0x40},
|
||||
{0x80, 0x03},
|
||||
|
||||
{0x9F, 0x10},
|
||||
{0xA0, 0x20},
|
||||
{0xA1, 0x38},
|
||||
{0xA2, 0x4e},
|
||||
{0xA3, 0x63},
|
||||
{0xA4, 0x76},
|
||||
{0xA5, 0x87},
|
||||
{0xA6, 0xa2},
|
||||
{0xA7, 0xb8},
|
||||
{0xA8, 0xca},
|
||||
{0xA9, 0xd8},
|
||||
{0xAA, 0xe3},
|
||||
{0xAB, 0xeb},
|
||||
{0xAC, 0xf0},
|
||||
{0xAD, 0xF8},
|
||||
{0xAE, 0xFd},
|
||||
{0xAF, 0xFF},
|
||||
|
||||
{0xc0, 0x00},
|
||||
{0xc1, 0x10},
|
||||
{0xc2, 0x1c},
|
||||
{0xc3, 0x30},
|
||||
{0xc4, 0x43},
|
||||
{0xc5, 0x54},
|
||||
{0xc6, 0x65},
|
||||
{0xc7, 0x75},
|
||||
{0xc8, 0x93},
|
||||
{0xc9, 0xB0},
|
||||
{0xca, 0xCB},
|
||||
{0xcb, 0xE6},
|
||||
{0xcc, 0xFF},
|
||||
{0xf0, 0x02},
|
||||
{0xf1, 0x01},
|
||||
{0xf2, 0x02},
|
||||
{0xf3, 0x30},
|
||||
{0xf7, 0x04},
|
||||
{0xf8, 0x02},
|
||||
{0xf9, 0x9f},
|
||||
{0xfa, 0x78},
|
||||
{0xfe, 0x01},
|
||||
{0x00, 0xf5},
|
||||
{0x02, 0x20},
|
||||
{0x04, 0x10},
|
||||
{0x05, 0x08},
|
||||
{0x06, 0x20},
|
||||
{0x08, 0x0a},
|
||||
{0x0a, 0xa0},
|
||||
{0x0b, 0x60},
|
||||
{0x0c, 0x08},
|
||||
{0x0e, 0x44},
|
||||
{0x0f, 0x32},
|
||||
{0x10, 0x41},
|
||||
{0x11, 0x37},
|
||||
{0x12, 0x22},
|
||||
{0x13, 0x19},
|
||||
{0x14, 0x44},
|
||||
{0x15, 0x44},
|
||||
{0x16, 0xc2},
|
||||
{0x17, 0xA8},
|
||||
{0x18, 0x18},
|
||||
{0x19, 0x50},
|
||||
{0x1a, 0xd8},
|
||||
{0x1b, 0xf5},
|
||||
{0x70, 0x40},
|
||||
{0x71, 0x58},
|
||||
{0x72, 0x30},
|
||||
{0x73, 0x48},
|
||||
{0x74, 0x20},
|
||||
{0x75, 0x60},
|
||||
{0x77, 0x20},
|
||||
{0x78, 0x32},
|
||||
{0x30, 0x03},
|
||||
{0x31, 0x40},
|
||||
{0x32, 0x10},
|
||||
{0x33, 0xe0},
|
||||
{0x34, 0xe0},
|
||||
{0x35, 0x00},
|
||||
{0x36, 0x80},
|
||||
{0x37, 0x00},
|
||||
{0x38, 0x04},
|
||||
{0x39, 0x09},
|
||||
{0x3a, 0x12},
|
||||
{0x3b, 0x1C},
|
||||
{0x3c, 0x28},
|
||||
{0x3d, 0x31},
|
||||
{0x3e, 0x44},
|
||||
{0x3f, 0x57},
|
||||
{0x40, 0x6C},
|
||||
{0x41, 0x81},
|
||||
{0x42, 0x94},
|
||||
{0x43, 0xA7},
|
||||
{0x44, 0xB8},
|
||||
{0x45, 0xD6},
|
||||
{0x46, 0xEE},
|
||||
{0x47, 0x0d},
|
||||
{0x62, 0xf7},
|
||||
{0x63, 0x68},
|
||||
{0x64, 0xd3},
|
||||
{0x65, 0xd3},
|
||||
{0x66, 0x60},
|
||||
{0xfe, 0x00},
|
||||
{REGLIST_TAIL, 0x00},
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
*
|
||||
* GC032A driver.
|
||||
*
|
||||
*/
|
||||
#ifndef __GC032A_H__
|
||||
#define __GC032A_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 gc032a_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int gc032a_init(sensor_t *sensor);
|
||||
|
||||
#endif // __GC032A_H__
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* GC032A register definitions.
|
||||
*/
|
||||
#ifndef __GC032A_REG_REGS_H__
|
||||
#define __GC032A_REG_REGS_H__
|
||||
|
||||
#define SENSOR_ID_HIGH 0XF0
|
||||
#define SENSOR_ID_LOW 0XF1
|
||||
#define PAD_VB_HIZ_MODE 0XF2
|
||||
#define SYNC_OUTPUT 0XF3
|
||||
#define I2C_CONFIG 0XF4
|
||||
#define PLL_MODE1 0XF7
|
||||
#define PLL_MODE2 0XF8
|
||||
#define CM_MODE 0XF9
|
||||
#define ISP_DIV_MODE 0XFA
|
||||
#define I2C_DEVICE_ID 0XFB
|
||||
#define ANALOG_PWC 0XFC
|
||||
#define ISP_DIV_MODE2 0XFD
|
||||
#define RESET_RELATED 0XFE // Bit[7]: Software reset
|
||||
// Bit[6]: cm reset
|
||||
// Bit[5]: spi reset
|
||||
// Bit[4]: CISCTL_restart_n
|
||||
// Bit[3]: PLL_rst
|
||||
// Bit[2:0]: page select
|
||||
// 000:page0
|
||||
// 001:page1
|
||||
// 010:page2
|
||||
// 011:page3
|
||||
|
||||
//----page0-----------------------------
|
||||
#define P0_EXPOSURE_HIGH 0X03
|
||||
#define P0_EXPOSURE_LOW 0X04
|
||||
#define P0_HB_HIGH 0X05
|
||||
#define P0_HB_LOW 0X06
|
||||
#define P0_VB_HIGH 0X07
|
||||
#define P0_VB_LOW 0X08
|
||||
#define P0_ROW_START_HIGH 0X09
|
||||
#define P0_ROW_START_LOW 0X0A
|
||||
#define P0_COLUMN_START_HIGH 0X0B
|
||||
#define P0_COLUMN_START_LOW 0X0C
|
||||
#define P0_WINDOW_HEIGHT_HIGH 0X0D
|
||||
#define P0_WINDOW_HEIGHT_LOW 0X0E
|
||||
#define P0_WINDOW_WIDTH_HIGH 0X0F
|
||||
#define P0_WINDOW_WIDTH_LOW 0X10
|
||||
#define P0_SH_DELAY 0X11
|
||||
#define P0_VS_ST 0X12
|
||||
#define P0_VS_ET 0X13
|
||||
#define P0_CISCTL_MODE1 0X17
|
||||
|
||||
#define P0_BLOCK_ENABLE_1 0X40
|
||||
#define P0_AAAA_ENABLE 0X42
|
||||
#define P0_SPECIAL_EFFECT 0X43
|
||||
#define P0_SYNC_MODE 0X46
|
||||
#define P0_GAIN_CODE 0X48
|
||||
#define P0_DEBUG_MODE2 0X4C
|
||||
#define P0_WIN_MODE 0X50
|
||||
#define P0_OUT_WIN_Y1_HIGH 0X51
|
||||
#define P0_OUT_WIN_Y1_LOW 0X52
|
||||
#define P0_OUT_WIN_X1_HIGH 0X53
|
||||
#define P0_OUT_WIN_X1_LOW 0X54
|
||||
#define P0_OUT_WIN_HEIGHT_HIGH 0X55
|
||||
#define P0_OUT_WIN_HEIGHT_LOW 0X56
|
||||
#define P0_OUT_WIN_WIDTH_HIGH 0X57
|
||||
#define P0_OUT_WIN_WIDTH_LOW 0X58
|
||||
|
||||
#define P0_GLOBAL_SATURATION 0XD0
|
||||
#define P0_SATURATION_CB 0XD1
|
||||
#define P0_SATURATION_CR 0XD2
|
||||
#define P0_LUMA_CONTRAST 0XD3
|
||||
#define P0_CONTRAST_CENTER 0XD4
|
||||
#define P0_LUMA_OFFSET 0XD5
|
||||
#define P0_FIXED_CB 0XDA
|
||||
#define P0_FIXED_CR 0XDB
|
||||
|
||||
//----page3-----------------------------
|
||||
#define P3_IMAGE_WIDTH_LOW 0X5B
|
||||
#define P3_IMAGE_WIDTH_HIGH 0X5C
|
||||
#define P3_IMAGE_HEIGHT_LOW 0X5D
|
||||
#define P3_IMAGE_HEIGHT_HIGH 0X5E
|
||||
|
||||
|
||||
#endif //__GC032A_REG_REGS_H__
|
||||
@@ -0,0 +1,401 @@
|
||||
#ifndef _GC032A_SETTINGS_H_
|
||||
#define _GC032A_SETTINGS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_attr.h"
|
||||
#include "gc032a_regs.h"
|
||||
|
||||
|
||||
#define REG_DLY 0xffff
|
||||
#define REGLIST_TAIL 0x0000
|
||||
|
||||
|
||||
/*
|
||||
* The default register settings, as obtained from OmniVision. There
|
||||
* is really no making sense of most of these - lots of "reserved" values
|
||||
* and such.
|
||||
*
|
||||
*/
|
||||
static const uint16_t gc032a_default_regs[][2] = {
|
||||
/*System*/
|
||||
{0xf3, 0xff},
|
||||
{0xf5, 0x06},
|
||||
{0xf7, 0x01},
|
||||
{0xf8, 0x03},
|
||||
{0xf9, 0xce},
|
||||
{0xfa, 0x00},
|
||||
{0xfc, 0x02},
|
||||
{0xfe, 0x02},
|
||||
{0x81, 0x03},
|
||||
|
||||
{0xfe, 0x00},
|
||||
{0x77, 0x64},
|
||||
{0x78, 0x40},
|
||||
{0x79, 0x60},
|
||||
/*ANALOG & CISCTL*/
|
||||
{0xfe, 0x00},
|
||||
{0x03, 0x01},
|
||||
{0x04, 0xce},
|
||||
{0x05, 0x01},
|
||||
{0x06, 0xad},
|
||||
{0x07, 0x00},
|
||||
{0x08, 0x10},
|
||||
{0x0a, 0x00},
|
||||
{0x0c, 0x00},
|
||||
{0x0d, 0x01},
|
||||
{0x0e, 0xe8}, // height 488
|
||||
{0x0f, 0x02},
|
||||
{0x10, 0x88}, // width 648
|
||||
{0x17, 0x54},
|
||||
{0x19, 0x08},
|
||||
{0x1a, 0x0a},
|
||||
{0x1f, 0x40},
|
||||
{0x20, 0x30},
|
||||
{0x2e, 0x80},
|
||||
{0x2f, 0x2b},
|
||||
{0x30, 0x1a},
|
||||
{0xfe, 0x02},
|
||||
{0x03, 0x02},
|
||||
{0x05, 0xd7},
|
||||
{0x06, 0x60},
|
||||
{0x08, 0x80},
|
||||
{0x12, 0x89},
|
||||
|
||||
/*blk*/
|
||||
{0xfe, 0x00},
|
||||
{0x18, 0x02},
|
||||
{0xfe, 0x02},
|
||||
{0x40, 0x22},
|
||||
{0x45, 0x00},
|
||||
{0x46, 0x00},
|
||||
{0x49, 0x20},
|
||||
{0x4b, 0x3c},
|
||||
{0x50, 0x20},
|
||||
{0x42, 0x10},
|
||||
|
||||
/*isp*/
|
||||
{0xfe, 0x01},
|
||||
{0x0a, 0xc5},
|
||||
{0x45, 0x00},
|
||||
{0xfe, 0x00},
|
||||
{0x40, 0xff},
|
||||
{0x41, 0x25},
|
||||
{0x42, 0xcf},
|
||||
{0x43, 0x10},
|
||||
{0x44, 0x83},
|
||||
{0x46, 0x23},
|
||||
{0x49, 0x03},
|
||||
{0x52, 0x02},
|
||||
{0x54, 0x00},
|
||||
{0xfe, 0x02},
|
||||
{0x22, 0xf6},
|
||||
|
||||
/*Shading*/
|
||||
{0xfe, 0x01},
|
||||
{0xc1, 0x38},
|
||||
{0xc2, 0x4c},
|
||||
{0xc3, 0x00},
|
||||
{0xc4, 0x32},
|
||||
{0xc5, 0x24},
|
||||
{0xc6, 0x16},
|
||||
{0xc7, 0x08},
|
||||
{0xc8, 0x08},
|
||||
{0xc9, 0x00},
|
||||
{0xca, 0x20},
|
||||
{0xdc, 0x8a},
|
||||
{0xdd, 0xa0},
|
||||
{0xde, 0xa6},
|
||||
{0xdf, 0x75},
|
||||
|
||||
/*AWB*/
|
||||
{0xfe, 0x01},
|
||||
{0x7c, 0x09},
|
||||
{0x65, 0x06},
|
||||
{0x7c, 0x08},
|
||||
{0x56, 0xf4},
|
||||
{0x66, 0x0f},
|
||||
{0x67, 0x84},
|
||||
{0x6b, 0x80},
|
||||
{0x6d, 0x12},
|
||||
{0x6e, 0xb0},
|
||||
{0x86, 0x00},
|
||||
{0x87, 0x00},
|
||||
{0x88, 0x00},
|
||||
{0x89, 0x00},
|
||||
{0x8a, 0x00},
|
||||
{0x8b, 0x00},
|
||||
{0x8c, 0x00},
|
||||
{0x8d, 0x00},
|
||||
{0x8e, 0x00},
|
||||
{0x8f, 0x00},
|
||||
{0x90, 0x00},
|
||||
{0x91, 0x00},
|
||||
{0x92, 0xf4},
|
||||
{0x93, 0xd5},
|
||||
{0x94, 0x50},
|
||||
{0x95, 0x0f},
|
||||
{0x96, 0xf4},
|
||||
{0x97, 0x2d},
|
||||
{0x98, 0x0f},
|
||||
{0x99, 0xa6},
|
||||
{0x9a, 0x2d},
|
||||
{0x9b, 0x0f},
|
||||
{0x9c, 0x59},
|
||||
{0x9d, 0x2d},
|
||||
{0x9e, 0xaa},
|
||||
{0x9f, 0x67},
|
||||
{0xa0, 0x59},
|
||||
{0xa1, 0x00},
|
||||
{0xa2, 0x00},
|
||||
{0xa3, 0x0a},
|
||||
{0xa4, 0x00},
|
||||
{0xa5, 0x00},
|
||||
{0xa6, 0xd4},
|
||||
{0xa7, 0x9f},
|
||||
{0xa8, 0x55},
|
||||
{0xa9, 0xd4},
|
||||
{0xaa, 0x9f},
|
||||
{0xab, 0xac},
|
||||
{0xac, 0x9f},
|
||||
{0xad, 0x55},
|
||||
{0xae, 0xd4},
|
||||
{0xaf, 0xac},
|
||||
{0xb0, 0xd4},
|
||||
{0xb1, 0xa3},
|
||||
{0xb2, 0x55},
|
||||
{0xb3, 0xd4},
|
||||
{0xb4, 0xac},
|
||||
{0xb5, 0x00},
|
||||
{0xb6, 0x00},
|
||||
{0xb7, 0x05},
|
||||
{0xb8, 0xd6},
|
||||
{0xb9, 0x8c},
|
||||
|
||||
/*CC*/
|
||||
{0xfe, 0x01},
|
||||
{0xd0, 0x40},
|
||||
{0xd1, 0xf8},
|
||||
{0xd2, 0x00},
|
||||
{0xd3, 0xfa},
|
||||
{0xd4, 0x45},
|
||||
{0xd5, 0x02},
|
||||
|
||||
{0xd6, 0x30},
|
||||
{0xd7, 0xfa},
|
||||
{0xd8, 0x08},
|
||||
{0xd9, 0x08},
|
||||
{0xda, 0x58},
|
||||
{0xdb, 0x02},
|
||||
{0xfe, 0x00},
|
||||
|
||||
/*Gamma*/
|
||||
{0xfe, 0x00},
|
||||
{0xba, 0x00},
|
||||
{0xbb, 0x04},
|
||||
{0xbc, 0x0a},
|
||||
{0xbd, 0x0e},
|
||||
{0xbe, 0x22},
|
||||
{0xbf, 0x30},
|
||||
{0xc0, 0x3d},
|
||||
{0xc1, 0x4a},
|
||||
{0xc2, 0x5d},
|
||||
{0xc3, 0x6b},
|
||||
{0xc4, 0x7a},
|
||||
{0xc5, 0x85},
|
||||
{0xc6, 0x90},
|
||||
{0xc7, 0xa5},
|
||||
{0xc8, 0xb5},
|
||||
{0xc9, 0xc2},
|
||||
{0xca, 0xcc},
|
||||
{0xcb, 0xd5},
|
||||
{0xcc, 0xde},
|
||||
{0xcd, 0xea},
|
||||
{0xce, 0xf5},
|
||||
{0xcf, 0xff},
|
||||
|
||||
/*Auto Gamma*/
|
||||
{0xfe, 0x00},
|
||||
{0x5a, 0x08},
|
||||
{0x5b, 0x0f},
|
||||
{0x5c, 0x15},
|
||||
{0x5d, 0x1c},
|
||||
{0x5e, 0x28},
|
||||
{0x5f, 0x36},
|
||||
{0x60, 0x45},
|
||||
{0x61, 0x51},
|
||||
{0x62, 0x6a},
|
||||
{0x63, 0x7d},
|
||||
{0x64, 0x8d},
|
||||
{0x65, 0x98},
|
||||
{0x66, 0xa2},
|
||||
{0x67, 0xb5},
|
||||
{0x68, 0xc3},
|
||||
{0x69, 0xcd},
|
||||
{0x6a, 0xd4},
|
||||
{0x6b, 0xdc},
|
||||
{0x6c, 0xe3},
|
||||
{0x6d, 0xf0},
|
||||
{0x6e, 0xf9},
|
||||
{0x6f, 0xff},
|
||||
|
||||
/*Gain*/
|
||||
{0xfe, 0x00},
|
||||
{0x70, 0x50},
|
||||
|
||||
/*AEC*/
|
||||
{0xfe, 0x00},
|
||||
{0x4f, 0x01},
|
||||
{0xfe, 0x01},
|
||||
{0x0d, 0x00},
|
||||
{0x12, 0xa0},
|
||||
{0x13, 0x3a},
|
||||
{0x44, 0x04},
|
||||
{0x1f, 0x30},
|
||||
{0x20, 0x40},
|
||||
{0x26, 0x9a},
|
||||
{0x3e, 0x20},
|
||||
{0x3f, 0x2d},
|
||||
{0x40, 0x40},
|
||||
{0x41, 0x5b},
|
||||
{0x42, 0x82},
|
||||
{0x43, 0xb7},
|
||||
{0x04, 0x0a},
|
||||
{0x02, 0x79},
|
||||
{0x03, 0xc0},
|
||||
|
||||
/*measure window*/
|
||||
{0xfe, 0x01},
|
||||
{0xcc, 0x08},
|
||||
{0xcd, 0x08},
|
||||
{0xce, 0xa4},
|
||||
{0xcf, 0xec},
|
||||
|
||||
/*DNDD*/
|
||||
{0xfe, 0x00},
|
||||
{0x81, 0xb8},
|
||||
{0x82, 0x12},
|
||||
{0x83, 0x0a},
|
||||
{0x84, 0x01},
|
||||
{0x86, 0x50},
|
||||
{0x87, 0x18},
|
||||
{0x88, 0x10},
|
||||
{0x89, 0x70},
|
||||
{0x8a, 0x20},
|
||||
{0x8b, 0x10},
|
||||
{0x8c, 0x08},
|
||||
{0x8d, 0x0a},
|
||||
|
||||
/*Intpee*/
|
||||
{0xfe, 0x00},
|
||||
{0x8f, 0xaa},
|
||||
{0x90, 0x9c},
|
||||
{0x91, 0x52},
|
||||
{0x92, 0x03},
|
||||
{0x93, 0x03},
|
||||
{0x94, 0x08},
|
||||
{0x95, 0x44},
|
||||
{0x97, 0x00},
|
||||
{0x98, 0x00},
|
||||
|
||||
/*ASDE*/
|
||||
{0xfe, 0x00},
|
||||
{0xa1, 0x30},
|
||||
{0xa2, 0x41},
|
||||
{0xa4, 0x30},
|
||||
{0xa5, 0x20},
|
||||
{0xaa, 0x30},
|
||||
{0xac, 0x32},
|
||||
|
||||
/*YCP*/
|
||||
{0xfe, 0x00},
|
||||
{0xd1, 0x3c},
|
||||
{0xd2, 0x3c},
|
||||
{0xd3, 0x38},
|
||||
{0xd6, 0xf4},
|
||||
{0xd7, 0x1d},
|
||||
{0xdd, 0x73},
|
||||
{0xde, 0x84},
|
||||
|
||||
/*Banding*/
|
||||
{0xfe, 0x00},
|
||||
{0x05, 0x01},
|
||||
{0x06, 0xad},
|
||||
{0x07, 0x00},
|
||||
{0x08, 0x10},
|
||||
|
||||
{0xfe, 0x01},
|
||||
{0x25, 0x00},
|
||||
{0x26, 0x9a},
|
||||
|
||||
{0x27, 0x01},
|
||||
{0x28, 0xce},
|
||||
{0x29, 0x02},
|
||||
{0x2a, 0x68},
|
||||
{0x2b, 0x02},
|
||||
{0x2c, 0x68},
|
||||
{0x2d, 0x07},
|
||||
{0x2e, 0xd2},
|
||||
{0x2f, 0x0b},
|
||||
{0x30, 0x6e},
|
||||
{0x31, 0x0e},
|
||||
{0x32, 0x70},
|
||||
{0x33, 0x12},
|
||||
{0x34, 0x0c},
|
||||
{0x3c, 0x30},
|
||||
|
||||
/*Analog&Cisctl*/
|
||||
{0xfe, 0x00},
|
||||
{0x05, 0x01},
|
||||
{0x06, 0xa0},
|
||||
{0x07, 0x00},
|
||||
{0x08, 0x20},
|
||||
{0x0a, 0x78},
|
||||
{0x0c, 0xa0},
|
||||
{0x0d, 0x00}, //window_height [8]
|
||||
{0x0e, 0xf8}, //window_height [7:0] 248
|
||||
{0x0f, 0x01}, //window_width [9:8]
|
||||
{0x10, 0x48}, //window_width [7:0] 328
|
||||
|
||||
{0x55, 0x00},
|
||||
{0x56, 0xf0}, // 240
|
||||
{0x57, 0x01},
|
||||
{0x58, 0x40}, // 320
|
||||
|
||||
/*SPI*/
|
||||
{0xfe, 0x03},
|
||||
{0x5b, 0x40},
|
||||
{0x5c, 0x01},
|
||||
{0x5d, 0xf0},
|
||||
{0x5e, 0x00},
|
||||
|
||||
/*AEC*/
|
||||
{0xfe, 0x01},
|
||||
{0x25, 0x00}, //step
|
||||
{0x26, 0x63},
|
||||
{0x27, 0x01},
|
||||
{0x28, 0x29},
|
||||
{0x29, 0x01},
|
||||
{0x2a, 0x29},
|
||||
{0x2b, 0x01},
|
||||
{0x2c, 0x29},
|
||||
{0x2d, 0x01},
|
||||
{0x2e, 0x29},
|
||||
{0x2f, 0x01},
|
||||
{0x30, 0x29},
|
||||
{0x31, 0x01},
|
||||
{0x32, 0x29},
|
||||
{0x33, 0x01},
|
||||
{0x34, 0x29},
|
||||
{0x3c, 0x00},
|
||||
|
||||
/*measure window*/
|
||||
{0xfe, 0x01},
|
||||
{0xcc, 0x04},
|
||||
{0xcd, 0x04},
|
||||
{0xce, 0x72},
|
||||
{0xcf, 0x52},
|
||||
{REGLIST_TAIL, 0x00},
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
#ifndef __GC2145_H__
|
||||
#define __GC2145_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 gc2145_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int gc2145_init(sensor_t *sensor);
|
||||
|
||||
#endif // __GC2145_H__
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* GC2145 register definitions.
|
||||
*/
|
||||
#ifndef __GC2145_REG_REGS_H__
|
||||
#define __GC2145_REG_REGS_H__
|
||||
|
||||
#define CHIP_ID_HIGH 0XF0
|
||||
#define CHIP_ID_LOW 0XF1
|
||||
#define PLL_MODE1 0XF7
|
||||
#define PLL_MODE2 0XF8
|
||||
#define CM_MODE 0XF9
|
||||
#define CLK_DIV_MODE 0XFA
|
||||
#define RESET_RELATED 0xfe // Bit[7]: Software reset
|
||||
// Bit[6]: cm reset
|
||||
// Bit[5]: mipi reset
|
||||
// Bit[4]: CISCTL_restart_n
|
||||
// Bit[3]: NA
|
||||
// Bit[2:0]: page select
|
||||
// 000:page0
|
||||
// 001:page1
|
||||
// 010:page2
|
||||
// 011:page3
|
||||
|
||||
//-page0----------------
|
||||
|
||||
#define P0_EXPOSURE_HIGH 0X03
|
||||
#define P0_EXPOSURE_LOW 0X04
|
||||
#define P0_HB_HIGH 0X05
|
||||
#define P0_HB_LOW 0X06
|
||||
#define P0_VB_HIGH 0X07
|
||||
#define P0_VB_LOW 0X08
|
||||
#define P0_ROW_START_HIGH 0X09
|
||||
#define P0_ROW_START_LOW 0X0A
|
||||
#define P0_COL_START_HIGH 0X0B
|
||||
#define P0_COL_START_LOW 0X0C
|
||||
|
||||
#define P0_WIN_HEIGHT_HIGH 0X0D
|
||||
#define P0_WIN_HEIGHT_LOW 0X0E
|
||||
#define P0_WIN_WIDTH_HIGH 0X0F
|
||||
#define P0_WIN_WIDTH_LOW 0X10
|
||||
#define P0_ANALOG_MODE1 0X17
|
||||
#define P0_ANALOG_MODE2 0X18
|
||||
|
||||
#define P0_SPECIAL_EFFECT 0X83
|
||||
#define P0_OUTPUT_FORMAT 0x84 // Format select
|
||||
// Bit[7]:YUV420 row switch
|
||||
// Bit[6]:YUV420 col switch
|
||||
// Bit[7]:YUV420_legacy
|
||||
// Bit[4:0]:output data mode
|
||||
// 5’h00 Cb Y Cr Y
|
||||
// 5’h01 Cr Y Cb Y
|
||||
// 5’h02 Y Cb Y Cr
|
||||
// 5’h03 Y Cr Y Cb
|
||||
// 5’h04 LSC bypass, C/Y
|
||||
// 5’h05 LSC bypass, Y/C
|
||||
// 5’h06 RGB 565
|
||||
// 5’h0f bypass 10bits
|
||||
// 5’h17 switch odd/even column /row to controls output Bayer pattern
|
||||
// 00 RGBG
|
||||
// 01 RGGB
|
||||
// 10 BGGR
|
||||
// 11 GBRG
|
||||
// 5'h18 DNDD out mode
|
||||
// 5'h19 LSC out mode
|
||||
// 5;h1b EEINTP out mode
|
||||
#define P0_FRAME_START 0X85
|
||||
#define P0_SYNC_MODE 0X86
|
||||
#define P0_MODULE_GATING 0X88
|
||||
#define P0_BYPASS_MODE 0X89
|
||||
#define P0_DEBUG_MODE2 0X8C
|
||||
#define P0_DEBUG_MODE3 0X8D
|
||||
#define P0_CROP_ENABLE 0X90
|
||||
#define P0_OUT_WIN_Y1_HIGH 0X91
|
||||
#define P0_OUT_WIN_Y1_LOW 0X92
|
||||
#define P0_OUT_WIN_X1_HIGH 0X93
|
||||
#define P0_OUT_WIN_X1_LOW 0X94
|
||||
#define P0_OUT_WIN_HEIGHT_HIGH 0X95
|
||||
#define P0_OUT_WIN_HEIGHT_LOW 0X96
|
||||
#define P0_OUT_WIN_WIDTH_HIGH 0X97
|
||||
#define P0_OUT_WIN_WIDTH_LOW 0X98
|
||||
#define P0_SUBSAMPLE 0X99
|
||||
#define P0_SUBSAMPLE_MODE 0X9A
|
||||
|
||||
|
||||
#endif // __GC2145_REG_REGS_H__
|
||||
@@ -0,0 +1,719 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define REG_DLY 0xffff
|
||||
#define REGLIST_TAIL 0x0000 /* Array end token */
|
||||
|
||||
static const uint16_t gc2145_default_init_regs[][2] = {
|
||||
{0xfe, 0xf0},
|
||||
{0xfe, 0xf0},
|
||||
{0xfe, 0xf0},
|
||||
|
||||
{0xfc, 0x06},
|
||||
{0xf6, 0x00},
|
||||
|
||||
{0xf7, 0x1d}, //37 //17 //37 //1d//05
|
||||
{0xf8, 0x83}, //87 //83 //82
|
||||
{0xfa, 0x00},
|
||||
{0xf9, 0xfe}, //ff
|
||||
{0xfd, 0x00},
|
||||
{0xc2, 0x00},
|
||||
{0xf2, 0x0f},
|
||||
//////////////////////////////////////////////////////
|
||||
//////////////////// Analog & Cisctl ////////////////
|
||||
//////////////////////////////////////////////////////
|
||||
{0xfe, 0x00},
|
||||
|
||||
{0x03, 0x04}, //exp time
|
||||
{0x04, 0x62}, //exp time
|
||||
|
||||
{0x05, 0x01}, //00 //hb[11:8]
|
||||
{0x06, 0x3b}, //0b //hb
|
||||
|
||||
{0x09, 0x00}, //row start
|
||||
{0x0a, 0x00}, //
|
||||
{0x0b, 0x00}, //col start
|
||||
{0x0c, 0x00},
|
||||
{0x0d, 0x04}, //height
|
||||
{0x0e, 0xc0},
|
||||
{0x0f, 0x06}, //width
|
||||
{0x10, 0x52},
|
||||
|
||||
{0x12, 0x2e}, //sh_delay 太短 YUV出图异常
|
||||
{0x17, 0x14}, //CISCTL Mode1 [1:0]mirror flip
|
||||
{0x18, 0x22}, //sdark mode
|
||||
{0x19, 0x0f}, // AD pipe number
|
||||
{0x1a, 0x01}, //AD manual switch mode
|
||||
|
||||
{0x1b, 0x4b}, //48 restg Width,SH width
|
||||
{0x1c, 0x07}, //06 帧率快后,横条纹 //12 //TX Width,Space Width
|
||||
{0x1d, 0x10}, //double reset
|
||||
{0x1e, 0x88}, //90//98 //fix 竖线//Analog Mode1,TX high,Coln_r
|
||||
{0x1f, 0x78}, //78 //38 //18 //Analog Mode2,txlow
|
||||
{0x20, 0x03}, //07 //Analog Mode3,comv,ad_clk mode
|
||||
{0x21, 0x40}, //10//20//40 //fix 灯管横条纹
|
||||
{0x22, 0xa0}, //d0//f0 //a2 //Vref vpix FPN严重
|
||||
{0x24, 0x1e},
|
||||
{0x25, 0x01}, //col sel
|
||||
{0x26, 0x10}, //Analog PGA gain1
|
||||
{0x2d, 0x60}, //40//40 //txl drv mode
|
||||
{0x30, 0x01}, //Analog Mode4
|
||||
{0x31, 0x90}, //b0//70 // Analog Mode7 [7:5]rsgh_r灯管横条纹[4:3]isp_g
|
||||
{0x33, 0x06}, //03//02//01 //EQ_hstart_width
|
||||
{0x34, 0x01},
|
||||
//
|
||||
///////////////////////////////////////////////////
|
||||
//////////////////// ISP reg //////////////////////
|
||||
//////////////////////////////////////////////////////
|
||||
{0x80, 0xff}, //outdoor gamma_en, GAMMA_en, CC_en, EE_en, INTP_en, DN_en, DD_en,LSC_en
|
||||
{0x81, 0x24}, //26//24 //BLK dither mode, ll_y_en ,skin_en, edge SA, new_skin_mode, autogray_en,ll_gamma_en,BFF test image
|
||||
{0x82, 0xfa}, //FA //auto_SA, auto_EE, auto_DN, auto_DD, auto_LSC, ABS_en, AWB_en, NA
|
||||
{0x83, 0x00}, //special_effect
|
||||
{0x84, 0x02}, //output format
|
||||
{0x86, 0x03}, //c2 //46 //c2 //sync mode
|
||||
{0x88, 0x03}, //[1]ctl_auto_gating [0]out_auto_gating
|
||||
{0x89, 0x03}, //bypass disable
|
||||
{0x85, 0x30}, //60//frame start cut
|
||||
{0x8a, 0x00}, //ISP_quiet_mode,close aaa pclk,BLK gate mode,exception,close first pipe clock,close dndd clock,close intp clock,DIV_gatedclk_en
|
||||
{0x8b, 0x00}, //[7:6]BFF_gate_mode,[5]BLK switch gain,[4]protect exp,[3:2]pipe gate mode,[1]not split sram,[0]dark current update
|
||||
|
||||
{0xb0, 0x55}, //60 //global gain
|
||||
{0xc3, 0x00}, //[7:4]auto_exp_gamma_th1[11:8],[3:0]auto_exp_gamma_th2[11:8]
|
||||
{0xc4, 0x80}, //auto_exp_gamma_th1[7:0] into
|
||||
{0xc5, 0x90}, //auto_exp_gamma_th2[7:0] out //outdoor gamma
|
||||
{0xc6, 0x38}, //auto_gamma_th1
|
||||
{0xc7, 0x40}, //auto_gamma_th2
|
||||
|
||||
{0xec, 0x06}, //measure window
|
||||
{0xed, 0x04},
|
||||
{0xee, 0x60}, //16 col
|
||||
{0xef, 0x90}, //8 row
|
||||
|
||||
{0xb6, 0x01}, //[0]aec en
|
||||
|
||||
{0x90, 0x01}, //crop
|
||||
{0x91, 0x00},
|
||||
{0x92, 0x00},
|
||||
{0x93, 0x00},
|
||||
{0x94, 0x00}, //08
|
||||
{0x95, 0x04},
|
||||
{0x96, 0xb0},
|
||||
{0x97, 0x06},
|
||||
{0x98, 0x40},
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/////////// BLK ////////////////////////
|
||||
///////////////////////////////////////////////
|
||||
{0x18, 0x02},
|
||||
{0x40, 0x42}, //2b //27
|
||||
{0x41, 0x00}, //80 //dark row sel
|
||||
{0x43, 0x54}, //[7:4]BLK start not smooth [3:0]output start frame
|
||||
|
||||
{0x5e, 0x00}, //00//10 //18
|
||||
{0x5f, 0x00}, //00//10 //18
|
||||
{0x60, 0x00}, //00//10 //18
|
||||
{0x61, 0x00}, //00///10 //18
|
||||
{0x62, 0x00}, //00//10 //18
|
||||
{0x63, 0x00}, //00//10 //18
|
||||
{0x64, 0x00}, //00/10 //18
|
||||
{0x65, 0x00}, //00//10 //18
|
||||
{0x66, 0x20}, //1e
|
||||
{0x67, 0x20}, //1e
|
||||
{0x68, 0x20}, //1e
|
||||
{0x69, 0x20}, //1e
|
||||
|
||||
|
||||
{0x76, 0x00}, //0f
|
||||
|
||||
{0x6a, 0x00}, //06
|
||||
{0x6b, 0x00}, //06
|
||||
{0x6c, 0x3e}, //06
|
||||
{0x6d, 0x3e}, //06
|
||||
{0x6e, 0x3f}, //06
|
||||
{0x6f, 0x3f}, //06
|
||||
{0x70, 0x00}, //06
|
||||
{0x71, 0x00}, //06 //manual offset
|
||||
|
||||
{0x76, 0x00}, //1f//add offset
|
||||
{0x72, 0xf0}, //[7:4]BLK DD th [3:0]BLK various th
|
||||
{0x7e, 0x3c}, //ndark
|
||||
{0x7f, 0x00},
|
||||
|
||||
{0xfe, 0x02},
|
||||
{0x48, 0x15},
|
||||
{0x49, 0x00}, //04//04 //ASDE OFFSET SLOPE
|
||||
{0x4b, 0x0b}, //ASDE y OFFSET SLOPE
|
||||
{0xfe, 0x00},
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/////////// AEC ////////////////////////
|
||||
///////////////////////////////////////////////
|
||||
{0xfe, 0x01},
|
||||
|
||||
{0x01, 0x04}, //AEC X1
|
||||
{0x02, 0xc0}, //AEC X2
|
||||
{0x03, 0x04}, //AEC Y1
|
||||
{0x04, 0x90}, //AEC Y2
|
||||
{0x05, 0x30}, //20 //AEC center X1
|
||||
{0x06, 0x90}, //40 //AEC center X2
|
||||
{0x07, 0x20}, //30 //AEC center Y1
|
||||
{0x08, 0x70}, //60 //AEC center Y2
|
||||
|
||||
{0x09, 0x00}, //AEC show mode
|
||||
{0x0a, 0xc2}, //[7]col gain enable
|
||||
{0x0b, 0x11}, //AEC every N
|
||||
{0x0c, 0x10}, //AEC_mode3 center weight
|
||||
{0x13, 0x40}, //2a //AEC Y target
|
||||
{0x17, 0x00}, //AEC ignore mode
|
||||
{0x1c, 0x11}, //
|
||||
{0x1e, 0x61}, //
|
||||
{0x1f, 0x30}, //40//50 //max pre gain
|
||||
{0x20, 0x40}, //60//40 //max post gain
|
||||
{0x22, 0x80}, //AEC outdoor THD
|
||||
{0x23, 0x20}, //target_Y_low_limit
|
||||
{0xfe, 0x02},
|
||||
{0x0f, 0x04}, //05
|
||||
{0xfe, 0x01},
|
||||
|
||||
{0x12, 0x35}, //35 //[5:4]group_size [3]slope_disable [2]outdoor_enable [0]histogram_enable
|
||||
{0x15, 0x50}, //target_Y_high_limit
|
||||
{0x10, 0x31}, //num_thd_high
|
||||
{0x3e, 0x28}, //num_thd_low
|
||||
{0x3f, 0xe0}, //luma_thd
|
||||
{0x40, 0x20}, //luma_slope
|
||||
{0x41, 0x0f}, //color_diff
|
||||
|
||||
{0xfe, 0x02},
|
||||
{0x0f, 0x05}, //max_col_level
|
||||
///////////////////////////
|
||||
////// INTPEE /////////////
|
||||
///////////////////////////
|
||||
{0xfe, 0x02}, //page2
|
||||
{0x90, 0x6c}, //ac //eeintp mode1
|
||||
{0x91, 0x03}, //02 ////eeintp mode2
|
||||
{0x92, 0xc8}, //44 //low criteria for direction
|
||||
{0x94, 0x66},
|
||||
{0x95, 0xb5},
|
||||
{0x97, 0x64}, //78 ////edge effect
|
||||
{0xa2, 0x11}, //fix direction
|
||||
{0xfe, 0x00},
|
||||
|
||||
/////////////////////////////
|
||||
//////// DNDD///////////////
|
||||
/////////////////////////////
|
||||
{0xfe, 0x02},
|
||||
{0x80, 0xc1}, //c1 //[7]share mode [6]skin mode [5]is 5x5 mode [1:0]noise value select 0:2 1:2.5 2:3 3:4
|
||||
{0x81, 0x08}, //
|
||||
{0x82, 0x08}, //signal a 0.6
|
||||
{0x83, 0x08}, //04 //signal b 2.5
|
||||
|
||||
{0x84, 0x0a}, //10 //05 dark_DD_TH
|
||||
{0x86, 0xf0}, //a0 Y_value_dd_th2
|
||||
{0x87, 0x50}, //90 Y_value_dd_th3
|
||||
{0x88, 0x15}, //60 Y_value_dd_th4
|
||||
|
||||
{0x89, 0x50}, //80 // asde th2
|
||||
{0x8a, 0x30}, //60 // asde th3
|
||||
{0x8b, 0x10}, //30 // asde th4
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
///////////// ASDE ////////////////////////
|
||||
/////////////////////////////////////////////////
|
||||
{0xfe, 0x01}, //page 1
|
||||
{0x21, 0x14}, //luma_value_div_sel(分频,与0xef呈2倍关系,增大1,0xef的值减小1倍)
|
||||
//ff ef luma_value read_only
|
||||
|
||||
{0xfe, 0x02}, //page2
|
||||
{0xa3, 0x40}, //ASDE_low_luma_value_LSC_th_H
|
||||
{0xa4, 0x20}, //ASDE_low_luma_value_LSC_th_L
|
||||
|
||||
{0xa5, 0x40}, //80 //ASDE_LSC_gain_dec_slope_H
|
||||
{0xa6, 0x80}, // 80 //ASDE_LSC_gain_dec_slope_L
|
||||
//ff a7 ASDE_LSC_gain_dec //read only
|
||||
|
||||
{0xab, 0x40}, //50 //ASDE_low_luma_value_OT_th
|
||||
|
||||
{0xae, 0x0c}, //[3]EE1_effect_inc_or_dec_high,[2]EE2_effect_inc_or_dec_high,
|
||||
//[1]EE1_effect_inc_or_dec_low,[0]EE2_effect_inc_or_dec_low, 1:inc 0:dec
|
||||
|
||||
{0xb3, 0x34}, //44 //ASDE_EE1_effect_slope_low,ASDE_EE2_effect_slope_low
|
||||
{0xb4, 0x44}, //12 //ASDE_EE1_effect_slope_high,ASDE_EE2_effect_slope_high
|
||||
|
||||
{0xb6, 0x38}, //40//40 //ASDE_auto_saturation_dec_slope
|
||||
{0xb7, 0x02}, //04 //ASDE_sub_saturation_slope
|
||||
{0xb9, 0x30}, //[7:0]ASDE_auto_saturation_low_limit
|
||||
{0x3c, 0x08}, //[3:0]auto gray_dec_slope
|
||||
{0x3d, 0x30}, //[7:0]auto gray_dec_th
|
||||
|
||||
|
||||
{0x4b, 0x0d}, //y offset slope
|
||||
{0x4c, 0x20}, //y offset limit
|
||||
|
||||
{0xfe, 0x00},
|
||||
//
|
||||
///////////////////gamma1////////////////////
|
||||
////Gamma
|
||||
{0xfe, 0x02},
|
||||
{0x10, 0x10},
|
||||
{0x11, 0x15},
|
||||
{0x12, 0x1a},
|
||||
{0x13, 0x1f},
|
||||
{0x14, 0x2c},
|
||||
{0x15, 0x39},
|
||||
{0x16, 0x45},
|
||||
{0x17, 0x54},
|
||||
{0x18, 0x69},
|
||||
{0x19, 0x7d},
|
||||
{0x1a, 0x8f},
|
||||
{0x1b, 0x9d},
|
||||
{0x1c, 0xa9},
|
||||
{0x1d, 0xbd},
|
||||
{0x1e, 0xcd},
|
||||
{0x1f, 0xd9},
|
||||
{0x20, 0xe3},
|
||||
{0x21, 0xea},
|
||||
{0x22, 0xef},
|
||||
{0x23, 0xf5},
|
||||
{0x24, 0xf9},
|
||||
{0x25, 0xff},
|
||||
|
||||
/////auto gamma/////
|
||||
{0xfe, 0x02},
|
||||
{0x26, 0x0f},
|
||||
{0x27, 0x14},
|
||||
{0x28, 0x19},
|
||||
{0x29, 0x1e},
|
||||
{0x2a, 0x27},
|
||||
{0x2b, 0x33},
|
||||
{0x2c, 0x3b},
|
||||
{0x2d, 0x45},
|
||||
{0x2e, 0x59},
|
||||
{0x2f, 0x69},
|
||||
{0x30, 0x7c},
|
||||
{0x31, 0x89},
|
||||
{0x32, 0x98},
|
||||
{0x33, 0xae},
|
||||
{0x34, 0xc0},
|
||||
{0x35, 0xcf},
|
||||
{0x36, 0xda},
|
||||
{0x37, 0xe2},
|
||||
{0x38, 0xe9},
|
||||
{0x39, 0xf3},
|
||||
{0x3a, 0xf9},
|
||||
{0x3b, 0xff},
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/////////// YCP ///////////////////////
|
||||
///////////////////////////////////////////////
|
||||
{0xfe, 0x02},
|
||||
{0xd1, 0x30}, //32 //
|
||||
{0xd2, 0x30}, //32 //
|
||||
{0xd3, 0x45},
|
||||
{0xdd, 0x14}, //edge sa
|
||||
{0xde, 0x86}, //asde auto gray
|
||||
{0xed, 0x01}, //
|
||||
{0xee, 0x28},
|
||||
{0xef, 0x30},
|
||||
{0xd8, 0xd8}, //autogray protecy
|
||||
|
||||
////////////////////////////
|
||||
//////// LSC 0.8///////////////
|
||||
////////////////////////////
|
||||
{0xfe, 0x01},
|
||||
{0xa1, 0x80}, // center_row
|
||||
{0xa2, 0x80}, // center_col
|
||||
{0xa4, 0x00}, // sign of b1
|
||||
{0xa5, 0x00}, // sign of b1
|
||||
{0xa6, 0x70}, // sign of b4
|
||||
{0xa7, 0x00}, // sign of b4
|
||||
{0xa8, 0x77}, // sign of b22
|
||||
{0xa9, 0x77}, // sign of b22
|
||||
{0xaa, 0x1f}, // Q1_b1 of R
|
||||
{0xab, 0x0d}, // Q1_b1 of G
|
||||
{0xac, 0x19}, // Q1_b1 of B
|
||||
{0xad, 0x24}, // Q2_b1 of R
|
||||
{0xae, 0x0e}, // Q2_b1 of G
|
||||
{0xaf, 0x1d}, // Q2_b1 of B
|
||||
{0xb0, 0x12}, // Q3_b1 of R
|
||||
{0xb1, 0x0c}, // Q3_b1 of G
|
||||
{0xb2, 0x06}, // Q3_b1 of B
|
||||
{0xb3, 0x13}, // Q4_b1 of R
|
||||
{0xb4, 0x10}, // Q4_b1 of G
|
||||
{0xb5, 0x0c}, // Q4_b1 of B
|
||||
{0xb6, 0x6a}, // right_b2 of R
|
||||
{0xb7, 0x46}, // right_b2 of G
|
||||
{0xb8, 0x40}, // right_b2 of B
|
||||
{0xb9, 0x0b}, // right_b4 of R
|
||||
{0xba, 0x04}, // right_b4 of G
|
||||
{0xbb, 0x00}, // right_b4 of B
|
||||
{0xbc, 0x53}, // left_b2 of R
|
||||
{0xbd, 0x37}, // left_b2 of G
|
||||
{0xbe, 0x2d}, // left_b2 of B
|
||||
{0xbf, 0x0a}, // left_b4 of R
|
||||
{0xc0, 0x0a}, // left_b4 of G
|
||||
{0xc1, 0x14}, // left_b4 of B
|
||||
{0xc2, 0x34}, // up_b2 of R
|
||||
{0xc3, 0x22}, // up_b2 of G
|
||||
{0xc4, 0x18}, // up_b2 of B
|
||||
{0xc5, 0x23}, // up_b4 of R
|
||||
{0xc6, 0x0f}, // up_b4 of G
|
||||
{0xc7, 0x3c}, // up_b4 of B
|
||||
{0xc8, 0x20}, // down_b2 of R
|
||||
{0xc9, 0x1f}, // down_b2 of G
|
||||
{0xca, 0x17}, // down_b2 of B
|
||||
{0xcb, 0x2d}, // down_b4 of R
|
||||
{0xcc, 0x12}, // down_b4 of G
|
||||
{0xcd, 0x20}, // down_b4 of B
|
||||
{0xd0, 0x61}, // right_up_b22 of R
|
||||
{0xd1, 0x2f}, // right_up_b22 of G
|
||||
{0xd2, 0x39}, // right_up_b22 of B
|
||||
{0xd3, 0x45}, // right_down_b22 of R
|
||||
{0xd4, 0x2c}, // right_down_b22 of G
|
||||
{0xd5, 0x21}, // right_down_b22 of B
|
||||
{0xd6, 0x64}, // left_up_b22 of R
|
||||
{0xd7, 0x2d}, // left_up_b22 of G
|
||||
{0xd8, 0x30}, // left_up_b22 of B
|
||||
{0xd9, 0x42}, // left_down_b22 of R
|
||||
{0xda, 0x27}, // left_down_b22 of G
|
||||
{0xdb, 0x13}, // left_down_b22 of B
|
||||
{0xfe, 0x00},
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
///////////// AWB ////////////////////////
|
||||
/////////////////////////////////////////////////
|
||||
{0xfe, 0x01},
|
||||
|
||||
{0x4f, 0x00},
|
||||
{0x4f, 0x00},
|
||||
{0x4b, 0x01},
|
||||
{0x4f, 0x00},
|
||||
|
||||
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0x6f},
|
||||
{0x4e, 0x02},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0x70},
|
||||
|
||||
{0x4e, 0x02},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0x8f},
|
||||
{0x4e, 0x02},
|
||||
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0x90},
|
||||
{0x4e, 0x02}, //light
|
||||
|
||||
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xed},
|
||||
{0x4e, 0x33}, //light
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xcd},
|
||||
{0x4e, 0x33}, //light
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xec},
|
||||
{0x4e, 0x03}, //light
|
||||
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0x6c},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0x6d},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0x6e},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0x8c},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0x8d},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0x8e},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xab},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xac},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xad},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xae},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xcb},
|
||||
{0x4e, 0x03},
|
||||
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xcc},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xce},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xeb},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xec},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xee},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0x0c},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0x0d},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xea},
|
||||
{0x4e, 0x03},
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xaf},
|
||||
{0x4e, 0x03}, //dark
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xcf},
|
||||
{0x4e, 0x03}, //dark
|
||||
|
||||
{0x4c, 0x01},
|
||||
{0x4d, 0xca},
|
||||
{0x4e, 0x04}, //light
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0x0b},
|
||||
{0x4e, 0x05}, //light
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0xc8},
|
||||
{0x4e, 0x06}, //light 100lux
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0xa8},
|
||||
|
||||
{0x4e, 0x06}, //light
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0xa9},
|
||||
{0x4e, 0x06}, //light
|
||||
|
||||
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0x89},
|
||||
{0x4e, 0x06}, //400lux
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0x69},
|
||||
{0x4e, 0x06}, //f12
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0x6a},
|
||||
{0x4e, 0x06}, //f12
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0xc7},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0xe7},
|
||||
{0x4e, 0x07}, //100lux
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x07},
|
||||
{0x4e, 0x07}, //light
|
||||
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0xe8},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x02},
|
||||
{0x4d, 0xe9},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x08},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x09},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x27},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x28},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x29},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x47},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x48},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x49},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x67},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x68},
|
||||
{0x4e, 0x07},
|
||||
{0x4c, 0x03},
|
||||
{0x4d, 0x69},
|
||||
{0x4e, 0x07},
|
||||
|
||||
{0x4f, 0x01},
|
||||
{0xfe, 0x01},
|
||||
{0x50, 0x80}, //AWB_PRE_mode
|
||||
{0x51, 0xa8}, //AWB_pre_THD_min[7:0]
|
||||
{0x52, 0x57}, //AWB_pre_THD_min[15:8] Dominiate luma 0.25=639c 0.22=57a8
|
||||
{0x53, 0x38}, //AWB_pre_THD_min_MIX[7:0]
|
||||
{0x54, 0xc7}, //AWB_pre_THD_min_MIX[15:8] Mix luma 0.5
|
||||
|
||||
{0x56, 0x0e}, //AWB_tone mode
|
||||
{0x58, 0x08}, //AWB_C_num_sel,AWB_D_num_sel
|
||||
{0x5b, 0x00}, //AWB_mix_mode
|
||||
|
||||
{0x5c, 0x74}, //green_num0[7:0]
|
||||
{0x5d, 0x8b}, //green_num0[15:8] 0.35
|
||||
|
||||
{0x61, 0xd3}, //R2G_stand0
|
||||
{0x62, 0xb5}, //B2G_stand0
|
||||
{0x63, 0x00}, //88//a4 //AWB gray mode [7]enable
|
||||
{0x65, 0x04}, //AWB margin
|
||||
|
||||
{0x67, 0xb2}, //R2G_stand3[7:0] FF/CWF
|
||||
{0x68, 0xac}, //B2G_stand3[7:0]
|
||||
{0x69, 0x00}, //R2G_stand4[9:8] B2G_stand4[9:8] R2G_stand3[9:8] B2G_stand3[9:8]
|
||||
{0x6a, 0xb2}, //R2G_stand4[7:0] TL84/TL84&CWF
|
||||
{0x6b, 0xac}, //B2G_stand4[7:0]
|
||||
{0x6c, 0xb2}, //R2G_stand5[7:0] A
|
||||
{0x6d, 0xac}, //B2G_stand5[7:0]
|
||||
{0x6e, 0x40}, //AWB_skin_weight R2G_stand5[9:8] B2G_stand5[9:8]
|
||||
{0x6f, 0x18}, //AWB_indoor_THD (0x21=17 caculate)
|
||||
{0x73, 0x00}, //AWB_indoor_mode
|
||||
|
||||
{0x70, 0x10}, //AWB low luma TH
|
||||
{0x71, 0xe8}, //AWB outdoor TH
|
||||
{0x72, 0xc0}, //outdoor mode
|
||||
{0x74, 0x01}, //[2:0]AWB skip mode 2x2,4x4,4x8,8x8
|
||||
{0x75, 0x01}, //[1:0]AWB_every_N
|
||||
{0x7f, 0x08}, //[3]gray world frame start
|
||||
|
||||
{0x76, 0x70}, //R limit
|
||||
{0x77, 0x58}, //G limit
|
||||
{0x78, 0xa0}, //d8 //B limit
|
||||
|
||||
{0xfe, 0x00},
|
||||
//
|
||||
//////////////////////////////////////////
|
||||
/////////// CC ////////////////////////
|
||||
//////////////////////////////////////////
|
||||
{0xfe, 0x02},
|
||||
|
||||
{0xc0, 0x01}, //[5:4] CC mode [0]CCT enable
|
||||
|
||||
{0xC1, 0x50}, //D50/D65
|
||||
{0xc2, 0xF9},
|
||||
{0xc3, 0x00}, //0
|
||||
{0xc4, 0xe8}, //e0
|
||||
{0xc5, 0x48},
|
||||
{0xc6, 0xf0},
|
||||
|
||||
|
||||
{0xC7, 0x50},
|
||||
{0xc8, 0xf2},
|
||||
{0xc9, 0x00},
|
||||
{0xcA, 0xE0},
|
||||
{0xcB, 0x45},
|
||||
{0xcC, 0xec},
|
||||
|
||||
{0xCd, 0x45},
|
||||
{0xce, 0xf0},
|
||||
{0xcf, 0x00},
|
||||
{0xe3, 0xf0},
|
||||
{0xe4, 0x45},
|
||||
{0xe5, 0xe8},
|
||||
|
||||
|
||||
{0xfe, 0x00},
|
||||
|
||||
{0xf2, 0x0f},
|
||||
|
||||
|
||||
//////////////frame rate 50Hz
|
||||
{0xfe, 0x00},
|
||||
|
||||
{0xf7, 0x1d},
|
||||
{0xf8, 0x84},
|
||||
{0xfa, 0x00},
|
||||
|
||||
{0x05, 0x01}, //hb
|
||||
{0x06, 0x3b},
|
||||
{0x07, 0x01}, //Vb
|
||||
{0x08, 0x0b},
|
||||
|
||||
{0xfe, 0x01},
|
||||
{0x25, 0x01},
|
||||
{0x26, 0x32}, //step
|
||||
{0x27, 0x03}, //8.15fps
|
||||
{0x28, 0x96},
|
||||
{0x29, 0x03}, //8.15fps
|
||||
{0x2a, 0x96},
|
||||
{0x2b, 0x03}, //8.15fps
|
||||
{0x2c, 0x96},
|
||||
{0x2d, 0x04}, //8.15fps
|
||||
{0x2e, 0x62},
|
||||
{0x3c, 0x00},
|
||||
{0xfe, 0x00},
|
||||
|
||||
/////////dark sun//////
|
||||
{0xfe, 0x00},
|
||||
{0x18, 0x22},
|
||||
{0xfe, 0x02},
|
||||
{0x40, 0xbf},
|
||||
{0x46, 0xcf},
|
||||
{0xfe, 0x00},
|
||||
|
||||
{0xfe, 0x00},
|
||||
|
||||
{0xf7, 0x1d},
|
||||
{0xf8, 0x84},
|
||||
{0xfa, 0x10},
|
||||
|
||||
{0x05, 0x01}, //hb
|
||||
{0x06, 0x18},
|
||||
{0x07, 0x00}, //Vb
|
||||
{0x08, 0x2e},
|
||||
|
||||
{0xfe, 0x01},
|
||||
{0x25, 0x00},
|
||||
{0x26, 0xa2}, //step
|
||||
{0x27, 0x01},
|
||||
{0x28, 0xe6},
|
||||
{0x29, 0x01},
|
||||
{0x2a, 0xe6},
|
||||
{0x2b, 0x01},
|
||||
{0x2c, 0xe6},
|
||||
{0x2d, 0x04}, // AEC_exp_level4[12:8]
|
||||
{0x2e, 0x62}, // AEC_exp_level4[7:0]
|
||||
{0x3c, 0x00},
|
||||
{0xfe, 0x00},
|
||||
|
||||
{0x09, 0x01}, //row start
|
||||
{0x0a, 0xd0}, //
|
||||
{0x0b, 0x02}, //col start
|
||||
{0x0c, 0x70},
|
||||
{0x0d, 0x01}, //height
|
||||
{0x0e, 0x00},
|
||||
{0x0f, 0x01}, //width
|
||||
{0x10, 0x50},
|
||||
|
||||
{0x90, 0x01}, //crop
|
||||
{0x91, 0x00},
|
||||
{0x92, 0x00},
|
||||
{0x93, 0x00},
|
||||
{0x94, 0x00},
|
||||
{0x95, 0x00},
|
||||
{0x96, 0xf0},
|
||||
{0x97, 0x01},
|
||||
{0x98, 0x40},
|
||||
|
||||
|
||||
{REGLIST_TAIL, 0x00},
|
||||
};
|
||||
@@ -11,6 +11,24 @@
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
int NT99141_init(sensor_t *sensor);
|
||||
/**
|
||||
* @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 nt99141_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int nt99141_init(sensor_t *sensor);
|
||||
|
||||
#endif // __NT99141_H__
|
||||
|
||||
@@ -9,5 +9,24 @@
|
||||
#ifndef __OV2640_H__
|
||||
#define __OV2640_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 ov2640_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int ov2640_init(sensor_t *sensor);
|
||||
|
||||
#endif // __OV2640_H__
|
||||
|
||||
@@ -120,8 +120,8 @@ typedef enum {
|
||||
#define HSTOP 0x18
|
||||
#define VSTART 0x19
|
||||
#define VSTOP 0x1A
|
||||
#define MIDH 0x1C
|
||||
#define MIDL 0x1D
|
||||
#define REG_MIDH 0x1C
|
||||
#define REG_MIDL 0x1D
|
||||
#define AEW 0x24
|
||||
#define AEB 0x25
|
||||
#define VV 0x26
|
||||
|
||||
@@ -11,6 +11,24 @@
|
||||
|
||||
#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 ov3660_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int ov3660_init(sensor_t *sensor);
|
||||
|
||||
#endif // __OV3660_H__
|
||||
|
||||
@@ -4,6 +4,24 @@
|
||||
|
||||
#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 ov5640_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int ov5640_init(sensor_t *sensor);
|
||||
|
||||
#endif // __OV5640_H__
|
||||
|
||||
@@ -10,5 +10,24 @@
|
||||
#define __OV7670_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 ov7670_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int ov7670_init(sensor_t *sensor);
|
||||
|
||||
#endif // __OV7670_H__
|
||||
|
||||
@@ -10,5 +10,24 @@
|
||||
#define __OV7725_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 ov7725_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int ov7725_init(sensor_t *sensor);
|
||||
|
||||
#endif // __OV7725_H__
|
||||
|
||||
528
code/components/esp32-camera-master/target/esp32/ll_cam.c
Normal file
528
code/components/esp32-camera-master/target/esp32/ll_cam.c
Normal file
@@ -0,0 +1,528 @@
|
||||
// Copyright 2010-2020 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 <string.h>
|
||||
#include "soc/i2s_struct.h"
|
||||
#include "esp_idf_version.h"
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR > 1)
|
||||
#include "hal/gpio_ll.h"
|
||||
#else
|
||||
#include "soc/gpio_periph.h"
|
||||
#define esp_rom_delay_us ets_delay_us
|
||||
static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
|
||||
{
|
||||
if (gpio_num < 32) {
|
||||
return (hw->in >> gpio_num) & 0x1;
|
||||
} else {
|
||||
return (hw->in1.data >> (gpio_num - 32)) & 0x1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#include "ll_cam.h"
|
||||
#include "xclk.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#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)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "esp32 ll_cam";
|
||||
|
||||
#define I2S_ISR_ENABLE(i) {I2S0.int_clr.i = 1;I2S0.int_ena.i = 1;}
|
||||
#define I2S_ISR_DISABLE(i) {I2S0.int_ena.i = 0;I2S0.int_clr.i = 1;}
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t sample2:8;
|
||||
uint32_t unused2:8;
|
||||
uint32_t sample1:8;
|
||||
uint32_t unused1:8;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma_elem_t;
|
||||
|
||||
typedef enum {
|
||||
/* camera sends byte sequence: s1, s2, s3, s4, ...
|
||||
* fifo receives: 00 s1 00 s2, 00 s2 00 s3, 00 s3 00 s4, ...
|
||||
*/
|
||||
SM_0A0B_0B0C = 0,
|
||||
/* camera sends byte sequence: s1, s2, s3, s4, ...
|
||||
* fifo receives: 00 s1 00 s2, 00 s3 00 s4, ...
|
||||
*/
|
||||
SM_0A0B_0C0D = 1,
|
||||
/* camera sends byte sequence: s1, s2, s3, s4, ...
|
||||
* fifo receives: 00 s1 00 00, 00 s2 00 00, 00 s3 00 00, ...
|
||||
*/
|
||||
SM_0A00_0B00 = 3,
|
||||
} i2s_sampling_mode_t;
|
||||
|
||||
typedef size_t (*dma_filter_t)(uint8_t* dst, const uint8_t* src, size_t len);
|
||||
|
||||
static i2s_sampling_mode_t sampling_mode = SM_0A00_0B00;
|
||||
|
||||
static size_t ll_cam_bytes_per_sample(i2s_sampling_mode_t mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case SM_0A00_0B00:
|
||||
return 4;
|
||||
case SM_0A0B_0B0C:
|
||||
return 4;
|
||||
case SM_0A0B_0C0D:
|
||||
return 2;
|
||||
default:
|
||||
assert(0 && "invalid sampling mode");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t IRAM_ATTR ll_cam_dma_filter_jpeg(uint8_t* dst, const uint8_t* src, size_t len)
|
||||
{
|
||||
const dma_elem_t* dma_el = (const dma_elem_t*)src;
|
||||
size_t elements = len / sizeof(dma_elem_t);
|
||||
size_t end = elements / 4;
|
||||
// manually unrolling 4 iterations of the loop here
|
||||
for (size_t i = 0; i < end; ++i) {
|
||||
dst[0] = dma_el[0].sample1;
|
||||
dst[1] = dma_el[1].sample1;
|
||||
dst[2] = dma_el[2].sample1;
|
||||
dst[3] = dma_el[3].sample1;
|
||||
dma_el += 4;
|
||||
dst += 4;
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
static size_t IRAM_ATTR ll_cam_dma_filter_grayscale(uint8_t* dst, const uint8_t* src, size_t len)
|
||||
{
|
||||
const dma_elem_t* dma_el = (const dma_elem_t*)src;
|
||||
size_t elements = len / sizeof(dma_elem_t);
|
||||
size_t end = elements / 4;
|
||||
for (size_t i = 0; i < end; ++i) {
|
||||
// manually unrolling 4 iterations of the loop here
|
||||
dst[0] = dma_el[0].sample1;
|
||||
dst[1] = dma_el[1].sample1;
|
||||
dst[2] = dma_el[2].sample1;
|
||||
dst[3] = dma_el[3].sample1;
|
||||
dma_el += 4;
|
||||
dst += 4;
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
static size_t IRAM_ATTR ll_cam_dma_filter_grayscale_highspeed(uint8_t* dst, const uint8_t* src, size_t len)
|
||||
{
|
||||
const dma_elem_t* dma_el = (const dma_elem_t*)src;
|
||||
size_t elements = len / sizeof(dma_elem_t);
|
||||
size_t end = elements / 8;
|
||||
for (size_t i = 0; i < end; ++i) {
|
||||
// manually unrolling 4 iterations of the loop here
|
||||
dst[0] = dma_el[0].sample1;
|
||||
dst[1] = dma_el[2].sample1;
|
||||
dst[2] = dma_el[4].sample1;
|
||||
dst[3] = dma_el[6].sample1;
|
||||
dma_el += 8;
|
||||
dst += 4;
|
||||
}
|
||||
// the final sample of a line in SM_0A0B_0B0C sampling mode needs special handling
|
||||
if ((elements & 0x7) != 0) {
|
||||
dst[0] = dma_el[0].sample1;
|
||||
dst[1] = dma_el[2].sample1;
|
||||
elements += 1;
|
||||
}
|
||||
return elements / 2;
|
||||
}
|
||||
|
||||
static size_t IRAM_ATTR ll_cam_dma_filter_yuyv(uint8_t* dst, const uint8_t* src, size_t len)
|
||||
{
|
||||
const dma_elem_t* dma_el = (const dma_elem_t*)src;
|
||||
size_t elements = len / sizeof(dma_elem_t);
|
||||
size_t end = elements / 4;
|
||||
for (size_t i = 0; i < end; ++i) {
|
||||
dst[0] = dma_el[0].sample1;//y0
|
||||
dst[1] = dma_el[0].sample2;//u
|
||||
dst[2] = dma_el[1].sample1;//y1
|
||||
dst[3] = dma_el[1].sample2;//v
|
||||
|
||||
dst[4] = dma_el[2].sample1;//y0
|
||||
dst[5] = dma_el[2].sample2;//u
|
||||
dst[6] = dma_el[3].sample1;//y1
|
||||
dst[7] = dma_el[3].sample2;//v
|
||||
dma_el += 4;
|
||||
dst += 8;
|
||||
}
|
||||
return elements * 2;
|
||||
}
|
||||
|
||||
static size_t IRAM_ATTR ll_cam_dma_filter_yuyv_highspeed(uint8_t* dst, const uint8_t* src, size_t len)
|
||||
{
|
||||
const dma_elem_t* dma_el = (const dma_elem_t*)src;
|
||||
size_t elements = len / sizeof(dma_elem_t);
|
||||
size_t end = elements / 8;
|
||||
for (size_t i = 0; i < end; ++i) {
|
||||
dst[0] = dma_el[0].sample1;//y0
|
||||
dst[1] = dma_el[1].sample1;//u
|
||||
dst[2] = dma_el[2].sample1;//y1
|
||||
dst[3] = dma_el[3].sample1;//v
|
||||
|
||||
dst[4] = dma_el[4].sample1;//y0
|
||||
dst[5] = dma_el[5].sample1;//u
|
||||
dst[6] = dma_el[6].sample1;//y1
|
||||
dst[7] = dma_el[7].sample1;//v
|
||||
dma_el += 8;
|
||||
dst += 8;
|
||||
}
|
||||
if ((elements & 0x7) != 0) {
|
||||
dst[0] = dma_el[0].sample1;//y0
|
||||
dst[1] = dma_el[1].sample1;//u
|
||||
dst[2] = dma_el[2].sample1;//y1
|
||||
dst[3] = dma_el[2].sample2;//v
|
||||
elements += 4;
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR ll_cam_vsync_isr(void *arg)
|
||||
{
|
||||
//DBG_PIN_SET(1);
|
||||
cam_obj_t *cam = (cam_obj_t *)arg;
|
||||
BaseType_t HPTaskAwoken = pdFALSE;
|
||||
// filter
|
||||
ets_delay_us(1);
|
||||
if (gpio_ll_get_level(&GPIO, cam->vsync_pin) == !cam->vsync_invert) {
|
||||
ll_cam_send_event(cam, CAM_VSYNC_EVENT, &HPTaskAwoken);
|
||||
if (HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
//DBG_PIN_SET(0);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
{
|
||||
//DBG_PIN_SET(1);
|
||||
cam_obj_t *cam = (cam_obj_t *)arg;
|
||||
BaseType_t HPTaskAwoken = pdFALSE;
|
||||
|
||||
typeof(I2S0.int_st) status = I2S0.int_st;
|
||||
if (status.val == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
I2S0.int_clr.val = status.val;
|
||||
|
||||
if (status.in_suc_eof) {
|
||||
ll_cam_send_event(cam, CAM_IN_SUC_EOF_EVENT, &HPTaskAwoken);
|
||||
}
|
||||
if (HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
//DBG_PIN_SET(0);
|
||||
}
|
||||
|
||||
bool ll_cam_stop(cam_obj_t *cam)
|
||||
{
|
||||
I2S0.conf.rx_start = 0;
|
||||
I2S_ISR_DISABLE(in_suc_eof);
|
||||
I2S0.in_link.stop = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_deinit(cam_obj_t *cam)
|
||||
{
|
||||
gpio_isr_handler_remove(cam->vsync_pin);
|
||||
|
||||
if (cam->cam_intr_handle) {
|
||||
esp_intr_free(cam->cam_intr_handle);
|
||||
cam->cam_intr_handle = NULL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool ll_cam_start(cam_obj_t *cam, int frame_pos)
|
||||
{
|
||||
I2S0.conf.rx_start = 0;
|
||||
|
||||
I2S_ISR_ENABLE(in_suc_eof);
|
||||
|
||||
I2S0.conf.rx_reset = 1;
|
||||
I2S0.conf.rx_reset = 0;
|
||||
I2S0.conf.rx_fifo_reset = 1;
|
||||
I2S0.conf.rx_fifo_reset = 0;
|
||||
I2S0.lc_conf.in_rst = 1;
|
||||
I2S0.lc_conf.in_rst = 0;
|
||||
I2S0.lc_conf.ahbm_fifo_rst = 1;
|
||||
I2S0.lc_conf.ahbm_fifo_rst = 0;
|
||||
I2S0.lc_conf.ahbm_rst = 1;
|
||||
I2S0.lc_conf.ahbm_rst = 0;
|
||||
|
||||
I2S0.rx_eof_num = cam->dma_half_buffer_size / sizeof(dma_elem_t);
|
||||
I2S0.in_link.addr = ((uint32_t)&cam->dma[0]) & 0xfffff;
|
||||
|
||||
I2S0.in_link.start = 1;
|
||||
I2S0.conf.rx_start = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
|
||||
{
|
||||
// Enable and configure I2S peripheral
|
||||
periph_module_enable(PERIPH_I2S0_MODULE);
|
||||
|
||||
I2S0.conf.rx_reset = 1;
|
||||
I2S0.conf.rx_reset = 0;
|
||||
I2S0.conf.rx_fifo_reset = 1;
|
||||
I2S0.conf.rx_fifo_reset = 0;
|
||||
I2S0.lc_conf.in_rst = 1;
|
||||
I2S0.lc_conf.in_rst = 0;
|
||||
I2S0.lc_conf.ahbm_fifo_rst = 1;
|
||||
I2S0.lc_conf.ahbm_fifo_rst = 0;
|
||||
I2S0.lc_conf.ahbm_rst = 1;
|
||||
I2S0.lc_conf.ahbm_rst = 0;
|
||||
|
||||
I2S0.conf.rx_slave_mod = 1;
|
||||
I2S0.conf.rx_right_first = 0;
|
||||
I2S0.conf.rx_msb_right = 0;
|
||||
I2S0.conf.rx_msb_shift = 0;
|
||||
I2S0.conf.rx_mono = 0;
|
||||
I2S0.conf.rx_short_sync = 0;
|
||||
|
||||
I2S0.conf2.lcd_en = 1;
|
||||
I2S0.conf2.camera_en = 1;
|
||||
|
||||
// Configure clock divider
|
||||
I2S0.clkm_conf.clkm_div_a = 0;
|
||||
I2S0.clkm_conf.clkm_div_b = 0;
|
||||
I2S0.clkm_conf.clkm_div_num = 2;
|
||||
|
||||
I2S0.fifo_conf.dscr_en = 1;
|
||||
I2S0.fifo_conf.rx_fifo_mod = sampling_mode;
|
||||
I2S0.fifo_conf.rx_fifo_mod_force_en = 1;
|
||||
|
||||
I2S0.conf_chan.rx_chan_mod = 1;
|
||||
I2S0.sample_rate_conf.rx_bits_mod = 0;
|
||||
I2S0.timing.val = 0;
|
||||
I2S0.timing.rx_dsync_sw = 1;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
|
||||
{
|
||||
if (en) {
|
||||
gpio_intr_enable(cam->vsync_pin);
|
||||
} else {
|
||||
gpio_intr_disable(cam->vsync_pin);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config)
|
||||
{
|
||||
gpio_config_t io_conf = {0};
|
||||
io_conf.intr_type = cam->vsync_invert ? GPIO_PIN_INTR_NEGEDGE : GPIO_PIN_INTR_POSEDGE;
|
||||
io_conf.pin_bit_mask = 1ULL << config->pin_vsync;
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pull_up_en = 1;
|
||||
io_conf.pull_down_en = 0;
|
||||
gpio_config(&io_conf);
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM);
|
||||
gpio_isr_handler_add(config->pin_vsync, ll_cam_vsync_isr, cam);
|
||||
gpio_intr_disable(config->pin_vsync);
|
||||
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_pclk], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_pclk, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(config->pin_pclk, GPIO_FLOATING);
|
||||
gpio_matrix_in(config->pin_pclk, I2S0I_WS_IN_IDX, false);
|
||||
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_vsync], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_vsync, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(config->pin_vsync, GPIO_FLOATING);
|
||||
gpio_matrix_in(config->pin_vsync, I2S0I_V_SYNC_IDX, false);
|
||||
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_href], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_href, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(config->pin_href, GPIO_FLOATING);
|
||||
gpio_matrix_in(config->pin_href, I2S0I_H_SYNC_IDX, false);
|
||||
|
||||
int data_pins[8] = {
|
||||
config->pin_d0, config->pin_d1, config->pin_d2, config->pin_d3, config->pin_d4, config->pin_d5, config->pin_d6, config->pin_d7,
|
||||
};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[data_pins[i]], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(data_pins[i], GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(data_pins[i], GPIO_FLOATING);
|
||||
gpio_matrix_in(data_pins[i], I2S0I_DATA_IN0_IDX + i, false);
|
||||
}
|
||||
|
||||
gpio_matrix_in(0x38, I2S0I_H_ENABLE_IDX, false);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_init_isr(cam_obj_t *cam)
|
||||
{
|
||||
return esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, ll_cam_dma_isr, cam, &cam->cam_intr_handle);
|
||||
}
|
||||
|
||||
void ll_cam_do_vsync(cam_obj_t *cam)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ll_cam_get_dma_align(cam_obj_t *cam)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
|
||||
size_t dma_half_buffer_max = CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX / 2 / cam->dma_bytes_per_item;
|
||||
size_t dma_buffer_max = 2 * dma_half_buffer_max;
|
||||
size_t node_max = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE / cam->dma_bytes_per_item;
|
||||
|
||||
size_t line_width = cam->width * cam->in_bytes_per_pixel;
|
||||
size_t image_size = cam->height * line_width;
|
||||
if (image_size > (4 * 1024 * 1024) || (line_width > dma_half_buffer_max)) {
|
||||
ESP_LOGE(TAG, "Resolution too high");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t node_size = node_max;
|
||||
size_t nodes_per_line = 1;
|
||||
size_t lines_per_node = 1;
|
||||
size_t lines_per_half_buffer = 1;
|
||||
size_t dma_half_buffer_min = node_max;
|
||||
size_t dma_half_buffer = dma_half_buffer_max;
|
||||
size_t dma_buffer_size = dma_buffer_max;
|
||||
|
||||
// Calculate DMA Node Size so that it's divisable by or divisor of the line width
|
||||
if(line_width >= node_max){
|
||||
// One or more nodes will be requied for one line
|
||||
for(size_t i = node_max; i > 0; i=i-1){
|
||||
if ((line_width % i) == 0) {
|
||||
node_size = i;
|
||||
nodes_per_line = line_width / node_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// One or more lines can fit into one node
|
||||
for(size_t i = node_max; i > 0; i=i-1){
|
||||
if ((i % line_width) == 0) {
|
||||
node_size = i;
|
||||
lines_per_node = node_size / line_width;
|
||||
while((cam->height % lines_per_node) != 0){
|
||||
lines_per_node = lines_per_node - 1;
|
||||
node_size = lines_per_node * line_width;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Calculate minimum EOF size = max(mode_size, line_size)
|
||||
dma_half_buffer_min = node_size * nodes_per_line;
|
||||
// Calculate max EOF size divisable by node size
|
||||
dma_half_buffer = (dma_half_buffer_max / dma_half_buffer_min) * dma_half_buffer_min;
|
||||
// Adjust EOF size so that height will be divisable by the number of lines in each EOF
|
||||
lines_per_half_buffer = dma_half_buffer / line_width;
|
||||
while((cam->height % lines_per_half_buffer) != 0){
|
||||
dma_half_buffer = dma_half_buffer - dma_half_buffer_min;
|
||||
lines_per_half_buffer = dma_half_buffer / line_width;
|
||||
}
|
||||
// Calculate DMA size
|
||||
dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
|
||||
|
||||
ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u, dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u, image_size: %u",
|
||||
node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node, dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item, image_size);
|
||||
|
||||
cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
|
||||
cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
|
||||
cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
|
||||
cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool ll_cam_dma_sizes(cam_obj_t *cam)
|
||||
{
|
||||
cam->dma_bytes_per_item = ll_cam_bytes_per_sample(sampling_mode);
|
||||
if (cam->jpeg_mode) {
|
||||
cam->dma_half_buffer_cnt = 8;
|
||||
cam->dma_node_buffer_size = 2048;
|
||||
cam->dma_half_buffer_size = cam->dma_node_buffer_size * 2;
|
||||
cam->dma_buffer_size = cam->dma_half_buffer_cnt * cam->dma_half_buffer_size;
|
||||
} else {
|
||||
return ll_cam_calc_rgb_dma(cam);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static dma_filter_t dma_filter = ll_cam_dma_filter_jpeg;
|
||||
|
||||
size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len)
|
||||
{
|
||||
//DBG_PIN_SET(1);
|
||||
size_t r = dma_filter(out, in, len);
|
||||
//DBG_PIN_SET(0);
|
||||
return r;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid)
|
||||
{
|
||||
if (pix_format == PIXFORMAT_GRAYSCALE) {
|
||||
if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID) {
|
||||
if (xclk_freq_hz > 10000000) {
|
||||
sampling_mode = SM_0A00_0B00;
|
||||
dma_filter = ll_cam_dma_filter_yuyv_highspeed;
|
||||
} else {
|
||||
sampling_mode = SM_0A0B_0C0D;
|
||||
dma_filter = ll_cam_dma_filter_yuyv;
|
||||
}
|
||||
cam->in_bytes_per_pixel = 1; // camera sends Y8
|
||||
} else {
|
||||
if (xclk_freq_hz > 10000000 && sensor_pid != OV7725_PID) {
|
||||
sampling_mode = SM_0A00_0B00;
|
||||
dma_filter = ll_cam_dma_filter_grayscale_highspeed;
|
||||
} else {
|
||||
sampling_mode = SM_0A0B_0C0D;
|
||||
dma_filter = ll_cam_dma_filter_grayscale;
|
||||
}
|
||||
cam->in_bytes_per_pixel = 2; // camera sends YU/YV
|
||||
}
|
||||
cam->fb_bytes_per_pixel = 1; // frame buffer stores Y8
|
||||
} else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
|
||||
if (xclk_freq_hz > 10000000 && sensor_pid != OV7725_PID) {
|
||||
if (sensor_pid == OV7670_PID) {
|
||||
sampling_mode = SM_0A0B_0B0C;
|
||||
} else {
|
||||
sampling_mode = SM_0A00_0B00;
|
||||
}
|
||||
dma_filter = ll_cam_dma_filter_yuyv_highspeed;
|
||||
} else {
|
||||
sampling_mode = SM_0A0B_0C0D;
|
||||
dma_filter = ll_cam_dma_filter_yuyv;
|
||||
}
|
||||
cam->in_bytes_per_pixel = 2; // camera sends YU/YV
|
||||
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
|
||||
} else if (pix_format == PIXFORMAT_JPEG) {
|
||||
cam->in_bytes_per_pixel = 1;
|
||||
cam->fb_bytes_per_pixel = 1;
|
||||
dma_filter = ll_cam_dma_filter_jpeg;
|
||||
sampling_mode = SM_0A00_0B00;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Requested format is not supported");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
I2S0.fifo_conf.rx_fifo_mod = sampling_mode;
|
||||
return ESP_OK;
|
||||
}
|
||||
408
code/components/esp32-camera-master/target/esp32s2/ll_cam.c
Normal file
408
code/components/esp32-camera-master/target/esp32s2/ll_cam.c
Normal file
@@ -0,0 +1,408 @@
|
||||
// Copyright 2010-2020 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 <string.h>
|
||||
#include "soc/system_reg.h"
|
||||
#include "soc/i2s_struct.h"
|
||||
#include "hal/gpio_ll.h"
|
||||
#include "ll_cam.h"
|
||||
#include "xclk.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#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)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "s2 ll_cam";
|
||||
|
||||
#define I2S_ISR_ENABLE(i) {I2S0.int_clr.i = 1;I2S0.int_ena.i = 1;}
|
||||
#define I2S_ISR_DISABLE(i) {I2S0.int_ena.i = 0;I2S0.int_clr.i = 1;}
|
||||
|
||||
static void IRAM_ATTR ll_cam_vsync_isr(void *arg)
|
||||
{
|
||||
//DBG_PIN_SET(1);
|
||||
cam_obj_t *cam = (cam_obj_t *)arg;
|
||||
BaseType_t HPTaskAwoken = pdFALSE;
|
||||
// filter
|
||||
ets_delay_us(1);
|
||||
if (gpio_ll_get_level(&GPIO, cam->vsync_pin) == !cam->vsync_invert) {
|
||||
ll_cam_send_event(cam, CAM_VSYNC_EVENT, &HPTaskAwoken);
|
||||
}
|
||||
|
||||
if (HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
//DBG_PIN_SET(0);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
{
|
||||
cam_obj_t *cam = (cam_obj_t *)arg;
|
||||
BaseType_t HPTaskAwoken = pdFALSE;
|
||||
|
||||
typeof(I2S0.int_st) status = I2S0.int_st;
|
||||
if (status.val == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
I2S0.int_clr.val = status.val;
|
||||
|
||||
if (status.in_suc_eof) {
|
||||
ll_cam_send_event(cam, CAM_IN_SUC_EOF_EVENT, &HPTaskAwoken);
|
||||
}
|
||||
|
||||
if (HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
bool ll_cam_stop(cam_obj_t *cam)
|
||||
{
|
||||
I2S0.conf.rx_start = 0;
|
||||
|
||||
if (cam->jpeg_mode || !cam->psram_mode) {
|
||||
I2S_ISR_DISABLE(in_suc_eof);
|
||||
}
|
||||
|
||||
I2S0.in_link.stop = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_deinit(cam_obj_t *cam)
|
||||
{
|
||||
gpio_isr_handler_remove(cam->vsync_pin);
|
||||
|
||||
if (cam->cam_intr_handle) {
|
||||
esp_intr_free(cam->cam_intr_handle);
|
||||
cam->cam_intr_handle = NULL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool ll_cam_start(cam_obj_t *cam, int frame_pos)
|
||||
{
|
||||
I2S0.conf.rx_start = 0;
|
||||
|
||||
if (cam->jpeg_mode || !cam->psram_mode) {
|
||||
I2S_ISR_ENABLE(in_suc_eof);
|
||||
}
|
||||
|
||||
I2S0.conf.rx_reset = 1;
|
||||
I2S0.conf.rx_reset = 0;
|
||||
I2S0.conf.rx_fifo_reset = 1;
|
||||
I2S0.conf.rx_fifo_reset = 0;
|
||||
I2S0.lc_conf.in_rst = 1;
|
||||
I2S0.lc_conf.in_rst = 0;
|
||||
I2S0.lc_conf.ahbm_fifo_rst = 1;
|
||||
I2S0.lc_conf.ahbm_fifo_rst = 0;
|
||||
I2S0.lc_conf.ahbm_rst = 1;
|
||||
I2S0.lc_conf.ahbm_rst = 0;
|
||||
|
||||
I2S0.rx_eof_num = cam->dma_half_buffer_size; // Ping pong operation
|
||||
if (!cam->psram_mode) {
|
||||
I2S0.in_link.addr = ((uint32_t)&cam->dma[0]) & 0xfffff;
|
||||
} else {
|
||||
I2S0.in_link.addr = ((uint32_t)&cam->frames[frame_pos].dma[0]) & 0xfffff;
|
||||
}
|
||||
|
||||
I2S0.in_link.start = 1;
|
||||
I2S0.conf.rx_start = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
|
||||
{
|
||||
esp_err_t err = camera_enable_out_clock(config);
|
||||
if(err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
periph_module_enable(PERIPH_I2S0_MODULE);
|
||||
// Configure the clock
|
||||
I2S0.clkm_conf.clkm_div_num = 2; // 160MHz / 2 = 80MHz
|
||||
I2S0.clkm_conf.clkm_div_b = 0;
|
||||
I2S0.clkm_conf.clkm_div_a = 0;
|
||||
I2S0.clkm_conf.clk_sel = 2;
|
||||
I2S0.clkm_conf.clk_en = 1;
|
||||
|
||||
|
||||
I2S0.conf.val = 0;
|
||||
I2S0.fifo_conf.val = 0;
|
||||
I2S0.fifo_conf.dscr_en = 1;
|
||||
|
||||
I2S0.lc_conf.ahbm_fifo_rst = 1;
|
||||
I2S0.lc_conf.ahbm_fifo_rst = 0;
|
||||
I2S0.lc_conf.ahbm_rst = 1;
|
||||
I2S0.lc_conf.ahbm_rst = 0;
|
||||
I2S0.lc_conf.check_owner = 0;
|
||||
//I2S0.lc_conf.indscr_burst_en = 1;
|
||||
//I2S0.lc_conf.ext_mem_bk_size = 0; // DMA access external memory block size. 0: 16 bytes, 1: 32 bytes, 2:64 bytes, 3:reserved
|
||||
|
||||
I2S0.timing.val = 0;
|
||||
|
||||
I2S0.int_ena.val = 0;
|
||||
I2S0.int_clr.val = ~0;
|
||||
|
||||
I2S0.conf2.lcd_en = 1;
|
||||
I2S0.conf2.camera_en = 1;
|
||||
|
||||
// Configuration data format
|
||||
I2S0.conf.rx_slave_mod = 1;
|
||||
I2S0.conf.rx_right_first = 0;
|
||||
I2S0.conf.rx_msb_right = cam->swap_data;
|
||||
I2S0.conf.rx_short_sync = 0;
|
||||
I2S0.conf.rx_mono = 0;
|
||||
I2S0.conf.rx_msb_shift = 0;
|
||||
I2S0.conf.rx_dma_equal = 1;
|
||||
|
||||
// Configure sampling rate
|
||||
I2S0.sample_rate_conf.rx_bck_div_num = 1;
|
||||
I2S0.sample_rate_conf.rx_bits_mod = 8;
|
||||
|
||||
I2S0.conf1.rx_pcm_bypass = 1;
|
||||
|
||||
I2S0.conf2.i_v_sync_filter_en = 1;
|
||||
I2S0.conf2.i_v_sync_filter_thres = 4;
|
||||
I2S0.conf2.cam_sync_fifo_reset = 1;
|
||||
I2S0.conf2.cam_sync_fifo_reset = 0;
|
||||
|
||||
I2S0.conf_chan.rx_chan_mod = 1;
|
||||
|
||||
I2S0.fifo_conf.rx_fifo_mod_force_en = 1;
|
||||
I2S0.fifo_conf.rx_data_num = 32;
|
||||
I2S0.fifo_conf.rx_fifo_mod = 2;
|
||||
|
||||
I2S0.lc_conf.in_rst = 1;
|
||||
I2S0.lc_conf.in_rst = 0;
|
||||
|
||||
I2S0.conf.rx_start = 1;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
|
||||
{
|
||||
if (en) {
|
||||
gpio_intr_enable(cam->vsync_pin);
|
||||
} else {
|
||||
gpio_intr_disable(cam->vsync_pin);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config)
|
||||
{
|
||||
gpio_config_t io_conf = {0};
|
||||
io_conf.intr_type = cam->vsync_invert ? GPIO_PIN_INTR_NEGEDGE : GPIO_PIN_INTR_POSEDGE;
|
||||
io_conf.pin_bit_mask = 1ULL << config->pin_vsync;
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pull_up_en = 1;
|
||||
io_conf.pull_down_en = 0;
|
||||
gpio_config(&io_conf);
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM);
|
||||
gpio_isr_handler_add(config->pin_vsync, ll_cam_vsync_isr, cam);
|
||||
gpio_intr_disable(config->pin_vsync);
|
||||
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_pclk], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_pclk, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(config->pin_pclk, GPIO_FLOATING);
|
||||
gpio_matrix_in(config->pin_pclk, I2S0I_WS_IN_IDX, false);
|
||||
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_vsync], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_vsync, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(config->pin_vsync, GPIO_FLOATING);
|
||||
gpio_matrix_in(config->pin_vsync, I2S0I_V_SYNC_IDX, cam->vsync_invert);
|
||||
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_href], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_href, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(config->pin_href, GPIO_FLOATING);
|
||||
gpio_matrix_in(config->pin_href, I2S0I_H_SYNC_IDX, false);
|
||||
|
||||
int data_pins[8] = {
|
||||
config->pin_d0, config->pin_d1, config->pin_d2, config->pin_d3, config->pin_d4, config->pin_d5, config->pin_d6, config->pin_d7,
|
||||
};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[data_pins[i]], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(data_pins[i], GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(data_pins[i], GPIO_FLOATING);
|
||||
// High bit alignment, IN16 is always the highest bit
|
||||
// fifo accesses data by bit, when rx_bits_mod is 8, the data needs to be aligned by 8 bits
|
||||
gpio_matrix_in(data_pins[i], I2S0I_DATA_IN0_IDX + 8 + i, false);
|
||||
}
|
||||
|
||||
gpio_matrix_in(0x38, I2S0I_H_ENABLE_IDX, false);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_init_isr(cam_obj_t *cam)
|
||||
{
|
||||
return esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, ll_cam_dma_isr, cam, &cam->cam_intr_handle);
|
||||
}
|
||||
|
||||
void ll_cam_do_vsync(cam_obj_t *cam)
|
||||
{
|
||||
ll_cam_vsync_intr_enable(cam, false);
|
||||
gpio_matrix_in(cam->vsync_pin, I2S0I_V_SYNC_IDX, !cam->vsync_invert);
|
||||
ets_delay_us(10);
|
||||
gpio_matrix_in(cam->vsync_pin, I2S0I_V_SYNC_IDX, cam->vsync_invert);
|
||||
ll_cam_vsync_intr_enable(cam, true);
|
||||
}
|
||||
|
||||
uint8_t ll_cam_get_dma_align(cam_obj_t *cam)
|
||||
{
|
||||
return 64;//16 << I2S0.lc_conf.ext_mem_bk_size;
|
||||
}
|
||||
|
||||
static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
|
||||
size_t node_max = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE / cam->dma_bytes_per_item;
|
||||
size_t line_width = cam->width * cam->in_bytes_per_pixel;
|
||||
size_t node_size = node_max;
|
||||
size_t nodes_per_line = 1;
|
||||
size_t lines_per_node = 1;
|
||||
|
||||
// Calculate DMA Node Size so that it's divisable by or divisor of the line width
|
||||
if(line_width >= node_max){
|
||||
// One or more nodes will be requied for one line
|
||||
for(size_t i = node_max; i > 0; i=i-1){
|
||||
if ((line_width % i) == 0) {
|
||||
node_size = i;
|
||||
nodes_per_line = line_width / node_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// One or more lines can fit into one node
|
||||
for(size_t i = node_max; i > 0; i=i-1){
|
||||
if ((i % line_width) == 0) {
|
||||
node_size = i;
|
||||
lines_per_node = node_size / line_width;
|
||||
while((cam->height % lines_per_node) != 0){
|
||||
lines_per_node = lines_per_node - 1;
|
||||
node_size = lines_per_node * line_width;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u",
|
||||
node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node);
|
||||
|
||||
cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
|
||||
|
||||
if (cam->psram_mode) {
|
||||
cam->dma_buffer_size = cam->recv_size * cam->dma_bytes_per_item;
|
||||
cam->dma_half_buffer_cnt = 2;
|
||||
cam->dma_half_buffer_size = cam->dma_buffer_size / cam->dma_half_buffer_cnt;
|
||||
} else {
|
||||
size_t dma_half_buffer_max = CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX / 2 / cam->dma_bytes_per_item;
|
||||
if (line_width > dma_half_buffer_max) {
|
||||
ESP_LOGE(TAG, "Resolution too high");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate minimum EOF size = max(mode_size, line_size)
|
||||
size_t dma_half_buffer_min = node_size * nodes_per_line;
|
||||
|
||||
// Calculate max EOF size divisable by node size
|
||||
size_t dma_half_buffer = (dma_half_buffer_max / dma_half_buffer_min) * dma_half_buffer_min;
|
||||
|
||||
// Adjust EOF size so that height will be divisable by the number of lines in each EOF
|
||||
size_t lines_per_half_buffer = dma_half_buffer / line_width;
|
||||
while((cam->height % lines_per_half_buffer) != 0){
|
||||
dma_half_buffer = dma_half_buffer - dma_half_buffer_min;
|
||||
lines_per_half_buffer = dma_half_buffer / line_width;
|
||||
}
|
||||
|
||||
// Calculate DMA size
|
||||
size_t dma_buffer_max = 2 * dma_half_buffer_max;
|
||||
size_t dma_buffer_size = dma_buffer_max;
|
||||
dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
|
||||
|
||||
ESP_LOGI(TAG, "dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u",
|
||||
dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item);
|
||||
|
||||
cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
|
||||
cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
|
||||
cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool ll_cam_dma_sizes(cam_obj_t *cam)
|
||||
{
|
||||
cam->dma_bytes_per_item = 1;
|
||||
if (cam->jpeg_mode) {
|
||||
if (cam->psram_mode) {
|
||||
cam->dma_buffer_size = cam->recv_size;
|
||||
cam->dma_half_buffer_size = 1024;
|
||||
cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
|
||||
cam->dma_node_buffer_size = cam->dma_half_buffer_size;
|
||||
} else {
|
||||
cam->dma_half_buffer_cnt = 16;
|
||||
cam->dma_buffer_size = cam->dma_half_buffer_cnt * 1024;
|
||||
cam->dma_half_buffer_size = cam->dma_buffer_size / cam->dma_half_buffer_cnt;
|
||||
cam->dma_node_buffer_size = cam->dma_half_buffer_size;
|
||||
}
|
||||
} else {
|
||||
return ll_cam_calc_rgb_dma(cam);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len)
|
||||
{
|
||||
// YUV to Grayscale
|
||||
if (cam->in_bytes_per_pixel == 2 && cam->fb_bytes_per_pixel == 1) {
|
||||
size_t end = len / 8;
|
||||
for (size_t i = 0; i < end; ++i) {
|
||||
out[0] = in[0];
|
||||
out[1] = in[2];
|
||||
out[2] = in[4];
|
||||
out[3] = in[6];
|
||||
out += 4;
|
||||
in += 8;
|
||||
}
|
||||
return len / 2;
|
||||
}
|
||||
|
||||
// just memcpy
|
||||
memcpy(out, in, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid)
|
||||
{
|
||||
if (pix_format == PIXFORMAT_GRAYSCALE) {
|
||||
if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID) {
|
||||
cam->in_bytes_per_pixel = 1; // camera sends Y8
|
||||
} else {
|
||||
cam->in_bytes_per_pixel = 2; // camera sends YU/YV
|
||||
}
|
||||
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
|
||||
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
|
||||
} else if (pix_format == PIXFORMAT_JPEG) {
|
||||
cam->in_bytes_per_pixel = 1;
|
||||
cam->fb_bytes_per_pixel = 1;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Requested format is not supported");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ TJpgDec - Tiny JPEG Decompressor include file (C)ChaN, 2012
|
||||
/----------------------------------------------------------------------------*/
|
||||
#ifndef _TJPGDEC
|
||||
#define _TJPGDEC
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* System Configurations */
|
||||
|
||||
#define JD_SZBUF 512 /* Size of stream input buffer */
|
||||
#define JD_FORMAT 0 /* Output pixel format 0:RGB888 (3 BYTE/pix), 1:RGB565 (1 WORD/pix) */
|
||||
#define JD_USE_SCALE 1 /* Use descaling feature for output */
|
||||
#define JD_TBLCLIP 1 /* Use table for saturation (might be a bit faster but increases 1K bytes of code size) */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* These types must be 16-bit, 32-bit or larger integer */
|
||||
typedef int INT;
|
||||
typedef unsigned int UINT;
|
||||
|
||||
/* These types must be 8-bit integer */
|
||||
typedef char CHAR;
|
||||
typedef unsigned char UCHAR;
|
||||
typedef unsigned char BYTE;
|
||||
|
||||
/* These types must be 16-bit integer */
|
||||
typedef short SHORT;
|
||||
typedef unsigned short USHORT;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned short WCHAR;
|
||||
|
||||
/* These types must be 32-bit integer */
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef unsigned long DWORD;
|
||||
|
||||
|
||||
/* Error code */
|
||||
typedef enum {
|
||||
JDR_OK = 0, /* 0: Succeeded */
|
||||
JDR_INTR, /* 1: Interrupted by output function */
|
||||
JDR_INP, /* 2: Device error or wrong termination of input stream */
|
||||
JDR_MEM1, /* 3: Insufficient memory pool for the image */
|
||||
JDR_MEM2, /* 4: Insufficient stream input buffer */
|
||||
JDR_PAR, /* 5: Parameter error */
|
||||
JDR_FMT1, /* 6: Data format error (may be damaged data) */
|
||||
JDR_FMT2, /* 7: Right format but not supported */
|
||||
JDR_FMT3 /* 8: Not supported JPEG standard */
|
||||
} JRESULT;
|
||||
|
||||
|
||||
|
||||
/* Rectangular structure */
|
||||
typedef struct {
|
||||
WORD left, right, top, bottom;
|
||||
} JRECT;
|
||||
|
||||
|
||||
|
||||
/* Decompressor object structure */
|
||||
typedef struct JDEC JDEC;
|
||||
struct JDEC {
|
||||
UINT dctr; /* Number of bytes available in the input buffer */
|
||||
BYTE* dptr; /* Current data read ptr */
|
||||
BYTE* inbuf; /* Bit stream input buffer */
|
||||
BYTE dmsk; /* Current bit in the current read byte */
|
||||
BYTE scale; /* Output scaling ratio */
|
||||
BYTE msx, msy; /* MCU size in unit of block (width, height) */
|
||||
BYTE qtid[3]; /* Quantization table ID of each component */
|
||||
SHORT dcv[3]; /* Previous DC element of each component */
|
||||
WORD nrst; /* Restart inverval */
|
||||
UINT width, height; /* Size of the input image (pixel) */
|
||||
BYTE* huffbits[2][2]; /* Huffman bit distribution tables [id][dcac] */
|
||||
WORD* huffcode[2][2]; /* Huffman code word tables [id][dcac] */
|
||||
BYTE* huffdata[2][2]; /* Huffman decoded data tables [id][dcac] */
|
||||
LONG* qttbl[4]; /* Dequaitizer tables [id] */
|
||||
void* workbuf; /* Working buffer for IDCT and RGB output */
|
||||
BYTE* mcubuf; /* Working buffer for the MCU */
|
||||
void* pool; /* Pointer to available memory pool */
|
||||
UINT sz_pool; /* Size of momory pool (bytes available) */
|
||||
UINT (*infunc)(JDEC*, BYTE*, UINT);/* Pointer to jpeg stream input function */
|
||||
void* device; /* Pointer to I/O device identifiler for the session */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* TJpgDec API functions */
|
||||
JRESULT jd_prepare (JDEC*, UINT(*)(JDEC*,BYTE*,UINT), void*, UINT, void*);
|
||||
JRESULT jd_decomp (JDEC*, UINT(*)(JDEC*,void*,JRECT*), BYTE);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TJPGDEC */
|
||||
970
code/components/esp32-camera-master/target/esp32s2/tjpgd.c
Normal file
970
code/components/esp32-camera-master/target/esp32s2/tjpgd.c
Normal file
@@ -0,0 +1,970 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ TJpgDec - Tiny JPEG Decompressor R0.01b (C)ChaN, 2012
|
||||
/-----------------------------------------------------------------------------/
|
||||
/ The TJpgDec is a generic JPEG decompressor module for tiny embedded systems.
|
||||
/ This is a free software that opened for education, research and commercial
|
||||
/ developments under license policy of following terms.
|
||||
/
|
||||
/ Copyright (C) 2012, ChaN, all right reserved.
|
||||
/
|
||||
/ * The TJpgDec module is a free software and there is NO WARRANTY.
|
||||
/ * No restriction on use. You can use, modify and redistribute it for
|
||||
/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
|
||||
/ * Redistributions of source code must retain the above copyright notice.
|
||||
/
|
||||
/-----------------------------------------------------------------------------/
|
||||
/ Oct 04,'11 R0.01 First release.
|
||||
/ Feb 19,'12 R0.01a Fixed decompression fails when scan starts with an escape seq.
|
||||
/ Sep 03,'12 R0.01b Added JD_TBLCLIP option.
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
#include "tjpgd.h"
|
||||
|
||||
#define SUPPORT_JPEG 1
|
||||
|
||||
#ifdef SUPPORT_JPEG
|
||||
/*-----------------------------------------------*/
|
||||
/* Zigzag-order to raster-order conversion table */
|
||||
/*-----------------------------------------------*/
|
||||
|
||||
#define ZIG(n) Zig[n]
|
||||
|
||||
static
|
||||
const BYTE Zig[64] = { /* Zigzag-order to raster-order conversion table */
|
||||
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
|
||||
12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
|
||||
35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
|
||||
58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------*/
|
||||
/* Input scale factor of Arai algorithm */
|
||||
/* (scaled up 16 bits for fixed point operations) */
|
||||
/*-------------------------------------------------*/
|
||||
|
||||
#define IPSF(n) Ipsf[n]
|
||||
|
||||
static
|
||||
const WORD Ipsf[64] = { /* See also aa_idct.png */
|
||||
(WORD)(1.00000*8192), (WORD)(1.38704*8192), (WORD)(1.30656*8192), (WORD)(1.17588*8192), (WORD)(1.00000*8192), (WORD)(0.78570*8192), (WORD)(0.54120*8192), (WORD)(0.27590*8192),
|
||||
(WORD)(1.38704*8192), (WORD)(1.92388*8192), (WORD)(1.81226*8192), (WORD)(1.63099*8192), (WORD)(1.38704*8192), (WORD)(1.08979*8192), (WORD)(0.75066*8192), (WORD)(0.38268*8192),
|
||||
(WORD)(1.30656*8192), (WORD)(1.81226*8192), (WORD)(1.70711*8192), (WORD)(1.53636*8192), (WORD)(1.30656*8192), (WORD)(1.02656*8192), (WORD)(0.70711*8192), (WORD)(0.36048*8192),
|
||||
(WORD)(1.17588*8192), (WORD)(1.63099*8192), (WORD)(1.53636*8192), (WORD)(1.38268*8192), (WORD)(1.17588*8192), (WORD)(0.92388*8192), (WORD)(0.63638*8192), (WORD)(0.32442*8192),
|
||||
(WORD)(1.00000*8192), (WORD)(1.38704*8192), (WORD)(1.30656*8192), (WORD)(1.17588*8192), (WORD)(1.00000*8192), (WORD)(0.78570*8192), (WORD)(0.54120*8192), (WORD)(0.27590*8192),
|
||||
(WORD)(0.78570*8192), (WORD)(1.08979*8192), (WORD)(1.02656*8192), (WORD)(0.92388*8192), (WORD)(0.78570*8192), (WORD)(0.61732*8192), (WORD)(0.42522*8192), (WORD)(0.21677*8192),
|
||||
(WORD)(0.54120*8192), (WORD)(0.75066*8192), (WORD)(0.70711*8192), (WORD)(0.63638*8192), (WORD)(0.54120*8192), (WORD)(0.42522*8192), (WORD)(0.29290*8192), (WORD)(0.14932*8192),
|
||||
(WORD)(0.27590*8192), (WORD)(0.38268*8192), (WORD)(0.36048*8192), (WORD)(0.32442*8192), (WORD)(0.27590*8192), (WORD)(0.21678*8192), (WORD)(0.14932*8192), (WORD)(0.07612*8192)
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------*/
|
||||
/* Conversion table for fast clipping process */
|
||||
/*---------------------------------------------*/
|
||||
|
||||
#if JD_TBLCLIP
|
||||
|
||||
#define BYTECLIP(v) Clip8[(UINT)(v) & 0x3FF]
|
||||
|
||||
static
|
||||
const BYTE Clip8[1024] = {
|
||||
/* 0..255 */
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
|
||||
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
|
||||
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
|
||||
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
|
||||
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
|
||||
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
|
||||
/* 256..511 */
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
/* -512..-257 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* -256..-1 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
#else /* JD_TBLCLIP */
|
||||
|
||||
inline
|
||||
BYTE BYTECLIP (
|
||||
INT val
|
||||
)
|
||||
{
|
||||
if (val < 0) val = 0;
|
||||
if (val > 255) val = 255;
|
||||
|
||||
return (BYTE)val;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Allocate a memory block from memory pool */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
void* alloc_pool ( /* Pointer to allocated memory block (NULL:no memory available) */
|
||||
JDEC* jd, /* Pointer to the decompressor object */
|
||||
UINT nd /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
char *rp = 0;
|
||||
|
||||
|
||||
nd = (nd + 3) & ~3; /* Align block size to the word boundary */
|
||||
|
||||
if (jd->sz_pool >= nd) {
|
||||
jd->sz_pool -= nd;
|
||||
rp = (char*)jd->pool; /* Get start of available memory pool */
|
||||
jd->pool = (void*)(rp + nd); /* Allocate requierd bytes */
|
||||
}
|
||||
|
||||
return (void*)rp; /* Return allocated memory block (NULL:no memory to allocate) */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Create de-quantization and prescaling tables with a DQT segment */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
UINT create_qt_tbl ( /* 0:OK, !0:Failed */
|
||||
JDEC* jd, /* Pointer to the decompressor object */
|
||||
const BYTE* data, /* Pointer to the quantizer tables */
|
||||
UINT ndata /* Size of input data */
|
||||
)
|
||||
{
|
||||
UINT i;
|
||||
BYTE d, z;
|
||||
LONG *pb;
|
||||
|
||||
|
||||
while (ndata) { /* Process all tables in the segment */
|
||||
if (ndata < 65) return JDR_FMT1; /* Err: table size is unaligned */
|
||||
ndata -= 65;
|
||||
d = *data++; /* Get table property */
|
||||
if (d & 0xF0) return JDR_FMT1; /* Err: not 8-bit resolution */
|
||||
i = d & 3; /* Get table ID */
|
||||
pb = alloc_pool(jd, 64 * sizeof (LONG));/* Allocate a memory block for the table */
|
||||
if (!pb) return JDR_MEM1; /* Err: not enough memory */
|
||||
jd->qttbl[i] = pb; /* Register the table */
|
||||
for (i = 0; i < 64; i++) { /* Load the table */
|
||||
z = ZIG(i); /* Zigzag-order to raster-order conversion */
|
||||
pb[z] = (LONG)((DWORD)*data++ * IPSF(z)); /* Apply scale factor of Arai algorithm to the de-quantizers */
|
||||
}
|
||||
}
|
||||
|
||||
return JDR_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Create huffman code tables with a DHT segment */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
UINT create_huffman_tbl ( /* 0:OK, !0:Failed */
|
||||
JDEC* jd, /* Pointer to the decompressor object */
|
||||
const BYTE* data, /* Pointer to the packed huffman tables */
|
||||
UINT ndata /* Size of input data */
|
||||
)
|
||||
{
|
||||
UINT i, j, b, np, cls, num;
|
||||
BYTE d, *pb, *pd;
|
||||
WORD hc, *ph;
|
||||
|
||||
|
||||
while (ndata) { /* Process all tables in the segment */
|
||||
if (ndata < 17) return JDR_FMT1; /* Err: wrong data size */
|
||||
ndata -= 17;
|
||||
d = *data++; /* Get table number and class */
|
||||
cls = (d >> 4); num = d & 0x0F; /* class = dc(0)/ac(1), table number = 0/1 */
|
||||
if (d & 0xEE) return JDR_FMT1; /* Err: invalid class/number */
|
||||
pb = alloc_pool(jd, 16); /* Allocate a memory block for the bit distribution table */
|
||||
if (!pb) return JDR_MEM1; /* Err: not enough memory */
|
||||
jd->huffbits[num][cls] = pb;
|
||||
for (np = i = 0; i < 16; i++) { /* Load number of patterns for 1 to 16-bit code */
|
||||
pb[i] = b = *data++;
|
||||
np += b; /* Get sum of code words for each code */
|
||||
}
|
||||
|
||||
ph = alloc_pool(jd, np * sizeof (WORD));/* Allocate a memory block for the code word table */
|
||||
if (!ph) return JDR_MEM1; /* Err: not enough memory */
|
||||
jd->huffcode[num][cls] = ph;
|
||||
hc = 0;
|
||||
for (j = i = 0; i < 16; i++) { /* Re-build huffman code word table */
|
||||
b = pb[i];
|
||||
while (b--) ph[j++] = hc++;
|
||||
hc <<= 1;
|
||||
}
|
||||
|
||||
if (ndata < np) return JDR_FMT1; /* Err: wrong data size */
|
||||
ndata -= np;
|
||||
pd = alloc_pool(jd, np); /* Allocate a memory block for the decoded data */
|
||||
if (!pd) return JDR_MEM1; /* Err: not enough memory */
|
||||
jd->huffdata[num][cls] = pd;
|
||||
for (i = 0; i < np; i++) { /* Load decoded data corresponds to each code ward */
|
||||
d = *data++;
|
||||
if (!cls && d > 11) return JDR_FMT1;
|
||||
*pd++ = d;
|
||||
}
|
||||
}
|
||||
|
||||
return JDR_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Extract N bits from input stream */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
INT bitext ( /* >=0: extracted data, <0: error code */
|
||||
JDEC* jd, /* Pointer to the decompressor object */
|
||||
UINT nbit /* Number of bits to extract (1 to 11) */
|
||||
)
|
||||
{
|
||||
BYTE msk, s, *dp;
|
||||
UINT dc, v, f;
|
||||
|
||||
|
||||
msk = jd->dmsk; dc = jd->dctr; dp = jd->dptr; /* Bit mask, number of data available, read ptr */
|
||||
s = *dp; v = f = 0;
|
||||
do {
|
||||
if (!msk) { /* Next byte? */
|
||||
if (!dc) { /* No input data is available, re-fill input buffer */
|
||||
dp = jd->inbuf; /* Top of input buffer */
|
||||
dc = jd->infunc(jd, dp, JD_SZBUF);
|
||||
if (!dc) return 0 - (INT)JDR_INP; /* Err: read error or wrong stream termination */
|
||||
} else {
|
||||
dp++; /* Next data ptr */
|
||||
}
|
||||
dc--; /* Decrement number of available bytes */
|
||||
if (f) { /* In flag sequence? */
|
||||
f = 0; /* Exit flag sequence */
|
||||
if (*dp != 0) return 0 - (INT)JDR_FMT1; /* Err: unexpected flag is detected (may be collapted data) */
|
||||
*dp = s = 0xFF; /* The flag is a data 0xFF */
|
||||
} else {
|
||||
s = *dp; /* Get next data byte */
|
||||
if (s == 0xFF) { /* Is start of flag sequence? */
|
||||
f = 1; continue; /* Enter flag sequence */
|
||||
}
|
||||
}
|
||||
msk = 0x80; /* Read from MSB */
|
||||
}
|
||||
v <<= 1; /* Get a bit */
|
||||
if (s & msk) v++;
|
||||
msk >>= 1;
|
||||
nbit--;
|
||||
} while (nbit);
|
||||
jd->dmsk = msk; jd->dctr = dc; jd->dptr = dp;
|
||||
|
||||
return (INT)v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Extract a huffman decoded data from input stream */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
INT huffext ( /* >=0: decoded data, <0: error code */
|
||||
JDEC* jd, /* Pointer to the decompressor object */
|
||||
const BYTE* hbits, /* Pointer to the bit distribution table */
|
||||
const WORD* hcode, /* Pointer to the code word table */
|
||||
const BYTE* hdata /* Pointer to the data table */
|
||||
)
|
||||
{
|
||||
BYTE msk, s, *dp;
|
||||
UINT dc, v, f, bl, nd;
|
||||
|
||||
|
||||
msk = jd->dmsk; dc = jd->dctr; dp = jd->dptr; /* Bit mask, number of data available, read ptr */
|
||||
s = *dp; v = f = 0;
|
||||
bl = 16; /* Max code length */
|
||||
do {
|
||||
if (!msk) { /* Next byte? */
|
||||
if (!dc) { /* No input data is available, re-fill input buffer */
|
||||
dp = jd->inbuf; /* Top of input buffer */
|
||||
dc = jd->infunc(jd, dp, JD_SZBUF);
|
||||
if (!dc) return 0 - (INT)JDR_INP; /* Err: read error or wrong stream termination */
|
||||
} else {
|
||||
dp++; /* Next data ptr */
|
||||
}
|
||||
dc--; /* Decrement number of available bytes */
|
||||
if (f) { /* In flag sequence? */
|
||||
f = 0; /* Exit flag sequence */
|
||||
if (*dp != 0)
|
||||
return 0 - (INT)JDR_FMT1; /* Err: unexpected flag is detected (may be collapted data) */
|
||||
*dp = s = 0xFF; /* The flag is a data 0xFF */
|
||||
} else {
|
||||
s = *dp; /* Get next data byte */
|
||||
if (s == 0xFF) { /* Is start of flag sequence? */
|
||||
f = 1; continue; /* Enter flag sequence, get trailing byte */
|
||||
}
|
||||
}
|
||||
msk = 0x80; /* Read from MSB */
|
||||
}
|
||||
v <<= 1; /* Get a bit */
|
||||
if (s & msk) v++;
|
||||
msk >>= 1;
|
||||
|
||||
for (nd = *hbits++; nd; nd--) { /* Search the code word in this bit length */
|
||||
if (v == *hcode++) { /* Matched? */
|
||||
jd->dmsk = msk; jd->dctr = dc; jd->dptr = dp;
|
||||
return *hdata; /* Return the decoded data */
|
||||
}
|
||||
hdata++;
|
||||
}
|
||||
bl--;
|
||||
} while (bl);
|
||||
|
||||
return 0 - (INT)JDR_FMT1; /* Err: code not found (may be collapted data) */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Apply Inverse-DCT in Arai Algorithm (see also aa_idct.png) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
void block_idct (
|
||||
LONG* src, /* Input block data (de-quantized and pre-scaled for Arai Algorithm) */
|
||||
BYTE* dst /* Pointer to the destination to store the block as byte array */
|
||||
)
|
||||
{
|
||||
const LONG M13 = (LONG)(1.41421*4096), M2 = (LONG)(1.08239*4096), M4 = (LONG)(2.61313*4096), M5 = (LONG)(1.84776*4096);
|
||||
LONG v0, v1, v2, v3, v4, v5, v6, v7;
|
||||
LONG t10, t11, t12, t13;
|
||||
UINT i;
|
||||
|
||||
/* Process columns */
|
||||
for (i = 0; i < 8; i++) {
|
||||
v0 = src[8 * 0]; /* Get even elements */
|
||||
v1 = src[8 * 2];
|
||||
v2 = src[8 * 4];
|
||||
v3 = src[8 * 6];
|
||||
|
||||
t10 = v0 + v2; /* Process the even elements */
|
||||
t12 = v0 - v2;
|
||||
t11 = (v1 - v3) * M13 >> 12;
|
||||
v3 += v1;
|
||||
t11 -= v3;
|
||||
v0 = t10 + v3;
|
||||
v3 = t10 - v3;
|
||||
v1 = t11 + t12;
|
||||
v2 = t12 - t11;
|
||||
|
||||
v4 = src[8 * 7]; /* Get odd elements */
|
||||
v5 = src[8 * 1];
|
||||
v6 = src[8 * 5];
|
||||
v7 = src[8 * 3];
|
||||
|
||||
t10 = v5 - v4; /* Process the odd elements */
|
||||
t11 = v5 + v4;
|
||||
t12 = v6 - v7;
|
||||
v7 += v6;
|
||||
v5 = (t11 - v7) * M13 >> 12;
|
||||
v7 += t11;
|
||||
t13 = (t10 + t12) * M5 >> 12;
|
||||
v4 = t13 - (t10 * M2 >> 12);
|
||||
v6 = t13 - (t12 * M4 >> 12) - v7;
|
||||
v5 -= v6;
|
||||
v4 -= v5;
|
||||
|
||||
src[8 * 0] = v0 + v7; /* Write-back transformed values */
|
||||
src[8 * 7] = v0 - v7;
|
||||
src[8 * 1] = v1 + v6;
|
||||
src[8 * 6] = v1 - v6;
|
||||
src[8 * 2] = v2 + v5;
|
||||
src[8 * 5] = v2 - v5;
|
||||
src[8 * 3] = v3 + v4;
|
||||
src[8 * 4] = v3 - v4;
|
||||
|
||||
src++; /* Next column */
|
||||
}
|
||||
|
||||
/* Process rows */
|
||||
src -= 8;
|
||||
for (i = 0; i < 8; i++) {
|
||||
v0 = src[0] + (128L << 8); /* Get even elements (remove DC offset (-128) here) */
|
||||
v1 = src[2];
|
||||
v2 = src[4];
|
||||
v3 = src[6];
|
||||
|
||||
t10 = v0 + v2; /* Process the even elements */
|
||||
t12 = v0 - v2;
|
||||
t11 = (v1 - v3) * M13 >> 12;
|
||||
v3 += v1;
|
||||
t11 -= v3;
|
||||
v0 = t10 + v3;
|
||||
v3 = t10 - v3;
|
||||
v1 = t11 + t12;
|
||||
v2 = t12 - t11;
|
||||
|
||||
v4 = src[7]; /* Get odd elements */
|
||||
v5 = src[1];
|
||||
v6 = src[5];
|
||||
v7 = src[3];
|
||||
|
||||
t10 = v5 - v4; /* Process the odd elements */
|
||||
t11 = v5 + v4;
|
||||
t12 = v6 - v7;
|
||||
v7 += v6;
|
||||
v5 = (t11 - v7) * M13 >> 12;
|
||||
v7 += t11;
|
||||
t13 = (t10 + t12) * M5 >> 12;
|
||||
v4 = t13 - (t10 * M2 >> 12);
|
||||
v6 = t13 - (t12 * M4 >> 12) - v7;
|
||||
v5 -= v6;
|
||||
v4 -= v5;
|
||||
|
||||
dst[0] = BYTECLIP((v0 + v7) >> 8); /* Descale the transformed values 8 bits and output */
|
||||
dst[7] = BYTECLIP((v0 - v7) >> 8);
|
||||
dst[1] = BYTECLIP((v1 + v6) >> 8);
|
||||
dst[6] = BYTECLIP((v1 - v6) >> 8);
|
||||
dst[2] = BYTECLIP((v2 + v5) >> 8);
|
||||
dst[5] = BYTECLIP((v2 - v5) >> 8);
|
||||
dst[3] = BYTECLIP((v3 + v4) >> 8);
|
||||
dst[4] = BYTECLIP((v3 - v4) >> 8);
|
||||
dst += 8;
|
||||
|
||||
src += 8; /* Next row */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Load all blocks in the MCU into working buffer */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
JRESULT mcu_load (
|
||||
JDEC* jd /* Pointer to the decompressor object */
|
||||
)
|
||||
{
|
||||
LONG *tmp = (LONG*)jd->workbuf; /* Block working buffer for de-quantize and IDCT */
|
||||
UINT blk, nby, nbc, i, z, id, cmp;
|
||||
INT b, d, e;
|
||||
BYTE *bp;
|
||||
const BYTE *hb, *hd;
|
||||
const WORD *hc;
|
||||
const LONG *dqf;
|
||||
|
||||
|
||||
nby = jd->msx * jd->msy; /* Number of Y blocks (1, 2 or 4) */
|
||||
nbc = 2; /* Number of C blocks (2) */
|
||||
bp = jd->mcubuf; /* Pointer to the first block */
|
||||
|
||||
for (blk = 0; blk < nby + nbc; blk++) {
|
||||
cmp = (blk < nby) ? 0 : blk - nby + 1; /* Component number 0:Y, 1:Cb, 2:Cr */
|
||||
id = cmp ? 1 : 0; /* Huffman table ID of the component */
|
||||
|
||||
/* Extract a DC element from input stream */
|
||||
hb = jd->huffbits[id][0]; /* Huffman table for the DC element */
|
||||
hc = jd->huffcode[id][0];
|
||||
hd = jd->huffdata[id][0];
|
||||
b = huffext(jd, hb, hc, hd); /* Extract a huffman coded data (bit length) */
|
||||
if (b < 0) return 0 - b; /* Err: invalid code or input */
|
||||
d = jd->dcv[cmp]; /* DC value of previous block */
|
||||
if (b) { /* If there is any difference from previous block */
|
||||
e = bitext(jd, b); /* Extract data bits */
|
||||
if (e < 0) return 0 - e; /* Err: input */
|
||||
b = 1 << (b - 1); /* MSB position */
|
||||
if (!(e & b)) e -= (b << 1) - 1; /* Restore sign if needed */
|
||||
d += e; /* Get current value */
|
||||
jd->dcv[cmp] = (SHORT)d; /* Save current DC value for next block */
|
||||
}
|
||||
dqf = jd->qttbl[jd->qtid[cmp]]; /* De-quantizer table ID for this component */
|
||||
tmp[0] = d * dqf[0] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */
|
||||
|
||||
/* Extract following 63 AC elements from input stream */
|
||||
for (i = 1; i < 64; i++) tmp[i] = 0; /* Clear rest of elements */
|
||||
hb = jd->huffbits[id][1]; /* Huffman table for the AC elements */
|
||||
hc = jd->huffcode[id][1];
|
||||
hd = jd->huffdata[id][1];
|
||||
i = 1; /* Top of the AC elements */
|
||||
do {
|
||||
b = huffext(jd, hb, hc, hd); /* Extract a huffman coded value (zero runs and bit length) */
|
||||
if (b == 0) break; /* EOB? */
|
||||
if (b < 0) return 0 - b; /* Err: invalid code or input error */
|
||||
z = (UINT)b >> 4; /* Number of leading zero elements */
|
||||
if (z) {
|
||||
i += z; /* Skip zero elements */
|
||||
if (i >= 64) return JDR_FMT1; /* Too long zero run */
|
||||
}
|
||||
if (b &= 0x0F) { /* Bit length */
|
||||
d = bitext(jd, b); /* Extract data bits */
|
||||
if (d < 0) return 0 - d; /* Err: input device */
|
||||
b = 1 << (b - 1); /* MSB position */
|
||||
if (!(d & b)) d -= (b << 1) - 1;/* Restore negative value if needed */
|
||||
z = ZIG(i); /* Zigzag-order to raster-order converted index */
|
||||
tmp[z] = d * dqf[z] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */
|
||||
}
|
||||
} while (++i < 64); /* Next AC element */
|
||||
|
||||
if (JD_USE_SCALE && jd->scale == 3)
|
||||
*bp = (*tmp / 256) + 128; /* If scale ratio is 1/8, IDCT can be ommited and only DC element is used */
|
||||
else
|
||||
block_idct(tmp, bp); /* Apply IDCT and store the block to the MCU buffer */
|
||||
|
||||
bp += 64; /* Next block */
|
||||
}
|
||||
|
||||
return JDR_OK; /* All blocks have been loaded successfully */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Output an MCU: Convert YCrCb to RGB and output it in RGB form */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
JRESULT mcu_output (
|
||||
JDEC* jd, /* Pointer to the decompressor object */
|
||||
UINT (*outfunc)(JDEC*, void*, JRECT*), /* RGB output function */
|
||||
UINT x, /* MCU position in the image (left of the MCU) */
|
||||
UINT y /* MCU position in the image (top of the MCU) */
|
||||
)
|
||||
{
|
||||
const INT CVACC = (sizeof (INT) > 2) ? 1024 : 128;
|
||||
UINT ix, iy, mx, my, rx, ry;
|
||||
INT yy, cb, cr;
|
||||
BYTE *py, *pc, *rgb24;
|
||||
JRECT rect;
|
||||
|
||||
|
||||
mx = jd->msx * 8; my = jd->msy * 8; /* MCU size (pixel) */
|
||||
rx = (x + mx <= jd->width) ? mx : jd->width - x; /* Output rectangular size (it may be clipped at right/bottom end) */
|
||||
ry = (y + my <= jd->height) ? my : jd->height - y;
|
||||
if (JD_USE_SCALE) {
|
||||
rx >>= jd->scale; ry >>= jd->scale;
|
||||
if (!rx || !ry) return JDR_OK; /* Skip this MCU if all pixel is to be rounded off */
|
||||
x >>= jd->scale; y >>= jd->scale;
|
||||
}
|
||||
rect.left = x; rect.right = x + rx - 1; /* Rectangular area in the frame buffer */
|
||||
rect.top = y; rect.bottom = y + ry - 1;
|
||||
|
||||
|
||||
if (!JD_USE_SCALE || jd->scale != 3) { /* Not for 1/8 scaling */
|
||||
|
||||
/* Build an RGB MCU from discrete comopnents */
|
||||
rgb24 = (BYTE*)jd->workbuf;
|
||||
for (iy = 0; iy < my; iy++) {
|
||||
pc = jd->mcubuf;
|
||||
py = pc + iy * 8;
|
||||
if (my == 16) { /* Double block height? */
|
||||
pc += 64 * 4 + (iy >> 1) * 8;
|
||||
if (iy >= 8) py += 64;
|
||||
} else { /* Single block height */
|
||||
pc += mx * 8 + iy * 8;
|
||||
}
|
||||
for (ix = 0; ix < mx; ix++) {
|
||||
cb = pc[0] - 128; /* Get Cb/Cr component and restore right level */
|
||||
cr = pc[64] - 128;
|
||||
if (mx == 16) { /* Double block width? */
|
||||
if (ix == 8) py += 64 - 8; /* Jump to next block if double block heigt */
|
||||
pc += ix & 1; /* Increase chroma pointer every two pixels */
|
||||
} else { /* Single block width */
|
||||
pc++; /* Increase chroma pointer every pixel */
|
||||
}
|
||||
yy = *py++; /* Get Y component */
|
||||
|
||||
/* Convert YCbCr to RGB */
|
||||
*rgb24++ = /* R */ BYTECLIP(yy + ((INT)(1.402 * CVACC) * cr) / CVACC);
|
||||
*rgb24++ = /* G */ BYTECLIP(yy - ((INT)(0.344 * CVACC) * cb + (INT)(0.714 * CVACC) * cr) / CVACC);
|
||||
*rgb24++ = /* B */ BYTECLIP(yy + ((INT)(1.772 * CVACC) * cb) / CVACC);
|
||||
}
|
||||
}
|
||||
|
||||
/* Descale the MCU rectangular if needed */
|
||||
if (JD_USE_SCALE && jd->scale) {
|
||||
UINT x, y, r, g, b, s, w, a;
|
||||
BYTE *op;
|
||||
|
||||
/* Get averaged RGB value of each square correcponds to a pixel */
|
||||
s = jd->scale * 2; /* Bumber of shifts for averaging */
|
||||
w = 1 << jd->scale; /* Width of square */
|
||||
a = (mx - w) * 3; /* Bytes to skip for next line in the square */
|
||||
op = (BYTE*)jd->workbuf;
|
||||
for (iy = 0; iy < my; iy += w) {
|
||||
for (ix = 0; ix < mx; ix += w) {
|
||||
rgb24 = (BYTE*)jd->workbuf + (iy * mx + ix) * 3;
|
||||
r = g = b = 0;
|
||||
for (y = 0; y < w; y++) { /* Accumulate RGB value in the square */
|
||||
for (x = 0; x < w; x++) {
|
||||
r += *rgb24++;
|
||||
g += *rgb24++;
|
||||
b += *rgb24++;
|
||||
}
|
||||
rgb24 += a;
|
||||
} /* Put the averaged RGB value as a pixel */
|
||||
*op++ = (BYTE)(r >> s);
|
||||
*op++ = (BYTE)(g >> s);
|
||||
*op++ = (BYTE)(b >> s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else { /* For only 1/8 scaling (left-top pixel in each block are the DC value of the block) */
|
||||
|
||||
/* Build a 1/8 descaled RGB MCU from discrete comopnents */
|
||||
rgb24 = (BYTE*)jd->workbuf;
|
||||
pc = jd->mcubuf + mx * my;
|
||||
cb = pc[0] - 128; /* Get Cb/Cr component and restore right level */
|
||||
cr = pc[64] - 128;
|
||||
for (iy = 0; iy < my; iy += 8) {
|
||||
py = jd->mcubuf;
|
||||
if (iy == 8) py += 64 * 2;
|
||||
for (ix = 0; ix < mx; ix += 8) {
|
||||
yy = *py; /* Get Y component */
|
||||
py += 64;
|
||||
|
||||
/* Convert YCbCr to RGB */
|
||||
*rgb24++ = /* R */ BYTECLIP(yy + ((INT)(1.402 * CVACC) * cr / CVACC));
|
||||
*rgb24++ = /* G */ BYTECLIP(yy - ((INT)(0.344 * CVACC) * cb + (INT)(0.714 * CVACC) * cr) / CVACC);
|
||||
*rgb24++ = /* B */ BYTECLIP(yy + ((INT)(1.772 * CVACC) * cb / CVACC));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Squeeze up pixel table if a part of MCU is to be truncated */
|
||||
mx >>= jd->scale;
|
||||
if (rx < mx) {
|
||||
BYTE *s, *d;
|
||||
UINT x, y;
|
||||
|
||||
s = d = (BYTE*)jd->workbuf;
|
||||
for (y = 0; y < ry; y++) {
|
||||
for (x = 0; x < rx; x++) { /* Copy effective pixels */
|
||||
*d++ = *s++;
|
||||
*d++ = *s++;
|
||||
*d++ = *s++;
|
||||
}
|
||||
s += (mx - rx) * 3; /* Skip truncated pixels */
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert RGB888 to RGB565 if needed */
|
||||
if (JD_FORMAT == 1) {
|
||||
BYTE *s = (BYTE*)jd->workbuf;
|
||||
WORD w, *d = (WORD*)s;
|
||||
UINT n = rx * ry;
|
||||
|
||||
do {
|
||||
w = (*s++ & 0xF8) << 8; /* RRRRR----------- */
|
||||
w |= (*s++ & 0xFC) << 3; /* -----GGGGGG----- */
|
||||
w |= *s++ >> 3; /* -----------BBBBB */
|
||||
*d++ = w;
|
||||
} while (--n);
|
||||
}
|
||||
|
||||
/* Output the RGB rectangular */
|
||||
return outfunc(jd, jd->workbuf, &rect) ? JDR_OK : JDR_INTR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Process restart interval */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
JRESULT restart (
|
||||
JDEC* jd, /* Pointer to the decompressor object */
|
||||
WORD rstn /* Expected restert sequense number */
|
||||
)
|
||||
{
|
||||
UINT i, dc;
|
||||
WORD d;
|
||||
BYTE *dp;
|
||||
|
||||
|
||||
/* Discard padding bits and get two bytes from the input stream */
|
||||
dp = jd->dptr; dc = jd->dctr;
|
||||
d = 0;
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!dc) { /* No input data is available, re-fill input buffer */
|
||||
dp = jd->inbuf;
|
||||
dc = jd->infunc(jd, dp, JD_SZBUF);
|
||||
if (!dc) return JDR_INP;
|
||||
} else {
|
||||
dp++;
|
||||
}
|
||||
dc--;
|
||||
d = (d << 8) | *dp; /* Get a byte */
|
||||
}
|
||||
jd->dptr = dp; jd->dctr = dc; jd->dmsk = 0;
|
||||
|
||||
/* Check the marker */
|
||||
if ((d & 0xFFD8) != 0xFFD0 || (d & 7) != (rstn & 7))
|
||||
return JDR_FMT1; /* Err: expected RSTn marker is not detected (may be collapted data) */
|
||||
|
||||
/* Reset DC offset */
|
||||
jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0;
|
||||
|
||||
return JDR_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Analyze the JPEG image and Initialize decompressor object */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#define LDB_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr))<<8)|(WORD)*(BYTE*)((ptr)+1))
|
||||
|
||||
|
||||
JRESULT jd_prepare (
|
||||
JDEC* jd, /* Blank decompressor object */
|
||||
UINT (*infunc)(JDEC*, BYTE*, UINT), /* JPEG strem input function */
|
||||
void* pool, /* Working buffer for the decompression session */
|
||||
UINT sz_pool, /* Size of working buffer */
|
||||
void* dev /* I/O device identifier for the session */
|
||||
)
|
||||
{
|
||||
BYTE *seg, b;
|
||||
WORD marker;
|
||||
DWORD ofs;
|
||||
UINT n, i, j, len;
|
||||
JRESULT rc;
|
||||
|
||||
|
||||
if (!pool) return JDR_PAR;
|
||||
|
||||
jd->pool = pool; /* Work memroy */
|
||||
jd->sz_pool = sz_pool; /* Size of given work memory */
|
||||
jd->infunc = infunc; /* Stream input function */
|
||||
jd->device = dev; /* I/O device identifier */
|
||||
jd->nrst = 0; /* No restart interval (default) */
|
||||
|
||||
for (i = 0; i < 2; i++) { /* Nulls pointers */
|
||||
for (j = 0; j < 2; j++) {
|
||||
jd->huffbits[i][j] = 0;
|
||||
jd->huffcode[i][j] = 0;
|
||||
jd->huffdata[i][j] = 0;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 4; i++) jd->qttbl[i] = 0;
|
||||
|
||||
jd->inbuf = seg = alloc_pool(jd, JD_SZBUF); /* Allocate stream input buffer */
|
||||
if (!seg) return JDR_MEM1;
|
||||
|
||||
if (jd->infunc(jd, seg, 2) != 2) return JDR_INP;/* Check SOI marker */
|
||||
if (LDB_WORD(seg) != 0xFFD8) return JDR_FMT1; /* Err: SOI is not detected */
|
||||
ofs = 2;
|
||||
|
||||
for (;;) {
|
||||
/* Get a JPEG marker */
|
||||
if (jd->infunc(jd, seg, 4) != 4) return JDR_INP;
|
||||
marker = LDB_WORD(seg); /* Marker */
|
||||
len = LDB_WORD(seg + 2); /* Length field */
|
||||
if (len <= 2 || (marker >> 8) != 0xFF) return JDR_FMT1;
|
||||
len -= 2; /* Content size excluding length field */
|
||||
ofs += 4 + len; /* Number of bytes loaded */
|
||||
|
||||
switch (marker & 0xFF) {
|
||||
case 0xC0: /* SOF0 (baseline JPEG) */
|
||||
/* Load segment data */
|
||||
if (len > JD_SZBUF) return JDR_MEM2;
|
||||
if (jd->infunc(jd, seg, len) != len) return JDR_INP;
|
||||
|
||||
jd->width = LDB_WORD(seg+3); /* Image width in unit of pixel */
|
||||
jd->height = LDB_WORD(seg+1); /* Image height in unit of pixel */
|
||||
if (seg[5] != 3) return JDR_FMT3; /* Err: Supports only Y/Cb/Cr format */
|
||||
|
||||
/* Check three image components */
|
||||
for (i = 0; i < 3; i++) {
|
||||
b = seg[7 + 3 * i]; /* Get sampling factor */
|
||||
if (!i) { /* Y component */
|
||||
if (b != 0x11 && b != 0x22 && b != 0x21)/* Check sampling factor */
|
||||
return JDR_FMT3; /* Err: Supports only 4:4:4, 4:2:0 or 4:2:2 */
|
||||
jd->msx = b >> 4; jd->msy = b & 15; /* Size of MCU [blocks] */
|
||||
} else { /* Cb/Cr component */
|
||||
if (b != 0x11) return JDR_FMT3; /* Err: Sampling factor of Cr/Cb must be 1 */
|
||||
}
|
||||
b = seg[8 + 3 * i]; /* Get dequantizer table ID for this component */
|
||||
if (b > 3) return JDR_FMT3; /* Err: Invalid ID */
|
||||
jd->qtid[i] = b;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xDD: /* DRI */
|
||||
/* Load segment data */
|
||||
if (len > JD_SZBUF) return JDR_MEM2;
|
||||
if (jd->infunc(jd, seg, len) != len) return JDR_INP;
|
||||
|
||||
/* Get restart interval (MCUs) */
|
||||
jd->nrst = LDB_WORD(seg);
|
||||
break;
|
||||
|
||||
case 0xC4: /* DHT */
|
||||
/* Load segment data */
|
||||
if (len > JD_SZBUF) return JDR_MEM2;
|
||||
if (jd->infunc(jd, seg, len) != len) return JDR_INP;
|
||||
|
||||
/* Create huffman tables */
|
||||
rc = create_huffman_tbl(jd, seg, len);
|
||||
if (rc) return rc;
|
||||
break;
|
||||
|
||||
case 0xDB: /* DQT */
|
||||
/* Load segment data */
|
||||
if (len > JD_SZBUF) return JDR_MEM2;
|
||||
if (jd->infunc(jd, seg, len) != len) return JDR_INP;
|
||||
|
||||
/* Create de-quantizer tables */
|
||||
rc = create_qt_tbl(jd, seg, len);
|
||||
if (rc) return rc;
|
||||
break;
|
||||
|
||||
case 0xDA: /* SOS */
|
||||
/* Load segment data */
|
||||
if (len > JD_SZBUF) return JDR_MEM2;
|
||||
if (jd->infunc(jd, seg, len) != len) return JDR_INP;
|
||||
|
||||
if (!jd->width || !jd->height) return JDR_FMT1; /* Err: Invalid image size */
|
||||
|
||||
if (seg[0] != 3) return JDR_FMT3; /* Err: Supports only three color components format */
|
||||
|
||||
/* Check if all tables corresponding to each components have been loaded */
|
||||
for (i = 0; i < 3; i++) {
|
||||
b = seg[2 + 2 * i]; /* Get huffman table ID */
|
||||
if (b != 0x00 && b != 0x11) return JDR_FMT3; /* Err: Different table number for DC/AC element */
|
||||
b = i ? 1 : 0;
|
||||
if (!jd->huffbits[b][0] || !jd->huffbits[b][1]) /* Check huffman table for this component */
|
||||
return JDR_FMT1; /* Err: Huffman table not loaded */
|
||||
if (!jd->qttbl[jd->qtid[i]]) return JDR_FMT1; /* Err: Dequantizer table not loaded */
|
||||
}
|
||||
|
||||
/* Allocate working buffer for MCU and RGB */
|
||||
n = jd->msy * jd->msx; /* Number of Y blocks in the MCU */
|
||||
if (!n) return JDR_FMT1; /* Err: SOF0 has not been loaded */
|
||||
len = n * 64 * 2 + 64; /* Allocate buffer for IDCT and RGB output */
|
||||
if (len < 256) len = 256; /* but at least 256 byte is required for IDCT */
|
||||
jd->workbuf = alloc_pool(jd, len); /* and it may occupy a part of following MCU working buffer for RGB output */
|
||||
if (!jd->workbuf) return JDR_MEM1; /* Err: not enough memory */
|
||||
jd->mcubuf = alloc_pool(jd, (n + 2) * 64); /* Allocate MCU working buffer */
|
||||
if (!jd->mcubuf) return JDR_MEM1; /* Err: not enough memory */
|
||||
|
||||
/* Pre-load the JPEG data to extract it from the bit stream */
|
||||
jd->dptr = seg; jd->dctr = 0; jd->dmsk = 0; /* Prepare to read bit stream */
|
||||
if (ofs %= JD_SZBUF) { /* Align read offset to JD_SZBUF */
|
||||
jd->dctr = jd->infunc(jd, seg + ofs, JD_SZBUF - (UINT)ofs);
|
||||
jd->dptr = seg + ofs - 1;
|
||||
}
|
||||
|
||||
return JDR_OK; /* Initialization succeeded. Ready to decompress the JPEG image. */
|
||||
|
||||
case 0xC1: /* SOF1 */
|
||||
case 0xC2: /* SOF2 */
|
||||
case 0xC3: /* SOF3 */
|
||||
case 0xC5: /* SOF5 */
|
||||
case 0xC6: /* SOF6 */
|
||||
case 0xC7: /* SOF7 */
|
||||
case 0xC9: /* SOF9 */
|
||||
case 0xCA: /* SOF10 */
|
||||
case 0xCB: /* SOF11 */
|
||||
case 0xCD: /* SOF13 */
|
||||
case 0xCE: /* SOF14 */
|
||||
case 0xCF: /* SOF15 */
|
||||
case 0xD9: /* EOI */
|
||||
return JDR_FMT3; /* Unsuppoted JPEG standard (may be progressive JPEG) */
|
||||
|
||||
default: /* Unknown segment (comment, exif or etc..) */
|
||||
/* Skip segment data */
|
||||
if (jd->infunc(jd, 0, len) != len) /* Null pointer specifies to skip bytes of stream */
|
||||
return JDR_INP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Start to decompress the JPEG picture */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
JRESULT jd_decomp (
|
||||
JDEC* jd, /* Initialized decompression object */
|
||||
UINT (*outfunc)(JDEC*, void*, JRECT*), /* RGB output function */
|
||||
BYTE scale /* Output de-scaling factor (0 to 3) */
|
||||
)
|
||||
{
|
||||
UINT x, y, mx, my;
|
||||
WORD rst, rsc;
|
||||
JRESULT rc;
|
||||
|
||||
|
||||
if (scale > (JD_USE_SCALE ? 3 : 0)) return JDR_PAR;
|
||||
jd->scale = scale;
|
||||
|
||||
mx = jd->msx * 8; my = jd->msy * 8; /* Size of the MCU (pixel) */
|
||||
|
||||
jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0; /* Initialize DC values */
|
||||
rst = rsc = 0;
|
||||
|
||||
rc = JDR_OK;
|
||||
for (y = 0; y < jd->height; y += my) { /* Vertical loop of MCUs */
|
||||
for (x = 0; x < jd->width; x += mx) { /* Horizontal loop of MCUs */
|
||||
if (jd->nrst && rst++ == jd->nrst) { /* Process restart interval if enabled */
|
||||
rc = restart(jd, rsc++);
|
||||
if (rc != JDR_OK) return rc;
|
||||
rst = 1;
|
||||
}
|
||||
rc = mcu_load(jd); /* Load an MCU (decompress huffman coded stream and apply IDCT) */
|
||||
if (rc != JDR_OK) return rc;
|
||||
rc = mcu_output(jd, outfunc, x, y); /* Output the MCU (color space conversion, scaling and output) */
|
||||
if (rc != JDR_OK) return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif//SUPPORT_JPEG
|
||||
|
||||
|
||||
457
code/components/esp32-camera-master/target/esp32s3/ll_cam.c
Normal file
457
code/components/esp32-camera-master/target/esp32s3/ll_cam.c
Normal file
@@ -0,0 +1,457 @@
|
||||
// Copyright 2010-2020 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 <string.h>
|
||||
#include "soc/system_reg.h"
|
||||
#include "soc/lcd_cam_struct.h"
|
||||
#include "soc/lcd_cam_reg.h"
|
||||
#include "soc/gdma_struct.h"
|
||||
#include "soc/gdma_periph.h"
|
||||
#include "soc/gdma_reg.h"
|
||||
#include "ll_cam.h"
|
||||
#include "cam_hal.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)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "s3 ll_cam";
|
||||
|
||||
static void IRAM_ATTR ll_cam_vsync_isr(void *arg)
|
||||
{
|
||||
//DBG_PIN_SET(1);
|
||||
cam_obj_t *cam = (cam_obj_t *)arg;
|
||||
BaseType_t HPTaskAwoken = pdFALSE;
|
||||
|
||||
typeof(LCD_CAM.lc_dma_int_st) status = LCD_CAM.lc_dma_int_st;
|
||||
if (status.val == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
LCD_CAM.lc_dma_int_clr.val = status.val;
|
||||
|
||||
if (status.cam_vsync_int_st) {
|
||||
ll_cam_send_event(cam, CAM_VSYNC_EVENT, &HPTaskAwoken);
|
||||
}
|
||||
|
||||
if (HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
//DBG_PIN_SET(0);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
{
|
||||
cam_obj_t *cam = (cam_obj_t *)arg;
|
||||
BaseType_t HPTaskAwoken = pdFALSE;
|
||||
|
||||
typeof(GDMA.channel[cam->dma_num].in.int_st) status = GDMA.channel[cam->dma_num].in.int_st;
|
||||
if (status.val == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GDMA.channel[cam->dma_num].in.int_clr.val = status.val;
|
||||
|
||||
if (status.in_suc_eof) {
|
||||
ll_cam_send_event(cam, CAM_IN_SUC_EOF_EVENT, &HPTaskAwoken);
|
||||
}
|
||||
|
||||
if (HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
bool 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;
|
||||
GDMA.channel[cam->dma_num].in.int_clr.in_suc_eof = 1;
|
||||
}
|
||||
GDMA.channel[cam->dma_num].in.link.stop = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_deinit(cam_obj_t *cam)
|
||||
{
|
||||
if (cam->cam_intr_handle) {
|
||||
esp_intr_free(cam->cam_intr_handle);
|
||||
cam->cam_intr_handle = NULL;
|
||||
}
|
||||
|
||||
if (cam->dma_intr_handle) {
|
||||
esp_intr_free(cam->dma_intr_handle);
|
||||
cam->dma_intr_handle = NULL;
|
||||
}
|
||||
GDMA.channel[cam->dma_num].in.link.addr = 0x0;
|
||||
|
||||
LCD_CAM.cam_ctrl1.cam_start = 0;
|
||||
LCD_CAM.cam_ctrl1.cam_reset = 1;
|
||||
LCD_CAM.cam_ctrl1.cam_reset = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool ll_cam_start(cam_obj_t *cam, int frame_pos)
|
||||
{
|
||||
LCD_CAM.cam_ctrl1.cam_start = 0;
|
||||
|
||||
if (cam->jpeg_mode || !cam->psram_mode) {
|
||||
GDMA.channel[cam->dma_num].in.int_clr.in_suc_eof = 1;
|
||||
GDMA.channel[cam->dma_num].in.int_ena.in_suc_eof = 1;
|
||||
}
|
||||
|
||||
LCD_CAM.cam_ctrl1.cam_reset = 1;
|
||||
LCD_CAM.cam_ctrl1.cam_reset = 0;
|
||||
LCD_CAM.cam_ctrl1.cam_afifo_reset = 1;
|
||||
LCD_CAM.cam_ctrl1.cam_afifo_reset = 0;
|
||||
GDMA.channel[cam->dma_num].in.conf0.in_rst = 1;
|
||||
GDMA.channel[cam->dma_num].in.conf0.in_rst = 0;
|
||||
|
||||
LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = cam->dma_half_buffer_size - 1; // Ping pong operation
|
||||
|
||||
if (!cam->psram_mode) {
|
||||
GDMA.channel[cam->dma_num].in.link.addr = ((uint32_t)&cam->dma[0]) & 0xfffff;
|
||||
} else {
|
||||
GDMA.channel[cam->dma_num].in.link.addr = ((uint32_t)&cam->frames[frame_pos].dma[0]) & 0xfffff;
|
||||
}
|
||||
|
||||
GDMA.channel[cam->dma_num].in.link.start = 1;
|
||||
|
||||
LCD_CAM.cam_ctrl.cam_update = 1;
|
||||
LCD_CAM.cam_ctrl1.cam_start = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
|
||||
{
|
||||
for (int x = (SOC_GDMA_PAIRS_PER_GROUP - 1); x >= 0; x--) {
|
||||
if (GDMA.channel[x].in.link.addr == 0x0) {
|
||||
cam->dma_num = x;
|
||||
ESP_LOGI(TAG, "DMA Channel=%d", cam->dma_num);
|
||||
break;
|
||||
}
|
||||
if (x == 0) {
|
||||
cam_deinit();
|
||||
ESP_LOGE(TAG, "Can't found available GDMA channel");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN) == 0) {
|
||||
REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN);
|
||||
REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN);
|
||||
REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST);
|
||||
REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST);
|
||||
}
|
||||
|
||||
GDMA.channel[cam->dma_num].in.int_clr.val = ~0;
|
||||
GDMA.channel[cam->dma_num].in.int_ena.val = 0;
|
||||
|
||||
GDMA.channel[cam->dma_num].in.conf0.val = 0;
|
||||
GDMA.channel[cam->dma_num].in.conf0.in_rst = 1;
|
||||
GDMA.channel[cam->dma_num].in.conf0.in_rst = 0;
|
||||
|
||||
//internal SRAM only
|
||||
if (!cam->psram_mode) {
|
||||
GDMA.channel[cam->dma_num].in.conf0.indscr_burst_en = 1;
|
||||
GDMA.channel[cam->dma_num].in.conf0.in_data_burst_en = 1;
|
||||
}
|
||||
|
||||
GDMA.channel[cam->dma_num].in.conf1.in_check_owner = 0;
|
||||
|
||||
GDMA.channel[cam->dma_num].in.peri_sel.sel = 5;
|
||||
//GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15
|
||||
//GDMA.channel[cam->dma_num].in.sram_size.in_size = 6;//This register is used to configure the size of L2 Tx FIFO for Rx channel. 0:16 bytes, 1:24 bytes, 2:32 bytes, 3: 40 bytes, 4: 48 bytes, 5:56 bytes, 6: 64 bytes, 7: 72 bytes, 8: 80 bytes.
|
||||
//GDMA.channel[cam->dma_num].in.wight.rx_weight = 7;//The weight of Rx channel 0-15
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
|
||||
{
|
||||
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);
|
||||
REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);
|
||||
REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);
|
||||
}
|
||||
|
||||
LCD_CAM.cam_ctrl.val = 0;
|
||||
|
||||
LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;
|
||||
LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;
|
||||
LCD_CAM.cam_ctrl.cam_clkm_div_num = 160000000 / config->xclk_freq_hz;
|
||||
LCD_CAM.cam_ctrl.cam_clk_sel = 3;//Select Camera module source clock. 0: no clock. 1: APLL. 2: CLK160. 3: no clock.
|
||||
|
||||
LCD_CAM.cam_ctrl.cam_stop_en = 0;
|
||||
LCD_CAM.cam_ctrl.cam_vsync_filter_thres = 4; // Filter by LCD_CAM clock
|
||||
LCD_CAM.cam_ctrl.cam_update = 0;
|
||||
LCD_CAM.cam_ctrl.cam_byte_order = cam->swap_data;
|
||||
LCD_CAM.cam_ctrl.cam_bit_order = 0;
|
||||
LCD_CAM.cam_ctrl.cam_line_int_en = 0;
|
||||
LCD_CAM.cam_ctrl.cam_vs_eof_en = 0; //1: CAM_VSYNC to generate in_suc_eof. 0: in_suc_eof is controlled by reg_cam_rec_data_cyclelen
|
||||
|
||||
LCD_CAM.cam_ctrl1.val = 0;
|
||||
LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE - 1; // Cannot be assigned to 0, and it is easy to overflow
|
||||
LCD_CAM.cam_ctrl1.cam_line_int_num = 0; // The number of hsyncs that generate hs interrupts
|
||||
LCD_CAM.cam_ctrl1.cam_clk_inv = 0;
|
||||
LCD_CAM.cam_ctrl1.cam_vsync_filter_en = 1;
|
||||
LCD_CAM.cam_ctrl1.cam_2byte_en = 0;
|
||||
LCD_CAM.cam_ctrl1.cam_de_inv = 0;
|
||||
LCD_CAM.cam_ctrl1.cam_hsync_inv = 0;
|
||||
LCD_CAM.cam_ctrl1.cam_vsync_inv = 0;
|
||||
LCD_CAM.cam_ctrl1.cam_vh_de_mode_en = 0;
|
||||
|
||||
LCD_CAM.cam_rgb_yuv.val = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
|
||||
{
|
||||
LCD_CAM.lc_dma_int_clr.cam_vsync_int_clr = 1;
|
||||
if (en) {
|
||||
LCD_CAM.lc_dma_int_ena.cam_vsync_int_ena = 1;
|
||||
} else {
|
||||
LCD_CAM.lc_dma_int_ena.cam_vsync_int_ena = 0;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config)
|
||||
{
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_pclk], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_pclk, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(config->pin_pclk, GPIO_FLOATING);
|
||||
gpio_matrix_in(config->pin_pclk, CAM_PCLK_IDX, false);
|
||||
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_vsync], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_vsync, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(config->pin_vsync, GPIO_FLOATING);
|
||||
gpio_matrix_in(config->pin_vsync, CAM_V_SYNC_IDX, cam->vsync_invert);
|
||||
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_href], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_href, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(config->pin_href, GPIO_FLOATING);
|
||||
gpio_matrix_in(config->pin_href, CAM_H_ENABLE_IDX, false);
|
||||
|
||||
int data_pins[8] = {
|
||||
config->pin_d0, config->pin_d1, config->pin_d2, config->pin_d3, config->pin_d4, config->pin_d5, config->pin_d6, config->pin_d7,
|
||||
};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[data_pins[i]], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(data_pins[i], GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(data_pins[i], GPIO_FLOATING);
|
||||
gpio_matrix_in(data_pins[i], CAM_DATA_IN0_IDX + i, false);
|
||||
}
|
||||
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_xclk], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_xclk, GPIO_MODE_OUTPUT);
|
||||
gpio_set_pull_mode(config->pin_xclk, GPIO_FLOATING);
|
||||
gpio_matrix_out(config->pin_xclk, CAM_CLK_IDX, false, false);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_init_isr(cam_obj_t *cam)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret = esp_intr_alloc_intrstatus(gdma_periph_signals.groups[0].pairs[cam->dma_num].rx_irq_id,
|
||||
ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM,
|
||||
(uint32_t)&GDMA.channel[cam->dma_num].in.int_st, GDMA_IN_SUC_EOF_CH0_INT_ST_M,
|
||||
ll_cam_dma_isr, cam, &cam->dma_intr_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "DMA interrupt allocation of camera failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = esp_intr_alloc_intrstatus(ETS_LCD_CAM_INTR_SOURCE,
|
||||
ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM,
|
||||
(uint32_t)&LCD_CAM.lc_dma_int_st.val, LCD_CAM_CAM_VSYNC_INT_ST_M,
|
||||
ll_cam_vsync_isr, cam, &cam->cam_intr_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "LCD_CAM interrupt allocation of camera failed");
|
||||
return ret;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void ll_cam_do_vsync(cam_obj_t *cam)
|
||||
{
|
||||
gpio_matrix_in(cam->vsync_pin, CAM_V_SYNC_IDX, !cam->vsync_invert);
|
||||
ets_delay_us(10);
|
||||
gpio_matrix_in(cam->vsync_pin, CAM_V_SYNC_IDX, cam->vsync_invert);
|
||||
}
|
||||
|
||||
uint8_t ll_cam_get_dma_align(cam_obj_t *cam)
|
||||
{
|
||||
return 16 << GDMA.channel[cam->dma_num].in.conf1.in_ext_mem_bk_size;
|
||||
}
|
||||
|
||||
static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
|
||||
size_t node_max = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE / cam->dma_bytes_per_item;
|
||||
size_t line_width = cam->width * cam->in_bytes_per_pixel;
|
||||
size_t node_size = node_max;
|
||||
size_t nodes_per_line = 1;
|
||||
size_t lines_per_node = 1;
|
||||
|
||||
// Calculate DMA Node Size so that it's divisable by or divisor of the line width
|
||||
if(line_width >= node_max){
|
||||
// One or more nodes will be requied for one line
|
||||
for(size_t i = node_max; i > 0; i=i-1){
|
||||
if ((line_width % i) == 0) {
|
||||
node_size = i;
|
||||
nodes_per_line = line_width / node_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// One or more lines can fit into one node
|
||||
for(size_t i = node_max; i > 0; i=i-1){
|
||||
if ((i % line_width) == 0) {
|
||||
node_size = i;
|
||||
lines_per_node = node_size / line_width;
|
||||
while((cam->height % lines_per_node) != 0){
|
||||
lines_per_node = lines_per_node - 1;
|
||||
node_size = lines_per_node * line_width;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u",
|
||||
node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node);
|
||||
|
||||
cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
|
||||
|
||||
size_t dma_half_buffer_max = CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX / 2 / cam->dma_bytes_per_item;
|
||||
if (line_width > dma_half_buffer_max) {
|
||||
ESP_LOGE(TAG, "Resolution too high");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate minimum EOF size = max(mode_size, line_size)
|
||||
size_t dma_half_buffer_min = node_size * nodes_per_line;
|
||||
|
||||
// Calculate max EOF size divisable by node size
|
||||
size_t dma_half_buffer = (dma_half_buffer_max / dma_half_buffer_min) * dma_half_buffer_min;
|
||||
|
||||
// Adjust EOF size so that height will be divisable by the number of lines in each EOF
|
||||
size_t lines_per_half_buffer = dma_half_buffer / line_width;
|
||||
while((cam->height % lines_per_half_buffer) != 0){
|
||||
dma_half_buffer = dma_half_buffer - dma_half_buffer_min;
|
||||
lines_per_half_buffer = dma_half_buffer / line_width;
|
||||
}
|
||||
|
||||
// Calculate DMA size
|
||||
size_t dma_buffer_max = 2 * dma_half_buffer_max;
|
||||
if (cam->psram_mode) {
|
||||
dma_buffer_max = cam->recv_size / cam->dma_bytes_per_item;
|
||||
}
|
||||
size_t dma_buffer_size = dma_buffer_max;
|
||||
if (!cam->psram_mode) {
|
||||
dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u",
|
||||
dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item);
|
||||
|
||||
cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
|
||||
cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
|
||||
cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool ll_cam_dma_sizes(cam_obj_t *cam)
|
||||
{
|
||||
cam->dma_bytes_per_item = 1;
|
||||
if (cam->jpeg_mode) {
|
||||
if (cam->psram_mode) {
|
||||
cam->dma_buffer_size = cam->recv_size;
|
||||
cam->dma_half_buffer_size = 1024;
|
||||
cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
|
||||
cam->dma_node_buffer_size = cam->dma_half_buffer_size;
|
||||
} else {
|
||||
cam->dma_half_buffer_cnt = 16;
|
||||
cam->dma_buffer_size = cam->dma_half_buffer_cnt * 1024;
|
||||
cam->dma_half_buffer_size = cam->dma_buffer_size / cam->dma_half_buffer_cnt;
|
||||
cam->dma_node_buffer_size = cam->dma_half_buffer_size;
|
||||
}
|
||||
} else {
|
||||
return ll_cam_calc_rgb_dma(cam);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len)
|
||||
{
|
||||
// YUV to Grayscale
|
||||
if (cam->in_bytes_per_pixel == 2 && cam->fb_bytes_per_pixel == 1) {
|
||||
size_t end = len / 8;
|
||||
for (size_t i = 0; i < end; ++i) {
|
||||
out[0] = in[0];
|
||||
out[1] = in[2];
|
||||
out[2] = in[4];
|
||||
out[3] = in[6];
|
||||
out += 4;
|
||||
in += 8;
|
||||
}
|
||||
return len / 2;
|
||||
}
|
||||
|
||||
// just memcpy
|
||||
memcpy(out, in, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid)
|
||||
{
|
||||
if (pix_format == PIXFORMAT_GRAYSCALE) {
|
||||
if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID) {
|
||||
cam->in_bytes_per_pixel = 1; // camera sends Y8
|
||||
} else {
|
||||
cam->in_bytes_per_pixel = 2; // camera sends YU/YV
|
||||
}
|
||||
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
|
||||
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
|
||||
} else if (pix_format == PIXFORMAT_JPEG) {
|
||||
cam->in_bytes_per_pixel = 1;
|
||||
cam->fb_bytes_per_pixel = 1;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Requested format is not supported");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// implements function from xclk.c to allow dynamic XCLK change
|
||||
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz)
|
||||
{
|
||||
LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;
|
||||
LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;
|
||||
LCD_CAM.cam_ctrl.cam_clkm_div_num = 160000000 / xclk_freq_hz;
|
||||
LCD_CAM.cam_ctrl.cam_clk_sel = 3;//Select Camera module source clock. 0: no clock. 1: APLL. 2: CLK160. 3: no clock.
|
||||
LCD_CAM.cam_ctrl.cam_update = 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
// Copyright 2010-2020 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 "sdkconfig.h"
|
||||
#include "esp_idf_version.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
#include "esp32/rom/lldesc.h"
|
||||
#else
|
||||
#include "rom/lldesc.h"
|
||||
#endif
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/lldesc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/lldesc.h"
|
||||
#endif
|
||||
#include "esp_log.h"
|
||||
#include "esp_camera.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#if __has_include("esp_private/periph_ctrl.h")
|
||||
# include "esp_private/periph_ctrl.h"
|
||||
#endif
|
||||
|
||||
#define CAMERA_DBG_PIN_ENABLE 0
|
||||
#if CAMERA_DBG_PIN_ENABLE
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define DBG_PIN_NUM 26
|
||||
#else
|
||||
#define DBG_PIN_NUM 7
|
||||
#endif
|
||||
#include "hal/gpio_ll.h"
|
||||
#define DBG_PIN_SET(v) gpio_ll_set_level(&GPIO, DBG_PIN_NUM, v)
|
||||
#else
|
||||
#define DBG_PIN_SET(v)
|
||||
#endif
|
||||
|
||||
#define CAM_CHECK(a, str, ret) if (!(a)) { \
|
||||
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define CAM_CHECK_GOTO(a, str, lab) if (!(a)) { \
|
||||
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
|
||||
goto lab; \
|
||||
}
|
||||
|
||||
#define LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE (4092)
|
||||
|
||||
typedef enum {
|
||||
CAM_IN_SUC_EOF_EVENT = 0,
|
||||
CAM_VSYNC_EVENT
|
||||
} cam_event_t;
|
||||
|
||||
typedef enum {
|
||||
CAM_STATE_IDLE = 0,
|
||||
CAM_STATE_READ_BUF = 1,
|
||||
} cam_state_t;
|
||||
|
||||
typedef struct {
|
||||
camera_fb_t fb;
|
||||
uint8_t en;
|
||||
//for RGB/YUV modes
|
||||
lldesc_t *dma;
|
||||
size_t fb_offset;
|
||||
} cam_frame_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t dma_bytes_per_item;
|
||||
uint32_t dma_buffer_size;
|
||||
uint32_t dma_half_buffer_size;
|
||||
uint32_t dma_half_buffer_cnt;
|
||||
uint32_t dma_node_buffer_size;
|
||||
uint32_t dma_node_cnt;
|
||||
uint32_t frame_copy_cnt;
|
||||
|
||||
//for JPEG mode
|
||||
lldesc_t *dma;
|
||||
uint8_t *dma_buffer;
|
||||
|
||||
cam_frame_t *frames;
|
||||
|
||||
QueueHandle_t event_queue;
|
||||
QueueHandle_t frame_buffer_queue;
|
||||
TaskHandle_t task_handle;
|
||||
intr_handle_t cam_intr_handle;
|
||||
|
||||
uint8_t dma_num;//ESP32-S3
|
||||
intr_handle_t dma_intr_handle;//ESP32-S3
|
||||
|
||||
uint8_t jpeg_mode;
|
||||
uint8_t vsync_pin;
|
||||
uint8_t vsync_invert;
|
||||
uint32_t frame_cnt;
|
||||
uint32_t recv_size;
|
||||
bool swap_data;
|
||||
bool psram_mode;
|
||||
|
||||
//for RGB/YUV modes
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t in_bytes_per_pixel;
|
||||
uint8_t fb_bytes_per_pixel;
|
||||
uint32_t fb_size;
|
||||
|
||||
cam_state_t state;
|
||||
} cam_obj_t;
|
||||
|
||||
|
||||
bool ll_cam_stop(cam_obj_t *cam);
|
||||
bool ll_cam_start(cam_obj_t *cam, int frame_pos);
|
||||
esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config);
|
||||
esp_err_t ll_cam_deinit(cam_obj_t *cam);
|
||||
void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en);
|
||||
esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config);
|
||||
esp_err_t ll_cam_init_isr(cam_obj_t *cam);
|
||||
void ll_cam_do_vsync(cam_obj_t *cam);
|
||||
uint8_t ll_cam_get_dma_align(cam_obj_t *cam);
|
||||
bool ll_cam_dma_sizes(cam_obj_t *cam);
|
||||
size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len);
|
||||
esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid);
|
||||
|
||||
// implemented in cam_hal
|
||||
void ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType_t * HPTaskAwoken);
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "xclk.h"
|
||||
#include "esp_camera.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
@@ -12,12 +13,15 @@
|
||||
static const char* TAG = "camera_xclk";
|
||||
#endif
|
||||
|
||||
static ledc_channel_t g_ledc_channel = 0;
|
||||
|
||||
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz)
|
||||
{
|
||||
ledc_timer_config_t timer_conf;
|
||||
timer_conf.duty_resolution = 2;
|
||||
timer_conf.duty_resolution = LEDC_TIMER_1_BIT;
|
||||
timer_conf.freq_hz = xclk_freq_hz;
|
||||
timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
|
||||
timer_conf.speed_mode = LEDC_LOW_SPEED_MODE;
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
timer_conf.clk_cfg = LEDC_AUTO_CLK;
|
||||
#endif
|
||||
@@ -31,21 +35,20 @@ esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz)
|
||||
|
||||
esp_err_t camera_enable_out_clock(camera_config_t* config)
|
||||
{
|
||||
periph_module_enable(PERIPH_LEDC_MODULE);
|
||||
|
||||
esp_err_t err = xclk_timer_conf(config->ledc_timer, config->xclk_freq_hz);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ledc_timer_config failed, rc=%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
g_ledc_channel = config->ledc_channel;
|
||||
ledc_channel_config_t ch_conf;
|
||||
ch_conf.gpio_num = config->pin_xclk;
|
||||
ch_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
|
||||
ch_conf.speed_mode = LEDC_LOW_SPEED_MODE;
|
||||
ch_conf.channel = config->ledc_channel;
|
||||
ch_conf.intr_type = LEDC_INTR_DISABLE;
|
||||
ch_conf.timer_sel = config->ledc_timer;
|
||||
ch_conf.duty = 2;
|
||||
ch_conf.duty = 1;
|
||||
ch_conf.hpoint = 0;
|
||||
err = ledc_channel_config(&ch_conf);
|
||||
if (err != ESP_OK) {
|
||||
@@ -57,5 +60,5 @@ esp_err_t camera_enable_out_clock(camera_config_t* config)
|
||||
|
||||
void camera_disable_out_clock()
|
||||
{
|
||||
periph_module_disable(PERIPH_LEDC_MODULE);
|
||||
ledc_stop(LEDC_LOW_SPEED_MODE, g_ledc_channel, 0);
|
||||
}
|
||||
4
code/components/esp32-camera-master/test/CMakeLists.txt
Normal file
4
code/components/esp32-camera-master/test/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
PRIV_INCLUDE_DIRS .
|
||||
PRIV_REQUIRES test_utils esp32-camera nvs_flash
|
||||
EMBED_TXTFILES pictures/testimg.jpeg pictures/test_outside.jpeg pictures/test_inside.jpeg)
|
||||
8
code/components/esp32-camera-master/test/component.mk
Normal file
8
code/components/esp32-camera-master/test/component.mk
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#Component Makefile
|
||||
#
|
||||
|
||||
COMPONENT_SRCDIRS += ./
|
||||
COMPONENT_PRIV_INCLUDEDIRS += ./
|
||||
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
code/components/esp32-camera-master/test/pictures/testimg.jpeg
Normal file
BIN
code/components/esp32-camera-master/test/pictures/testimg.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
500
code/components/esp32-camera-master/test/test_camera.c
Normal file
500
code/components/esp32-camera-master/test/test_camera.c
Normal file
@@ -0,0 +1,500 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include <mbedtls/base64.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_camera.h"
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
#define BOARD_WROVER_KIT 1
|
||||
#elif defined CONFIG_IDF_TARGET_ESP32S2
|
||||
#define BOARD_CAMERA_MODEL_ESP32S2 1
|
||||
#elif defined CONFIG_IDF_TARGET_ESP32S3
|
||||
#define BOARD_CAMERA_MODEL_ESP32_S3_EYE 1
|
||||
#endif
|
||||
|
||||
// WROVER-KIT PIN Map
|
||||
#if BOARD_WROVER_KIT
|
||||
|
||||
#define PWDN_GPIO_NUM -1 //power down is not used
|
||||
#define RESET_GPIO_NUM -1 //software reset will be performed
|
||||
#define XCLK_GPIO_NUM 21
|
||||
#define SIOD_GPIO_NUM 26
|
||||
#define SIOC_GPIO_NUM 27
|
||||
|
||||
#define Y9_GPIO_NUM 35
|
||||
#define Y8_GPIO_NUM 34
|
||||
#define Y7_GPIO_NUM 39
|
||||
#define Y6_GPIO_NUM 36
|
||||
#define Y5_GPIO_NUM 19
|
||||
#define Y4_GPIO_NUM 18
|
||||
#define Y3_GPIO_NUM 5
|
||||
#define Y2_GPIO_NUM 4
|
||||
#define VSYNC_GPIO_NUM 25
|
||||
#define HREF_GPIO_NUM 23
|
||||
#define PCLK_GPIO_NUM 22
|
||||
|
||||
// ESP32Cam (AiThinker) PIN Map
|
||||
#elif BOARD_ESP32CAM_AITHINKER
|
||||
|
||||
#define PWDN_GPIO_NUM 32
|
||||
#define RESET_GPIO_NUM -1 //software reset will be performed
|
||||
#define XCLK_GPIO_NUM 0
|
||||
#define SIOD_GPIO_NUM 26
|
||||
#define SIOC_GPIO_NUM 27
|
||||
|
||||
#define Y9_GPIO_NUM 35
|
||||
#define Y8_GPIO_NUM 34
|
||||
#define Y7_GPIO_NUM 39
|
||||
#define Y6_GPIO_NUM 36
|
||||
#define Y5_GPIO_NUM 21
|
||||
#define Y4_GPIO_NUM 19
|
||||
#define Y3_GPIO_NUM 18
|
||||
#define Y2_GPIO_NUM 5
|
||||
#define VSYNC_GPIO_NUM 25
|
||||
#define HREF_GPIO_NUM 23
|
||||
#define PCLK_GPIO_NUM 22
|
||||
|
||||
#elif BOARD_CAMERA_MODEL_ESP32S2
|
||||
|
||||
#define PWDN_GPIO_NUM -1
|
||||
#define RESET_GPIO_NUM -1
|
||||
|
||||
#define VSYNC_GPIO_NUM 21
|
||||
#define HREF_GPIO_NUM 38
|
||||
#define PCLK_GPIO_NUM 11
|
||||
#define XCLK_GPIO_NUM 40
|
||||
|
||||
#define SIOD_GPIO_NUM 17
|
||||
#define SIOC_GPIO_NUM 18
|
||||
|
||||
#define Y9_GPIO_NUM 39
|
||||
#define Y8_GPIO_NUM 41
|
||||
#define Y7_GPIO_NUM 42
|
||||
#define Y6_GPIO_NUM 12
|
||||
#define Y5_GPIO_NUM 3
|
||||
#define Y4_GPIO_NUM 14
|
||||
#define Y3_GPIO_NUM 37
|
||||
#define Y2_GPIO_NUM 13
|
||||
|
||||
#elif BOARD_CAMERA_MODEL_ESP32_S3_EYE
|
||||
|
||||
#define PWDN_GPIO_NUM 43
|
||||
#define RESET_GPIO_NUM 44
|
||||
|
||||
#define VSYNC_GPIO_NUM 6
|
||||
#define HREF_GPIO_NUM 7
|
||||
#define PCLK_GPIO_NUM 13
|
||||
#define XCLK_GPIO_NUM 15
|
||||
|
||||
#define SIOD_GPIO_NUM 4
|
||||
#define SIOC_GPIO_NUM 5
|
||||
|
||||
#define Y9_GPIO_NUM 16
|
||||
#define Y8_GPIO_NUM 17
|
||||
#define Y7_GPIO_NUM 18
|
||||
#define Y6_GPIO_NUM 12
|
||||
#define Y5_GPIO_NUM 11
|
||||
#define Y4_GPIO_NUM 10
|
||||
#define Y3_GPIO_NUM 9
|
||||
#define Y2_GPIO_NUM 8
|
||||
|
||||
#endif
|
||||
|
||||
static const char *TAG = "test camera";
|
||||
|
||||
typedef void (*decode_func_t)(uint8_t *jpegbuffer, uint32_t size, uint8_t *outbuffer);
|
||||
|
||||
static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, framesize_t frame_size, uint8_t fb_count)
|
||||
{
|
||||
framesize_t size_bak = frame_size;
|
||||
if (PIXFORMAT_JPEG == pixel_format && FRAMESIZE_SVGA > frame_size) {
|
||||
frame_size = FRAMESIZE_HD;
|
||||
}
|
||||
camera_config_t camera_config = {
|
||||
.pin_pwdn = PWDN_GPIO_NUM,
|
||||
.pin_reset = RESET_GPIO_NUM,
|
||||
.pin_xclk = XCLK_GPIO_NUM,
|
||||
.pin_sscb_sda = SIOD_GPIO_NUM,
|
||||
.pin_sscb_scl = SIOC_GPIO_NUM,
|
||||
|
||||
.pin_d7 = Y9_GPIO_NUM,
|
||||
.pin_d6 = Y8_GPIO_NUM,
|
||||
.pin_d5 = Y7_GPIO_NUM,
|
||||
.pin_d4 = Y6_GPIO_NUM,
|
||||
.pin_d3 = Y5_GPIO_NUM,
|
||||
.pin_d2 = Y4_GPIO_NUM,
|
||||
.pin_d1 = Y3_GPIO_NUM,
|
||||
.pin_d0 = Y2_GPIO_NUM,
|
||||
.pin_vsync = VSYNC_GPIO_NUM,
|
||||
.pin_href = HREF_GPIO_NUM,
|
||||
.pin_pclk = PCLK_GPIO_NUM,
|
||||
|
||||
//EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode
|
||||
.xclk_freq_hz = xclk_freq_hz,
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
|
||||
.pixel_format = pixel_format, //YUV422,GRAYSCALE,RGB565,JPEG
|
||||
.frame_size = frame_size, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
|
||||
|
||||
.jpeg_quality = 12, //0-63 lower number means higher quality
|
||||
.fb_count = fb_count, //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
.grab_mode = CAMERA_GRAB_WHEN_EMPTY
|
||||
};
|
||||
|
||||
//initialize the camera
|
||||
esp_err_t ret = esp_camera_init(&camera_config);
|
||||
|
||||
if (ESP_OK == ret && PIXFORMAT_JPEG == pixel_format && FRAMESIZE_SVGA > size_bak) {
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
s->set_framesize(s, size_bak);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool camera_test_fps(uint16_t times, float *fps, uint32_t *size)
|
||||
{
|
||||
*fps = 0.0f;
|
||||
*size = 0;
|
||||
uint32_t s = 0;
|
||||
uint32_t num = 0;
|
||||
uint64_t total_time = esp_timer_get_time();
|
||||
for (size_t i = 0; i < times; i++) {
|
||||
camera_fb_t *pic = esp_camera_fb_get();
|
||||
if (NULL == pic) {
|
||||
ESP_LOGW(TAG, "fb get failed");
|
||||
return 0;
|
||||
} else {
|
||||
s += pic->len;
|
||||
num++;
|
||||
}
|
||||
esp_camera_fb_return(pic);
|
||||
}
|
||||
total_time = esp_timer_get_time() - total_time;
|
||||
if (num) {
|
||||
*fps = num * 1000000.0f / total_time ;
|
||||
*size = s / num;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *get_cam_format_name(pixformat_t pixel_format)
|
||||
{
|
||||
switch (pixel_format) {
|
||||
case PIXFORMAT_JPEG: return "JPEG";
|
||||
case PIXFORMAT_RGB565: return "RGB565";
|
||||
case PIXFORMAT_RGB888: return "RGB888";
|
||||
case PIXFORMAT_YUV422: return "YUV422";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "UNKNOW";
|
||||
}
|
||||
|
||||
static void printf_img_base64(const camera_fb_t *pic)
|
||||
{
|
||||
uint8_t *outbuffer = NULL;
|
||||
size_t outsize = 0;
|
||||
if (PIXFORMAT_JPEG != pic->format) {
|
||||
fmt2jpg(pic->buf, pic->width * pic->height * 2, pic->width, pic->height, pic->format, 50, &outbuffer, &outsize);
|
||||
} else {
|
||||
outbuffer = pic->buf;
|
||||
outsize = pic->len;
|
||||
}
|
||||
|
||||
uint8_t *base64_buf = calloc(1, outsize * 4);
|
||||
if (NULL != base64_buf) {
|
||||
size_t out_len = 0;
|
||||
mbedtls_base64_encode(base64_buf, outsize * 4, &out_len, outbuffer, outsize);
|
||||
printf("%s\n", base64_buf);
|
||||
free(base64_buf);
|
||||
if (PIXFORMAT_JPEG != pic->format) {
|
||||
free(outbuffer);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "malloc for base64 buffer failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void camera_performance_test(uint32_t xclk_freq, uint32_t pic_num)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
//detect sensor information
|
||||
TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
camera_sensor_info_t *info = esp_camera_sensor_get_info(&s->id);
|
||||
TEST_ASSERT_NOT_NULL(info);
|
||||
TEST_ESP_OK(esp_camera_deinit());
|
||||
vTaskDelay(500 / portTICK_RATE_MS);
|
||||
framesize_t max_size = info->max_size;
|
||||
pixformat_t all_format[] = {PIXFORMAT_JPEG, PIXFORMAT_RGB565, PIXFORMAT_YUV422, };
|
||||
pixformat_t *format_s = &all_format[0];
|
||||
pixformat_t *format_e = &all_format[2];
|
||||
if (false == info->support_jpeg) {
|
||||
format_s++; // skip jpeg
|
||||
}
|
||||
|
||||
struct fps_result {
|
||||
float fps[FRAMESIZE_INVALID];
|
||||
uint32_t size[FRAMESIZE_INVALID];
|
||||
};
|
||||
struct fps_result results[3] = {0};
|
||||
|
||||
for (; format_s <= format_e; format_s++) {
|
||||
for (size_t i = 0; i <= max_size; i++) {
|
||||
ESP_LOGI(TAG, "\n\n===> Testing format:%s resolution: %d x %d <===", get_cam_format_name(*format_s), resolution[i].width, resolution[i].height);
|
||||
ret = init_camera(xclk_freq, *format_s, i, 2);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
if (ESP_OK != ret) {
|
||||
ESP_LOGW(TAG, "Testing init failed :-(, skip this item");
|
||||
vTaskDelay(500 / portTICK_RATE_MS);
|
||||
continue;
|
||||
}
|
||||
camera_test_fps(pic_num, &results[format_s - all_format].fps[i], &results[format_s - all_format].size[i]);
|
||||
TEST_ESP_OK(esp_camera_deinit());
|
||||
}
|
||||
}
|
||||
|
||||
printf("FPS Result\n");
|
||||
printf("resolution , JPEG fps, JPEG size, RGB565 fps, RGB565 size, YUV422 fps, YUV422 size \n");
|
||||
for (size_t i = 0; i <= max_size; i++) {
|
||||
printf("%4d x %4d , %5.2f, %6d, %5.2f, %7d, %5.2f, %7d \n",
|
||||
resolution[i].width, resolution[i].height,
|
||||
results[0].fps[i], results[0].size[i],
|
||||
results[1].fps[i], results[1].size[i],
|
||||
results[2].fps[i], results[2].size[i]);
|
||||
}
|
||||
printf("----------------------------------------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
TEST_CASE("Camera driver init, deinit test", "[camera]")
|
||||
{
|
||||
uint64_t t1 = esp_timer_get_time();
|
||||
TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
|
||||
uint64_t t2 = esp_timer_get_time();
|
||||
ESP_LOGI(TAG, "Camera init time %llu ms", (t2 - t1) / 1000);
|
||||
|
||||
TEST_ESP_OK(esp_camera_deinit());
|
||||
}
|
||||
|
||||
TEST_CASE("Camera driver take RGB565 picture test", "[camera]")
|
||||
{
|
||||
TEST_ESP_OK(init_camera(10000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
|
||||
vTaskDelay(500 / portTICK_RATE_MS);
|
||||
ESP_LOGI(TAG, "Taking picture...");
|
||||
camera_fb_t *pic = esp_camera_fb_get();
|
||||
if (pic) {
|
||||
ESP_LOGI(TAG, "picture: %d x %d, size: %u", pic->width, pic->height, pic->len);
|
||||
printf_img_base64(pic);
|
||||
esp_camera_fb_return(pic);
|
||||
}
|
||||
|
||||
TEST_ESP_OK(esp_camera_deinit());
|
||||
TEST_ASSERT_NOT_NULL(pic);
|
||||
}
|
||||
|
||||
TEST_CASE("Camera driver take YUV422 picture test", "[camera]")
|
||||
{
|
||||
TEST_ESP_OK(init_camera(10000000, PIXFORMAT_YUV422, FRAMESIZE_QVGA, 2));
|
||||
vTaskDelay(500 / portTICK_RATE_MS);
|
||||
ESP_LOGI(TAG, "Taking picture...");
|
||||
camera_fb_t *pic = esp_camera_fb_get();
|
||||
if (pic) {
|
||||
ESP_LOGI(TAG, "picture: %d x %d, size: %u", pic->width, pic->height, pic->len);
|
||||
printf_img_base64(pic);
|
||||
esp_camera_fb_return(pic);
|
||||
}
|
||||
|
||||
TEST_ESP_OK(esp_camera_deinit());
|
||||
TEST_ASSERT_NOT_NULL(pic);
|
||||
}
|
||||
|
||||
TEST_CASE("Camera driver take JPEG picture test", "[camera]")
|
||||
{
|
||||
TEST_ESP_OK(init_camera(20000000, PIXFORMAT_JPEG, FRAMESIZE_QVGA, 2));
|
||||
vTaskDelay(500 / portTICK_RATE_MS);
|
||||
ESP_LOGI(TAG, "Taking picture...");
|
||||
camera_fb_t *pic = esp_camera_fb_get();
|
||||
if (pic) {
|
||||
ESP_LOGI(TAG, "picture: %d x %d, size: %u", pic->width, pic->height, pic->len);
|
||||
printf_img_base64(pic);
|
||||
esp_camera_fb_return(pic);
|
||||
}
|
||||
|
||||
TEST_ESP_OK(esp_camera_deinit());
|
||||
TEST_ASSERT_NOT_NULL(pic);
|
||||
}
|
||||
|
||||
TEST_CASE("Camera driver performance test", "[camera]")
|
||||
{
|
||||
camera_performance_test(20 * 1000000, 16);
|
||||
}
|
||||
|
||||
|
||||
static void print_rgb565_img(uint8_t *img, int width, int height)
|
||||
{
|
||||
uint16_t *p = (uint16_t *)img;
|
||||
const char temp2char[17] = "@MNHQ&#UJ*x7^i;.";
|
||||
for (size_t j = 0; j < height; j++) {
|
||||
for (size_t i = 0; i < width; i++) {
|
||||
uint32_t c = p[j * width + i];
|
||||
uint8_t r = c >> 11;
|
||||
uint8_t g = (c >> 6) & 0x1f;
|
||||
uint8_t b = c & 0x1f;
|
||||
c = (r + g + b) / 3;
|
||||
c >>= 1;
|
||||
printf("%c", temp2char[15 - c]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_rgb888_img(uint8_t *img, int width, int height)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)img;
|
||||
const char temp2char[17] = "@MNHQ&#UJ*x7^i;.";
|
||||
for (size_t j = 0; j < height; j++) {
|
||||
for (size_t i = 0; i < width; i++) {
|
||||
uint8_t *c = p + 3 * (j * width + i);
|
||||
uint8_t r = *c++;
|
||||
uint8_t g = *c++;
|
||||
uint8_t b = *c;
|
||||
uint32_t v = (r + g + b) / 3;
|
||||
v >>= 4;
|
||||
printf("%c", temp2char[15 - v]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void tjpgd_decode_rgb565(uint8_t *mjpegbuffer, uint32_t size, uint8_t *outbuffer)
|
||||
{
|
||||
jpg2rgb565(mjpegbuffer, size, outbuffer, JPG_SCALE_NONE);
|
||||
}
|
||||
|
||||
static void tjpgd_decode_rgb888(uint8_t *mjpegbuffer, uint32_t size, uint8_t *outbuffer)
|
||||
{
|
||||
fmt2rgb888(mjpegbuffer, size, PIXFORMAT_JPEG, outbuffer);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
DECODE_RGB565,
|
||||
DECODE_RGB888,
|
||||
} decode_type_t;
|
||||
|
||||
static const decode_func_t g_decode_func[2][2] = {
|
||||
{tjpgd_decode_rgb565,},
|
||||
{tjpgd_decode_rgb888,},
|
||||
};
|
||||
|
||||
|
||||
static float jpg_decode_test(uint8_t decoder_index, decode_type_t type, const uint8_t *jpg, uint32_t length, uint32_t img_w, uint32_t img_h, uint32_t times)
|
||||
{
|
||||
uint8_t *jpg_buf = malloc(length);
|
||||
if (NULL == jpg_buf) {
|
||||
ESP_LOGE(TAG, "malloc for jpg buffer failed");
|
||||
return 0;
|
||||
}
|
||||
memcpy(jpg_buf, jpg, length);
|
||||
|
||||
uint8_t *rgb_buf = heap_caps_malloc(img_w * img_h * 3, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (NULL == rgb_buf) {
|
||||
free(jpg_buf);
|
||||
ESP_LOGE(TAG, "malloc for rgb buffer failed");
|
||||
return 0;
|
||||
}
|
||||
decode_func_t decode = g_decode_func[type][decoder_index];
|
||||
decode(jpg_buf, length, rgb_buf);
|
||||
if (DECODE_RGB565 == type) {
|
||||
ESP_LOGI(TAG, "jpeg decode to rgb565");
|
||||
print_rgb565_img(rgb_buf, img_w, img_h);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "jpeg decode to rgb888");
|
||||
print_rgb888_img(rgb_buf, img_w, img_h);
|
||||
}
|
||||
|
||||
uint64_t t_decode[times];
|
||||
for (size_t i = 0; i < times; i++) {
|
||||
uint64_t t1 = esp_timer_get_time();
|
||||
decode(jpg_buf, length, rgb_buf);
|
||||
t_decode[i] = esp_timer_get_time() - t1;
|
||||
}
|
||||
|
||||
printf("resolution , t \n");
|
||||
uint64_t t_total = 0;
|
||||
for (size_t i = 0; i < times; i++) {
|
||||
t_total += t_decode[i];
|
||||
float t = t_decode[i] / 1000.0f;
|
||||
printf("%4d x %4d , %5.2f ms \n", img_w, img_h, t);
|
||||
}
|
||||
|
||||
float fps = times / (t_total / 1000000.0f);
|
||||
printf("Decode FPS Result\n");
|
||||
printf("resolution , fps \n");
|
||||
printf("%4d x %4d , %5.2f \n", img_w, img_h, fps);
|
||||
|
||||
free(jpg_buf);
|
||||
heap_caps_free(rgb_buf);
|
||||
return fps;
|
||||
}
|
||||
|
||||
static void img_jpeg_decode_test(uint16_t pic_index, uint16_t lib_index)
|
||||
{
|
||||
extern const uint8_t img1_start[] asm("_binary_testimg_jpeg_start");
|
||||
extern const uint8_t img1_end[] asm("_binary_testimg_jpeg_end");
|
||||
extern const uint8_t img2_start[] asm("_binary_test_inside_jpeg_start");
|
||||
extern const uint8_t img2_end[] asm("_binary_test_inside_jpeg_end");
|
||||
extern const uint8_t img3_start[] asm("_binary_test_outside_jpeg_start");
|
||||
extern const uint8_t img3_end[] asm("_binary_test_outside_jpeg_end");
|
||||
|
||||
struct img_t {
|
||||
const uint8_t *buf;
|
||||
uint32_t length;
|
||||
uint16_t w, h;
|
||||
};
|
||||
struct img_t imgs[3] = {
|
||||
{
|
||||
.buf = img1_start,
|
||||
.length = img1_end - img1_start,
|
||||
.w = 227,
|
||||
.h = 149,
|
||||
},
|
||||
{
|
||||
.buf = img2_start,
|
||||
.length = img2_end - img2_start,
|
||||
.w = 320,
|
||||
.h = 240,
|
||||
},
|
||||
{
|
||||
.buf = img3_start,
|
||||
.length = img3_end - img3_start,
|
||||
.w = 480,
|
||||
.h = 320,
|
||||
},
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "pic_index:%d", pic_index);
|
||||
ESP_LOGI(TAG, "lib_index:%d", lib_index);
|
||||
jpg_decode_test(lib_index, DECODE_RGB565, imgs[pic_index].buf, imgs[pic_index].length, imgs[pic_index].w, imgs[pic_index].h, 16);
|
||||
}
|
||||
|
||||
TEST_CASE("Conversions image 227x149 jpeg decode test", "[camera]")
|
||||
{
|
||||
img_jpeg_decode_test(0, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Conversions image 320x240 jpeg decode test", "[camera]")
|
||||
{
|
||||
img_jpeg_decode_test(1, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Conversions image 480x320 jpeg decode test", "[camera]")
|
||||
{
|
||||
img_jpeg_decode_test(2, 0);
|
||||
}
|
||||
BIN
code/components/esp32-camera-master_neu_20220121.zip
Normal file
BIN
code/components/esp32-camera-master_neu_20220121.zip
Normal file
Binary file not shown.
BIN
code/components/esp32-camera-master_old_version.zip
Normal file
BIN
code/components/esp32-camera-master_old_version.zip
Normal file
Binary file not shown.
@@ -39,18 +39,27 @@ bool ConfigFile::GetNextParagraph(std::string& aktparamgraph, bool &disabled, bo
|
||||
bool ConfigFile::getNextLine(std::string *rt, bool &disabled, bool &eof)
|
||||
{
|
||||
eof = false;
|
||||
char zw[1024];
|
||||
char zw[1024] = "";
|
||||
if (pFile == NULL)
|
||||
{
|
||||
*rt = "";
|
||||
return false;
|
||||
}
|
||||
fgets(zw, 1024, pFile);
|
||||
printf("%s", zw);
|
||||
if ((strlen(zw) == 0) && feof(pFile))
|
||||
|
||||
if (fgets(zw, 1024, pFile))
|
||||
{
|
||||
printf("%s", zw);
|
||||
if ((strlen(zw) == 0) && feof(pFile))
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
*rt = zw;
|
||||
|
||||
132
code/components/jomjol_controlGPIO/Color.cpp
Normal file
132
code/components/jomjol_controlGPIO/Color.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
#include "Color.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
|
||||
namespace {
|
||||
|
||||
// Int -> fixed point
|
||||
int up( int x ) { return x * 255; }
|
||||
|
||||
} // namespace
|
||||
|
||||
int iRgbSqrt( int num ) {
|
||||
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29
|
||||
assert( "sqrt input should be non-negative" && num >= 0 );
|
||||
assert( "sqrt input should no exceed 16 bits" && num <= 0xFFFF );
|
||||
int res = 0;
|
||||
int bit = 1 << 16;
|
||||
while ( bit > num )
|
||||
bit >>= 2;
|
||||
while ( bit != 0 ) {
|
||||
if ( num >= res + bit ) {
|
||||
num -= res + bit;
|
||||
res = ( res >> 1 ) + bit;
|
||||
} else
|
||||
res >>= 1;
|
||||
bit >>= 2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Rgb::Rgb( Hsv y ) {
|
||||
// https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python
|
||||
// greyscale
|
||||
if( y.s == 0 ) {
|
||||
r = g = b = y.v;
|
||||
return;
|
||||
}
|
||||
|
||||
const int region = y.h / 43;
|
||||
const int remainder = ( y.h - ( region * 43 ) ) * 6;
|
||||
|
||||
const int p = ( y.v * ( 255 - y.s ) ) >> 8;
|
||||
const int q = ( y.v * ( 255 - ( ( y.s * remainder ) >> 8 ) ) ) >> 8;
|
||||
const int t = ( y.v * ( 255 - ( ( y.s * (255 -remainder ) ) >> 8 ) ) ) >> 8;
|
||||
|
||||
switch( region ) {
|
||||
case 0: r = y.v; g = t; b = p; break;
|
||||
case 1: r = q; g = y.v; b = p; break;
|
||||
case 2: r = p; g = y.v; b = t; break;
|
||||
case 3: r = p; g = q; b = y.v; break;
|
||||
case 4: r = t; g = p; b = y.v; break;
|
||||
case 5: r = y.v; g = p; b = q; break;
|
||||
default: __builtin_trap();
|
||||
}
|
||||
|
||||
a = y.a;
|
||||
}
|
||||
|
||||
Rgb& Rgb::operator=( Hsv hsv ) {
|
||||
Rgb r{ hsv };
|
||||
swap( r );
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rgb Rgb::operator+( Rgb in ) const {
|
||||
auto copy = *this;
|
||||
copy += in;
|
||||
return copy;
|
||||
}
|
||||
|
||||
Rgb& Rgb::operator+=( Rgb in ) {
|
||||
unsigned int red = r + in.r;
|
||||
r = ( red < 255 ) ? red : 255;
|
||||
unsigned int green = g + in.g;
|
||||
g = ( green < 255 ) ? green : 255;
|
||||
unsigned int blue = b + in.b;
|
||||
b = ( blue < 255 ) ? blue : 255;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rgb& Rgb::blend( Rgb in ) {
|
||||
unsigned int inAlpha = in.a * ( 255 - a );
|
||||
unsigned int alpha = a + inAlpha;
|
||||
r = iRgbSqrt( ( ( r * r * a ) + ( in.r * in.r * inAlpha ) ) / alpha );
|
||||
g = iRgbSqrt( ( ( g * g * a ) + ( in.g * in.g * inAlpha ) ) / alpha );
|
||||
b = iRgbSqrt( ( ( b * b * a ) + ( in.b * in.b * inAlpha ) ) / alpha );
|
||||
a = alpha;
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR Rgb::getGrb( int idx ) {
|
||||
switch ( idx ) {
|
||||
case 0: return g;
|
||||
case 1: return r;
|
||||
case 2: return b;
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Hsv::Hsv( Rgb r ) {
|
||||
int min = std::min( r.r, std::min( r.g, r.b ) );
|
||||
int max = std::max( r.r, std::max( r.g, r.b ) );
|
||||
int chroma = max - min;
|
||||
|
||||
v = max;
|
||||
if ( chroma == 0 ) {
|
||||
h = s = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
s = up( chroma ) / max;
|
||||
int hh;
|
||||
if ( max == r.r )
|
||||
hh = ( up( int( r.g ) - int( r.b ) ) ) / chroma / 6;
|
||||
else if ( max == r.g )
|
||||
hh = 255 / 3 + ( up( int( r.b ) - int( r.r ) ) ) / chroma / 6;
|
||||
else
|
||||
hh = 2 * 255 / 3 + ( up( int( r.r ) - int( r.g ) ) ) / chroma / 6;
|
||||
|
||||
if ( hh < 0 )
|
||||
hh += 255;
|
||||
h = hh;
|
||||
|
||||
a = r.a;
|
||||
}
|
||||
|
||||
Hsv& Hsv::operator=( Rgb rgb ) {
|
||||
Hsv h{ rgb };
|
||||
swap( h );
|
||||
return *this;
|
||||
}
|
||||
69
code/components/jomjol_controlGPIO/Color.h
Normal file
69
code/components/jomjol_controlGPIO/Color.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "esp_attr.h"
|
||||
union Hsv;
|
||||
|
||||
union Rgb {
|
||||
struct __attribute__ ((packed)) {
|
||||
uint8_t r, g, b, a;
|
||||
};
|
||||
uint32_t value;
|
||||
|
||||
Rgb( uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255 ) : r( r ), g( g ), b( b ), a( a ) {}
|
||||
Rgb( Hsv c );
|
||||
Rgb& operator=( Rgb rgb ) { swap( rgb ); return *this; }
|
||||
Rgb& operator=( Hsv hsv );
|
||||
Rgb operator+( Rgb in ) const;
|
||||
Rgb& operator+=( Rgb in );
|
||||
bool operator==( Rgb in ) const { return in.value == value; }
|
||||
Rgb& blend( Rgb in );
|
||||
void swap( Rgb& o ) { value = o.value; }
|
||||
void linearize() {
|
||||
r = channelGamma(r);
|
||||
g = channelGamma(g);
|
||||
b = channelGamma(b);
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR getGrb( int idx );
|
||||
|
||||
void stretchChannels( uint8_t maxR, uint8_t maxG, uint8_t maxB ) {
|
||||
r = stretch( r, maxR );
|
||||
g = stretch( g, maxG );
|
||||
b = stretch( b, maxB );
|
||||
}
|
||||
|
||||
void stretchChannelsEvenly( uint8_t max ) {
|
||||
stretchChannels( max, max, max );
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t stretch( int value, uint8_t max ) {
|
||||
return ( value * max ) >> 8;
|
||||
}
|
||||
|
||||
uint8_t channelGamma( int channel ) {
|
||||
/* The optimal gamma correction is x^2.8. However, this is expensive to
|
||||
* compute. Therefore, we use x^3 for gamma correction. Also, we add a
|
||||
* bias as the WS2812 LEDs do not turn on for values less than 4. */
|
||||
if (channel == 0)
|
||||
return channel;
|
||||
channel = channel * channel * channel * 251;
|
||||
channel >>= 24;
|
||||
return static_cast< uint8_t >( 4 + channel );
|
||||
}
|
||||
};
|
||||
|
||||
union Hsv {
|
||||
struct __attribute__ ((packed)) {
|
||||
uint8_t h, s, v, a;
|
||||
};
|
||||
uint32_t value;
|
||||
|
||||
Hsv( uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255 ) : h( h ), s( s ), v( v ), a( a ) {}
|
||||
Hsv( Rgb r );
|
||||
Hsv& operator=( Hsv h ) { swap( h ); return *this; }
|
||||
Hsv& operator=( Rgb rgb );
|
||||
bool operator==( Hsv in ) const { return in.value == value; }
|
||||
void swap( Hsv& o ) { value = o.value; }
|
||||
};
|
||||
63
code/components/jomjol_controlGPIO/SmartLeds.cpp
Normal file
63
code/components/jomjol_controlGPIO/SmartLeds.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "SmartLeds.h"
|
||||
|
||||
IsrCore SmartLed::_interruptCore = CoreCurrent;
|
||||
intr_handle_t SmartLed::_interruptHandle = NULL;
|
||||
|
||||
SmartLed*& IRAM_ATTR SmartLed::ledForChannel( int channel ) {
|
||||
static SmartLed* table[8] = { nullptr };
|
||||
assert( channel < 8 );
|
||||
return table[ channel ];
|
||||
}
|
||||
|
||||
void IRAM_ATTR SmartLed::interruptHandler(void*) {
|
||||
for (int channel = 0; channel != 8; channel++) {
|
||||
auto self = ledForChannel( channel );
|
||||
|
||||
if ( RMT.int_st.val & (1 << (24 + channel ) ) ) { // tx_thr_event
|
||||
if ( self )
|
||||
self->copyRmtHalfBlock();
|
||||
RMT.int_clr.val |= 1 << ( 24 + channel );
|
||||
} else if ( RMT.int_st.val & ( 1 << (3 * channel ) ) ) { // tx_end
|
||||
if ( self )
|
||||
xSemaphoreGiveFromISR( self->_finishedFlag, nullptr );
|
||||
RMT.int_clr.val |= 1 << ( 3 * channel );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR SmartLed::copyRmtHalfBlock() {
|
||||
int offset = detail::MAX_PULSES * _halfIdx;
|
||||
_halfIdx = !_halfIdx;
|
||||
int len = 3 - _componentPosition + 3 * ( _count - 1 );
|
||||
len = std::min( len, detail::MAX_PULSES / 8 );
|
||||
|
||||
if ( !len ) {
|
||||
for ( int i = 0; i < detail::MAX_PULSES; i++) {
|
||||
RMTMEM.chan[ _channel].data32[i + offset ].val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
for ( i = 0; i != len && _pixelPosition != _count; i++ ) {
|
||||
uint8_t val = _buffer[ _pixelPosition ].getGrb( _componentPosition );
|
||||
for ( int j = 0; j != 8; j++, val <<= 1 ) {
|
||||
int bit = val >> 7;
|
||||
int idx = i * 8 + offset + j;
|
||||
RMTMEM.chan[ _channel ].data32[ idx ].val = _bitToRmt[ bit & 0x01 ].value;
|
||||
}
|
||||
if ( _pixelPosition == _count - 1 && _componentPosition == 2 ) {
|
||||
RMTMEM.chan[ _channel ].data32[ i * 8 + offset + 7 ].duration1 =
|
||||
_timing.TRS / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
}
|
||||
|
||||
_componentPosition++;
|
||||
if ( _componentPosition == 3 ) {
|
||||
_componentPosition = 0;
|
||||
_pixelPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i *= 8; i != detail::MAX_PULSES; i++ ) {
|
||||
RMTMEM.chan[ _channel ].data32[ i + offset ].val = 0;
|
||||
}
|
||||
}
|
||||
530
code/components/jomjol_controlGPIO/SmartLeds.h
Normal file
530
code/components/jomjol_controlGPIO/SmartLeds.h
Normal file
@@ -0,0 +1,530 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
|
||||
*
|
||||
* Jan "yaqwsx" Mrázek <email@honzamrazek.cz>
|
||||
*
|
||||
* Based on the work by Martin F. Falatic - https://github.com/FozzTexx/ws2812-demo
|
||||
*/
|
||||
|
||||
/*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#if defined ( ARDUINO )
|
||||
extern "C" { // ...someone forgot to put in the includes...
|
||||
#include "esp32-hal.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_ipc.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "soc/rmt_struct.h"
|
||||
#include <driver/spi_master.h>
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
|
||||
#include "soc/dport_reg.h"
|
||||
#endif
|
||||
}
|
||||
#elif defined ( ESP_PLATFORM )
|
||||
extern "C" { // ...someone forgot to put in the includes...
|
||||
#include <esp_intr_alloc.h>
|
||||
#include <esp_ipc.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <soc/dport_reg.h>
|
||||
#include <soc/gpio_sig_map.h>
|
||||
#include <soc/rmt_struct.h>
|
||||
#include <driver/spi_master.h>
|
||||
}
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "Color.h"
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct TimingParams {
|
||||
uint32_t T0H;
|
||||
uint32_t T1H;
|
||||
uint32_t T0L;
|
||||
uint32_t T1L;
|
||||
uint32_t TRS;
|
||||
};
|
||||
|
||||
union RmtPulsePair {
|
||||
struct {
|
||||
int duration0:15;
|
||||
int level0:1;
|
||||
int duration1:15;
|
||||
int level1:1;
|
||||
};
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
static const int DIVIDER = 4; // 8 still seems to work, but timings become marginal
|
||||
static const int MAX_PULSES = 32; // A channel has a 64 "pulse" buffer - we use half per pass
|
||||
static const double RMT_DURATION_NS = 12.5; // minimum time of a single RMT duration based on clock ns
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using LedType = detail::TimingParams;
|
||||
|
||||
static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
|
||||
static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 };
|
||||
static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 };
|
||||
static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 };
|
||||
|
||||
enum BufferType { SingleBuffer = 0, DoubleBuffer };
|
||||
|
||||
enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2};
|
||||
|
||||
class SmartLed {
|
||||
public:
|
||||
// The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds
|
||||
// can't fill the RMT buffer fast enough, resulting in rendering artifacts.
|
||||
// Usually, that means you have to set isrCore == CoreSecond.
|
||||
//
|
||||
// If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running,
|
||||
// so you can't use it if you define SmartLed as global variable.
|
||||
SmartLed( const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = SingleBuffer, IsrCore isrCore = CoreCurrent)
|
||||
: _timing( type ),
|
||||
_channel( channel ),
|
||||
_count( count ),
|
||||
_firstBuffer( new Rgb[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ),
|
||||
_finishedFlag( xSemaphoreCreateBinary() )
|
||||
{
|
||||
assert( channel >= 0 && channel < 8 );
|
||||
assert( ledForChannel( channel ) == nullptr );
|
||||
|
||||
xSemaphoreGive( _finishedFlag );
|
||||
|
||||
DPORT_SET_PERI_REG_MASK( DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN );
|
||||
DPORT_CLEAR_PERI_REG_MASK( DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST );
|
||||
|
||||
PIN_FUNC_SELECT( GPIO_PIN_MUX_REG[ pin ], 2 );
|
||||
gpio_set_direction( static_cast< gpio_num_t >( pin ), GPIO_MODE_OUTPUT );
|
||||
gpio_matrix_out( static_cast< gpio_num_t >( pin ), RMT_SIG_OUT0_IDX + _channel, 0, 0 );
|
||||
initChannel( _channel );
|
||||
|
||||
RMT.tx_lim_ch[ _channel ].limit = detail::MAX_PULSES;
|
||||
RMT.int_ena.val |= 1 << ( 24 + _channel );
|
||||
RMT.int_ena.val |= 1 << ( 3 * _channel );
|
||||
|
||||
_bitToRmt[ 0 ].level0 = 1;
|
||||
_bitToRmt[ 0 ].level1 = 0;
|
||||
_bitToRmt[ 0 ].duration0 = _timing.T0H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
_bitToRmt[ 0 ].duration1 = _timing.T0L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
|
||||
_bitToRmt[ 1 ].level0 = 1;
|
||||
_bitToRmt[ 1 ].level1 = 0;
|
||||
_bitToRmt[ 1 ].duration0 = _timing.T1H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
_bitToRmt[ 1 ].duration1 = _timing.T1L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
|
||||
if ( !anyAlive() ) {
|
||||
_interruptCore = isrCore;
|
||||
if(isrCore != CoreCurrent) {
|
||||
ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, NULL));
|
||||
} else {
|
||||
registerInterrupt(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
ledForChannel( channel ) = this;
|
||||
}
|
||||
|
||||
~SmartLed() {
|
||||
ledForChannel( _channel ) = nullptr;
|
||||
if ( !anyAlive() ) {
|
||||
if(_interruptCore != CoreCurrent) {
|
||||
ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, NULL));
|
||||
} else {
|
||||
unregisterInterrupt(NULL);
|
||||
}
|
||||
}
|
||||
vSemaphoreDelete( _finishedFlag );
|
||||
}
|
||||
|
||||
Rgb& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
const Rgb& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
startTransmission();
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
bool wait( TickType_t timeout = portMAX_DELAY ) {
|
||||
if( xSemaphoreTake( _finishedFlag, timeout ) == pdTRUE ) {
|
||||
xSemaphoreGive( _finishedFlag );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int size() const {
|
||||
return _count;
|
||||
}
|
||||
|
||||
Rgb *begin() { return _firstBuffer.get(); }
|
||||
const Rgb *begin() const { return _firstBuffer.get(); }
|
||||
const Rgb *cbegin() const { return _firstBuffer.get(); }
|
||||
|
||||
Rgb *end() { return _firstBuffer.get() + _count; }
|
||||
const Rgb *end() const { return _firstBuffer.get() + _count; }
|
||||
const Rgb *cend() const { return _firstBuffer.get() + _count; }
|
||||
|
||||
private:
|
||||
static intr_handle_t _interruptHandle;
|
||||
static IsrCore _interruptCore;
|
||||
|
||||
static void initChannel( int channel ) {
|
||||
RMT.apb_conf.fifo_mask = 1; //enable memory access, instead of FIFO mode.
|
||||
RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
|
||||
RMT.conf_ch[ channel ].conf0.div_cnt = detail::DIVIDER;
|
||||
RMT.conf_ch[ channel ].conf0.mem_size = 1;
|
||||
RMT.conf_ch[ channel ].conf0.carrier_en = 0;
|
||||
RMT.conf_ch[ channel ].conf0.carrier_out_lv = 1;
|
||||
RMT.conf_ch[ channel ].conf0.mem_pd = 0;
|
||||
|
||||
RMT.conf_ch[ channel ].conf1.rx_en = 0;
|
||||
RMT.conf_ch[ channel ].conf1.mem_owner = 0;
|
||||
RMT.conf_ch[ channel ].conf1.tx_conti_mode = 0; //loop back mode.
|
||||
RMT.conf_ch[ channel ].conf1.ref_always_on = 1; // use apb clock: 80M
|
||||
RMT.conf_ch[ channel ].conf1.idle_out_en = 1;
|
||||
RMT.conf_ch[ channel ].conf1.idle_out_lv = 0;
|
||||
}
|
||||
|
||||
static void registerInterrupt(void *) {
|
||||
ESP_ERROR_CHECK(esp_intr_alloc( ETS_RMT_INTR_SOURCE, 0, interruptHandler, nullptr, &_interruptHandle));
|
||||
}
|
||||
|
||||
static void unregisterInterrupt(void*) {
|
||||
esp_intr_free( _interruptHandle );
|
||||
}
|
||||
|
||||
static SmartLed*& IRAM_ATTR ledForChannel( int channel );
|
||||
static void IRAM_ATTR interruptHandler( void* );
|
||||
|
||||
void IRAM_ATTR copyRmtHalfBlock();
|
||||
|
||||
void swapBuffers() {
|
||||
if ( _secondBuffer )
|
||||
_firstBuffer.swap( _secondBuffer );
|
||||
}
|
||||
|
||||
void startTransmission() {
|
||||
// Invalid use of the library
|
||||
if( xSemaphoreTake( _finishedFlag, 0 ) != pdTRUE )
|
||||
abort();
|
||||
|
||||
_pixelPosition = _componentPosition = _halfIdx = 0;
|
||||
copyRmtHalfBlock();
|
||||
if ( _pixelPosition < _count )
|
||||
copyRmtHalfBlock();
|
||||
|
||||
RMT.conf_ch[ _channel ].conf1.mem_rd_rst = 1;
|
||||
RMT.conf_ch[ _channel ].conf1.tx_start = 1;
|
||||
}
|
||||
|
||||
static bool anyAlive() {
|
||||
for ( int i = 0; i != 8; i++ )
|
||||
if ( ledForChannel( i ) != nullptr ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const LedType& _timing;
|
||||
int _channel;
|
||||
detail::RmtPulsePair _bitToRmt[ 2 ];
|
||||
int _count;
|
||||
std::unique_ptr< Rgb[] > _firstBuffer;
|
||||
std::unique_ptr< Rgb[] > _secondBuffer;
|
||||
Rgb *_buffer;
|
||||
|
||||
xSemaphoreHandle _finishedFlag;
|
||||
|
||||
int _pixelPosition;
|
||||
int _componentPosition;
|
||||
int _halfIdx;
|
||||
};
|
||||
|
||||
class Apa102 {
|
||||
public:
|
||||
struct ApaRgb {
|
||||
ApaRgb( uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF )
|
||||
: v( 0xE0 | v ), b( b ), g( g ), r( r )
|
||||
{}
|
||||
|
||||
ApaRgb& operator=( const Rgb& o ) {
|
||||
r = o.r;
|
||||
g = o.g;
|
||||
b = o.b;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ApaRgb& operator=( const Hsv& o ) {
|
||||
*this = Rgb{ o };
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8_t v, b, g, r;
|
||||
};
|
||||
|
||||
static const int FINAL_FRAME_SIZE = 4;
|
||||
static const int TRANS_COUNT = 2 + 8;
|
||||
|
||||
Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer )
|
||||
: _count( count ),
|
||||
_firstBuffer( new ApaRgb[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ),
|
||||
_initFrame( 0 )
|
||||
{
|
||||
spi_bus_config_t buscfg;
|
||||
memset( &buscfg, 0, sizeof( buscfg ) );
|
||||
buscfg.mosi_io_num = datapin;
|
||||
buscfg.miso_io_num = -1;
|
||||
buscfg.sclk_io_num = clkpin;
|
||||
buscfg.quadwp_io_num = -1;
|
||||
buscfg.quadhd_io_num = -1;
|
||||
buscfg.max_transfer_sz = 65535;
|
||||
|
||||
spi_device_interface_config_t devcfg;
|
||||
memset( &devcfg, 0, sizeof( devcfg ) );
|
||||
devcfg.clock_speed_hz = 1000000;
|
||||
devcfg.mode = 0;
|
||||
devcfg.spics_io_num = -1;
|
||||
devcfg.queue_size = TRANS_COUNT;
|
||||
devcfg.pre_cb = nullptr;
|
||||
|
||||
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
std::fill_n( _finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF );
|
||||
}
|
||||
|
||||
~Apa102() {
|
||||
// ToDo
|
||||
}
|
||||
|
||||
ApaRgb& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
const ApaRgb& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
startTransmission();
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
for ( int i = 0; i != _transCount; i++ ) {
|
||||
spi_transaction_t *t;
|
||||
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
|
||||
}
|
||||
}
|
||||
private:
|
||||
void swapBuffers() {
|
||||
if ( _secondBuffer )
|
||||
_firstBuffer.swap( _secondBuffer );
|
||||
}
|
||||
|
||||
void startTransmission() {
|
||||
for ( int i = 0; i != TRANS_COUNT; i++ ) {
|
||||
_transactions[ i ].cmd = 0;
|
||||
_transactions[ i ].addr = 0;
|
||||
_transactions[ i ].flags = 0;
|
||||
_transactions[ i ].rxlength = 0;
|
||||
_transactions[ i ].rx_buffer = nullptr;
|
||||
}
|
||||
// Init frame
|
||||
_transactions[ 0 ].length = 32;
|
||||
_transactions[ 0 ].tx_buffer = &_initFrame;
|
||||
spi_device_queue_trans( _spi, _transactions + 0, portMAX_DELAY );
|
||||
// Data
|
||||
_transactions[ 1 ].length = 32 * _count;
|
||||
_transactions[ 1 ].tx_buffer = _buffer;
|
||||
spi_device_queue_trans( _spi, _transactions + 1, portMAX_DELAY );
|
||||
_transCount = 2;
|
||||
// End frame
|
||||
for ( int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++ ) {
|
||||
_transactions[ 2 + i ].length = 32 * FINAL_FRAME_SIZE;
|
||||
_transactions[ 2 + i ].tx_buffer = _finalFrame;
|
||||
spi_device_queue_trans( _spi, _transactions + 2 + i, portMAX_DELAY );
|
||||
_transCount++;
|
||||
}
|
||||
}
|
||||
|
||||
spi_device_handle_t _spi;
|
||||
int _count;
|
||||
std::unique_ptr< ApaRgb[] > _firstBuffer, _secondBuffer;
|
||||
ApaRgb *_buffer;
|
||||
|
||||
spi_transaction_t _transactions[ TRANS_COUNT ];
|
||||
int _transCount;
|
||||
|
||||
uint32_t _initFrame;
|
||||
uint32_t _finalFrame[ FINAL_FRAME_SIZE ];
|
||||
};
|
||||
|
||||
class LDP8806 {
|
||||
public:
|
||||
struct LDP8806_GRB {
|
||||
|
||||
LDP8806_GRB( uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0 )
|
||||
: g( g_7bit ), r( r_7bit ), b( b_7bit )
|
||||
{
|
||||
}
|
||||
|
||||
LDP8806_GRB& operator=( const Rgb& o ) {
|
||||
//Convert 8->7bit colour
|
||||
r = ( o.r * 127 / 256 ) | 0x80;
|
||||
g = ( o.g * 127 / 256 ) | 0x80;
|
||||
b = ( o.b * 127 / 256 ) | 0x80;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LDP8806_GRB& operator=( const Hsv& o ) {
|
||||
*this = Rgb{ o };
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8_t g, r, b;
|
||||
};
|
||||
|
||||
static const int LED_FRAME_SIZE_BYTES = sizeof( LDP8806_GRB );
|
||||
static const int LATCH_FRAME_SIZE_BYTES = 3;
|
||||
static const int TRANS_COUNT_MAX = 20;//Arbitrary, supports up to 600 LED
|
||||
|
||||
LDP8806( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000 )
|
||||
: _count( count ),
|
||||
_firstBuffer( new LDP8806_GRB[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ),
|
||||
// one 'latch'/start-of-data mark frame for every 32 leds
|
||||
_latchFrames( ( count + 31 ) / 32 )
|
||||
{
|
||||
spi_bus_config_t buscfg;
|
||||
memset( &buscfg, 0, sizeof( buscfg ) );
|
||||
buscfg.mosi_io_num = datapin;
|
||||
buscfg.miso_io_num = -1;
|
||||
buscfg.sclk_io_num = clkpin;
|
||||
buscfg.quadwp_io_num = -1;
|
||||
buscfg.quadhd_io_num = -1;
|
||||
buscfg.max_transfer_sz = 65535;
|
||||
|
||||
spi_device_interface_config_t devcfg;
|
||||
memset( &devcfg, 0, sizeof( devcfg ) );
|
||||
devcfg.clock_speed_hz = clock_speed_hz;
|
||||
devcfg.mode = 0;
|
||||
devcfg.spics_io_num = -1;
|
||||
devcfg.queue_size = TRANS_COUNT_MAX;
|
||||
devcfg.pre_cb = nullptr;
|
||||
|
||||
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
std::fill_n( _latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0 );
|
||||
}
|
||||
|
||||
~LDP8806() {
|
||||
// noop
|
||||
}
|
||||
|
||||
LDP8806_GRB& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
const LDP8806_GRB& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
startTransmission();
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
while ( _transCount-- ) {
|
||||
spi_transaction_t *t;
|
||||
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
|
||||
}
|
||||
}
|
||||
private:
|
||||
void swapBuffers() {
|
||||
if ( _secondBuffer )
|
||||
_firstBuffer.swap( _secondBuffer );
|
||||
}
|
||||
|
||||
void startTransmission() {
|
||||
_transCount = 0;
|
||||
for ( int i = 0; i != TRANS_COUNT_MAX; i++ ) {
|
||||
_transactions[ i ].cmd = 0;
|
||||
_transactions[ i ].addr = 0;
|
||||
_transactions[ i ].flags = 0;
|
||||
_transactions[ i ].rxlength = 0;
|
||||
_transactions[ i ].rx_buffer = nullptr;
|
||||
}
|
||||
// LED Data
|
||||
_transactions[ 0 ].length = ( LED_FRAME_SIZE_BYTES * 8 ) * _count;
|
||||
_transactions[ 0 ].tx_buffer = _buffer;
|
||||
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
|
||||
_transCount++;
|
||||
|
||||
// 'latch'/start-of-data marker frames
|
||||
for ( int i = 0; i < _latchFrames; i++ ) {
|
||||
_transactions[ _transCount ].length = ( LATCH_FRAME_SIZE_BYTES * 8 );
|
||||
_transactions[ _transCount ].tx_buffer = _latchBuffer;
|
||||
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
|
||||
_transCount++;
|
||||
}
|
||||
}
|
||||
|
||||
spi_device_handle_t _spi;
|
||||
int _count;
|
||||
std::unique_ptr< LDP8806_GRB[] > _firstBuffer, _secondBuffer;
|
||||
LDP8806_GRB *_buffer;
|
||||
|
||||
spi_transaction_t _transactions[ TRANS_COUNT_MAX ];
|
||||
int _transCount;
|
||||
|
||||
int _latchFrames;
|
||||
uint8_t _latchBuffer[ LATCH_FRAME_SIZE_BYTES ];
|
||||
};
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
#include "server_tflite.h"
|
||||
|
||||
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#include "esp_log.h"
|
||||
//#include "errno.h"
|
||||
@@ -105,7 +107,8 @@ void GpioPin::init()
|
||||
//configure GPIO with the given settings
|
||||
gpio_config(&io_conf);
|
||||
|
||||
if (_interruptType != GPIO_INTR_DISABLE) {
|
||||
// if (_interruptType != GPIO_INTR_DISABLE) { // ohne GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X, wenn das genutzt wird, dann soll auch der Handler hier nicht initialisiert werden, da das dann über SmartLED erfolgt.
|
||||
if ((_interruptType != GPIO_INTR_DISABLE) && (_interruptType != GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)) {
|
||||
//hook isr handler for specific gpio pin
|
||||
ESP_LOGD(TAG_SERVERGPIO, "GpioPin::init add isr handler for GPIO %d\r\n", _gpio);
|
||||
gpio_isr_handler_add(_gpio, gpio_isr_handler, (void*)&_gpio);
|
||||
@@ -210,12 +213,15 @@ void GpioHandler::init()
|
||||
// printf("wait before start %ldms\r\n", (long) xDelay);
|
||||
// vTaskDelay( xDelay );
|
||||
|
||||
printf("*************** Start GPIOHandler_Init *****************\n");
|
||||
|
||||
if (gpioMap == NULL) {
|
||||
gpioMap = new std::map<gpio_num_t, GpioPin*>();
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
ESP_LOGI(TAG_SERVERGPIO, "read GPIO config and init GPIO");
|
||||
if (!readConfig()) {
|
||||
clear();
|
||||
@@ -225,6 +231,7 @@ void GpioHandler::init()
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) {
|
||||
it->second->init();
|
||||
}
|
||||
@@ -291,17 +298,32 @@ bool GpioHandler::readConfig()
|
||||
std::string line = "";
|
||||
bool disabledLine = false;
|
||||
bool eof = false;
|
||||
gpio_num_t gpioExtLED = (gpio_num_t) 0;
|
||||
|
||||
// printf("readConfig - Start 1\n");
|
||||
|
||||
while ((!configFile.GetNextParagraph(line, disabledLine, eof) || (line.compare("[GPIO]") != 0)) && !disabledLine && !eof) {}
|
||||
while ((!configFile.GetNextParagraph(line, disabledLine, eof) || (line.compare("[GPIO]") != 0)) && !eof) {}
|
||||
if (eof)
|
||||
return false;
|
||||
|
||||
|
||||
// printf("readConfig - Start 2 line: %s, disabbledLine: %d\n", line.c_str(), (int) disabledLine);
|
||||
|
||||
|
||||
_isEnabled = !disabledLine;
|
||||
|
||||
if (!_isEnabled)
|
||||
return false;
|
||||
|
||||
std::string mainTopicMQTT = "";
|
||||
// printf("readConfig - Start 3\n");
|
||||
|
||||
// std::string mainTopicMQTT = "";
|
||||
std::string mainTopicMQTT = GetMQTTMainTopic();
|
||||
if (mainTopicMQTT.length() > 0)
|
||||
{
|
||||
mainTopicMQTT = mainTopicMQTT + "/GPIO";
|
||||
ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found\r\n");
|
||||
}
|
||||
|
||||
bool registerISR = false;
|
||||
while (configFile.getNextLine(&line, disabledLine, eof) && !configFile.isNewParagraph(line))
|
||||
{
|
||||
@@ -313,8 +335,8 @@ bool GpioHandler::readConfig()
|
||||
// std::string gpioStr = pieces_match[1];
|
||||
ESP_LOGD(TAG_SERVERGPIO, "conf param %s\r\n", toUpper(zerlegt[0]).c_str());
|
||||
if (toUpper(zerlegt[0]) == "MAINTOPICMQTT") {
|
||||
ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found\r\n");
|
||||
mainTopicMQTT = zerlegt[1];
|
||||
// ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found\r\n");
|
||||
// mainTopicMQTT = zerlegt[1];
|
||||
} else if ((zerlegt[0].rfind("IO", 0) == 0) && (zerlegt.size() >= 6))
|
||||
{
|
||||
ESP_LOGI(TAG_SERVERGPIO,"Enable GP%s in %s mode", zerlegt[0].c_str(), zerlegt[1].c_str());
|
||||
@@ -335,10 +357,40 @@ bool GpioHandler::readConfig()
|
||||
GpioPin* gpioPin = new GpioPin(gpioNr, gpioName, pinMode, intType,dutyResolution, mqttTopic, httpEnabled);
|
||||
(*gpioMap)[gpioNr] = gpioPin;
|
||||
|
||||
if (pinMode == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)
|
||||
{
|
||||
printf("Set WS2812 to GPIO %d\n", gpioNr);
|
||||
gpioExtLED = gpioNr;
|
||||
}
|
||||
|
||||
if (intType != GPIO_INTR_DISABLE) {
|
||||
registerISR = true;
|
||||
}
|
||||
}
|
||||
if (toUpper(zerlegt[0]) == "LEDNUMBERS")
|
||||
{
|
||||
LEDNumbers = stoi(zerlegt[1]);
|
||||
}
|
||||
if (toUpper(zerlegt[0]) == "LEDCOLOR")
|
||||
{
|
||||
uint8_t _r, _g, _b;
|
||||
_r = stoi(zerlegt[1]);
|
||||
_g = stoi(zerlegt[2]);
|
||||
_b = stoi(zerlegt[3]);
|
||||
|
||||
LEDColor = Rgb{_r, _g, _b};
|
||||
}
|
||||
if (toUpper(zerlegt[0]) == "LEDTYPE")
|
||||
{
|
||||
if (zerlegt[1] == "WS2812")
|
||||
LEDType = LED_WS2812;
|
||||
if (zerlegt[1] == "WS2812B")
|
||||
LEDType = LED_WS2812B;
|
||||
if (zerlegt[1] == "SK6812")
|
||||
LEDType = LED_SK6812;
|
||||
if (zerlegt[1] == "WS2813")
|
||||
LEDType = LED_WS2813;
|
||||
}
|
||||
}
|
||||
|
||||
if (registerISR) {
|
||||
@@ -346,6 +398,23 @@ bool GpioHandler::readConfig()
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM);
|
||||
}
|
||||
|
||||
if (gpioExtLED > 0)
|
||||
{
|
||||
// LogFile.WriteToFile("Startsequence 06"); // Nremove
|
||||
// vTaskDelay( xDelay );
|
||||
// xDelay = 5000 / portTICK_PERIOD_MS;
|
||||
// printf("main: sleep for : %ldms\n", (long) xDelay);
|
||||
|
||||
// SmartLed leds( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer );
|
||||
|
||||
|
||||
// leds[ 0 ] = Rgb{ 255, 0, 0 };
|
||||
// leds[ 1 ] = Rgb{ 255, 255, 255 };
|
||||
// leds.show();
|
||||
// SmartLed leds = new SmartLed(LEDType, LEDNumbers, gpioExtLED, 0, DoubleBuffer);
|
||||
// _SmartLED = new SmartLed( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -487,7 +556,47 @@ void GpioHandler::flashLightEnable(bool value)
|
||||
} else {
|
||||
ESP_LOGE(TAG_SERVERGPIO, "Can't set flash light pin GPIO %d. Error: %s\r\n", (int)it->first, resp_str.c_str());
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)
|
||||
{
|
||||
#ifdef __LEDGLOBAL
|
||||
if (leds_global == NULL) {
|
||||
ESP_LOGI(TAG_SERVERGPIO, "init SmartLed: LEDNumber=%d, GPIO=%d", LEDNumbers, (int)it->second->getGPIO());
|
||||
leds_global = new SmartLed( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer );
|
||||
} else {
|
||||
// wait until we can update: https://github.com/RoboticsBrno/SmartLeds/issues/10#issuecomment-386921623
|
||||
leds_global->wait();
|
||||
}
|
||||
#else
|
||||
SmartLed leds( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer );
|
||||
#endif
|
||||
|
||||
if (value)
|
||||
{
|
||||
for (int i = 0; i < LEDNumbers; ++i)
|
||||
#ifdef __LEDGLOBAL
|
||||
(*leds_global)[i] = LEDColor;
|
||||
#else
|
||||
leds[i] = LEDColor;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < LEDNumbers; ++i)
|
||||
#ifdef __LEDGLOBAL
|
||||
(*leds_global)[i] = Rgb{0, 0, 0};
|
||||
#else
|
||||
leds[i] = Rgb{0, 0, 0};
|
||||
#endif
|
||||
}
|
||||
#ifdef __LEDGLOBAL
|
||||
leds_global->show();
|
||||
#else
|
||||
leds.show();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,13 @@
|
||||
#include <map>
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "SmartLeds.h"
|
||||
|
||||
//#include "ClassControllCamera.h"
|
||||
|
||||
// wenn __LEDGLOBAL definiert ist, wird eine globale Variable für die LED-Ansteuerung verwendet, ansonsten lokal und jedesmal neu
|
||||
#define __LEDGLOBAL
|
||||
|
||||
typedef enum {
|
||||
GPIO_PIN_MODE_DISABLED = 0x0,
|
||||
GPIO_PIN_MODE_INPUT = 0x1,
|
||||
@@ -45,6 +50,7 @@ public:
|
||||
void gpioInterrupt(int value);
|
||||
gpio_int_type_t getInterruptType() { return _interruptType; }
|
||||
gpio_pin_mode_t getMode() { return _mode; }
|
||||
gpio_num_t getGPIO(){return _gpio;};
|
||||
|
||||
private:
|
||||
gpio_num_t _gpio;
|
||||
@@ -80,6 +86,13 @@ private:
|
||||
TaskHandle_t xHandleTaskGpio = NULL;
|
||||
bool _isEnabled = false;
|
||||
|
||||
int LEDNumbers = 2;
|
||||
Rgb LEDColor = Rgb{ 255, 255, 255 };
|
||||
LedType LEDType = LED_WS2812;
|
||||
#ifdef __LEDGLOBAL
|
||||
SmartLed *leds_global = NULL;
|
||||
#endif
|
||||
|
||||
bool readConfig();
|
||||
void clear();
|
||||
|
||||
|
||||
@@ -30,10 +30,26 @@
|
||||
|
||||
// #define DEBUG_DETAIL_ON
|
||||
|
||||
#define USE_PWM_LEDFLASH
|
||||
|
||||
#ifdef USE_PWM_LEDFLASH
|
||||
|
||||
//// PWM für Flash-LED
|
||||
#define LEDC_TIMER LEDC_TIMER_1 // LEDC_TIMER_0
|
||||
#define LEDC_MODE LEDC_LOW_SPEED_MODE
|
||||
#define LEDC_OUTPUT_IO (4) // Define the output GPIO
|
||||
#define LEDC_CHANNEL LEDC_CHANNEL_1
|
||||
#define LEDC_DUTY_RES LEDC_TIMER_13_BIT // Set duty resolution to 13 bits
|
||||
//#define LEDC_DUTY (195) // Set duty to 50%. ((2 ** 13) - 1) * 50% = 4095
|
||||
#define LEDC_FREQUENCY (5000) // Frequency in Hertz. Set frequency at 5 kHz
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// ESP32Cam (AiThinker) PIN Map
|
||||
|
||||
#define CAM_PIN_PWDN (gpio_num_t) 32
|
||||
#define CAM_PIN_PWDN 32
|
||||
#define CAM_PIN_RESET -1 //software reset will be performed
|
||||
#define CAM_PIN_XCLK 0
|
||||
#define CAM_PIN_SIOD 26
|
||||
@@ -51,6 +67,7 @@
|
||||
#define CAM_PIN_HREF 23
|
||||
#define CAM_PIN_PCLK 22
|
||||
|
||||
|
||||
static const char *TAGCAMERACLASS = "server_part_camera";
|
||||
|
||||
static camera_config_t camera_config = {
|
||||
@@ -73,19 +90,20 @@ static camera_config_t camera_config = {
|
||||
.pin_pclk = CAM_PIN_PCLK,
|
||||
|
||||
//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
|
||||
// .xclk_freq_hz = 20000000, // Orginalwert
|
||||
.xclk_freq_hz = 5000000, // Test, um die Bildfehler los zu werden !!!!
|
||||
.xclk_freq_hz = 20000000, // Orginalwert
|
||||
// .xclk_freq_hz = 5000000, // Test, um die Bildfehler los zu werden !!!! Hängt in Version 9.2 !!!!
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
|
||||
.pixel_format = PIXFORMAT_JPEG, //YUV422,GRAYSCALE,RGB565,JPEG
|
||||
.frame_size = FRAMESIZE_VGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
|
||||
// .frame_size = FRAMESIZE_UXGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
|
||||
|
||||
.jpeg_quality = 12, //0-63 lower number means higher quality
|
||||
.fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
.fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */
|
||||
// .grab_mode = CAMERA_GRAB_WHEN_EMPTY,
|
||||
.grab_mode = CAMERA_GRAB_LATEST, // erst ab neuer esp32cam-version
|
||||
|
||||
|
||||
.jpeg_quality = 5, //0-63 lower number means higher quality
|
||||
.fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
};
|
||||
|
||||
|
||||
@@ -102,29 +120,36 @@ typedef struct {
|
||||
} jpg_chunking_t;
|
||||
|
||||
|
||||
#define LEDC_LS_CH2_GPIO (4)
|
||||
#define LEDC_LS_CH2_CHANNEL LEDC_CHANNEL_2
|
||||
#define LEDC_LS_TIMER LEDC_TIMER_1
|
||||
#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE
|
||||
#define LEDC_TEST_DUTY (4000)
|
||||
void CCamera::ledc_init(void)
|
||||
{
|
||||
#ifdef USE_PWM_LEDFLASH
|
||||
|
||||
void test(){
|
||||
// Prepare and then apply the LEDC PWM timer configuration
|
||||
ledc_timer_config_t ledc_timer = { };
|
||||
|
||||
ledc_timer.speed_mode = LEDC_MODE;
|
||||
ledc_timer.timer_num = LEDC_TIMER;
|
||||
ledc_timer.duty_resolution = LEDC_DUTY_RES;
|
||||
ledc_timer.freq_hz = LEDC_FREQUENCY; // Set output frequency at 5 kHz
|
||||
ledc_timer.clk_cfg = LEDC_AUTO_CLK;
|
||||
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
||||
|
||||
// Prepare and then apply the LEDC PWM channel configuration
|
||||
ledc_channel_config_t ledc_channel = { };
|
||||
|
||||
ledc_channel.channel = LEDC_LS_CH2_CHANNEL;
|
||||
ledc_channel.duty = 0;
|
||||
ledc_channel.gpio_num = FLASH_GPIO;
|
||||
ledc_channel.speed_mode = LEDC_LS_MODE;
|
||||
ledc_channel.hpoint = 0;
|
||||
ledc_channel.timer_sel = LEDC_LS_TIMER;
|
||||
ledc_channel.speed_mode = LEDC_MODE;
|
||||
ledc_channel.channel = LEDC_CHANNEL;
|
||||
ledc_channel.timer_sel = LEDC_TIMER;
|
||||
ledc_channel.intr_type = LEDC_INTR_DISABLE;
|
||||
ledc_channel.gpio_num = LEDC_OUTPUT_IO;
|
||||
ledc_channel.duty = 0; // Set duty to 0%
|
||||
ledc_channel.hpoint = 0;
|
||||
|
||||
ledc_channel_config(&ledc_channel);
|
||||
|
||||
ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, LEDC_TEST_DUTY);
|
||||
ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
};
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){
|
||||
@@ -147,9 +172,11 @@ bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, in
|
||||
_brightness = min(2, max(-2, _brightness));
|
||||
if (_contrast > -100)
|
||||
_contrast = min(2, max(-2, _contrast));
|
||||
// _saturation = min(2, max(-2, _saturation));
|
||||
if (_saturation > -100)
|
||||
_saturation = min(2, max(-2, _saturation));
|
||||
|
||||
// s->set_saturation(s, _saturation);
|
||||
if (_saturation > -100)
|
||||
s->set_saturation(s, _saturation);
|
||||
if (_contrast > -100)
|
||||
s->set_contrast(s, _contrast);
|
||||
if (_brightness > -100)
|
||||
@@ -222,6 +249,7 @@ void CCamera::SetQualitySize(int qual, framesize_t resol)
|
||||
|
||||
void CCamera::EnableAutoExposure(int flashdauer)
|
||||
{
|
||||
printf("EnableAutoExposure");
|
||||
LEDOnOff(true);
|
||||
if (flashdauer > 0)
|
||||
LightOnOff(true);
|
||||
@@ -229,6 +257,8 @@ void CCamera::EnableAutoExposure(int flashdauer)
|
||||
vTaskDelay( xDelay );
|
||||
|
||||
camera_fb_t * fb = esp_camera_fb_get();
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
ESP_LOGE(TAGCAMERACLASS, "Camera Capture Failed");
|
||||
LEDOnOff(false);
|
||||
@@ -275,10 +305,16 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
|
||||
#endif
|
||||
|
||||
camera_fb_t * fb = esp_camera_fb_get();
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
ESP_LOGE(TAGCAMERACLASS, "CaptureToBasisImage: Camera Capture Failed");
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
|
||||
LogFile.SwitchOnOff(true);
|
||||
LogFile.WriteToFile("Camera is not working anymore - most propably hardware problem (instablility, ...). "
|
||||
"System will reboot.");
|
||||
doReboot();
|
||||
|
||||
return ESP_FAIL;
|
||||
@@ -286,6 +322,12 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
|
||||
|
||||
int _size = fb->len;
|
||||
zwischenspeicher = (uint8_t*) malloc(_size);
|
||||
if (!zwischenspeicher)
|
||||
{
|
||||
ESP_LOGE(TAGCAMERACLASS, "Nicht ausreichend Speicherplatz für Bild in Funktion CaptureToBasisImage()");
|
||||
LogFile.SwitchOnOff(true);
|
||||
LogFile.WriteToFile("Nicht ausreichend Speicherplatz für Bild in Funktion CaptureToBasisImage()");
|
||||
}
|
||||
for (int i = 0; i < _size; ++i)
|
||||
*(zwischenspeicher + i) = *(fb->buf + i);
|
||||
esp_camera_fb_return(fb);
|
||||
@@ -362,6 +404,8 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
|
||||
}
|
||||
|
||||
camera_fb_t * fb = esp_camera_fb_get();
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
ESP_LOGE(TAGCAMERACLASS, "CaptureToFile: Camera Capture Failed");
|
||||
LEDOnOff(false);
|
||||
@@ -453,6 +497,8 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
|
||||
}
|
||||
|
||||
|
||||
fb = esp_camera_fb_get();
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
ESP_LOGE(TAGCAMERACLASS, "Camera capture failed");
|
||||
@@ -499,17 +545,34 @@ void CCamera::LightOnOff(bool status)
|
||||
{
|
||||
GpioHandler* gpioHandler = gpio_handler_get();
|
||||
if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) {
|
||||
printf("Use gpioHandler flashLigh\n");
|
||||
gpioHandler->flashLightEnable(status);
|
||||
} else {
|
||||
#ifdef USE_PWM_LEDFLASH
|
||||
if (status)
|
||||
{
|
||||
printf("Internal Flash-LED turn on with PWM %d\n", led_intensity);
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_intensity));
|
||||
// Update duty to apply the new value
|
||||
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Internal Flash-LED turn off PWM\n");
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0));
|
||||
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
|
||||
}
|
||||
#else
|
||||
// Init the GPIO
|
||||
gpio_pad_select_gpio(FLASH_GPIO);
|
||||
/* Set the GPIO as a push/pull output */
|
||||
// Set the GPIO as a push/pull output
|
||||
gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT);
|
||||
|
||||
if (status)
|
||||
gpio_set_level(FLASH_GPIO, 1);
|
||||
else
|
||||
gpio_set_level(FLASH_GPIO, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,18 +663,12 @@ CCamera::CCamera()
|
||||
contrast = -5;
|
||||
saturation = -5;
|
||||
isFixedExposure = false;
|
||||
|
||||
ledc_init();
|
||||
}
|
||||
|
||||
esp_err_t CCamera::InitCam()
|
||||
{
|
||||
if(CAM_PIN_PWDN != -1){
|
||||
// Init the GPIO
|
||||
gpio_pad_select_gpio(CAM_PIN_PWDN);
|
||||
/* Set the GPIO as a push/pull output */
|
||||
gpio_set_direction(CAM_PIN_PWDN, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(CAM_PIN_PWDN, 0);
|
||||
}
|
||||
|
||||
printf("Init Camera\n");
|
||||
ActualQuality = camera_config.jpeg_quality;
|
||||
ActualResolution = camera_config.frame_size;
|
||||
@@ -623,4 +680,14 @@ esp_err_t CCamera::InitCam()
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
void CCamera::SetLEDIntensity(float _intrel)
|
||||
{
|
||||
_intrel = min(_intrel, (float) 100);
|
||||
_intrel = max(_intrel, (float) 0);
|
||||
_intrel = _intrel / 100;
|
||||
led_intensity = (int) (_intrel * 8191);
|
||||
printf("Set led_intensity to %d of 8191\n", led_intensity);
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ class CCamera {
|
||||
int brightness, contrast, saturation;
|
||||
bool isFixedExposure;
|
||||
int waitbeforepicture_org;
|
||||
int led_intensity = 4095;
|
||||
|
||||
void ledc_init(void);
|
||||
|
||||
public:
|
||||
int image_height, image_width;
|
||||
@@ -36,6 +39,7 @@ class CCamera {
|
||||
void SetQualitySize(int qual, framesize_t resol);
|
||||
bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation);
|
||||
void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol);
|
||||
void SetLEDIntensity(float _intrel);
|
||||
|
||||
void EnableAutoExposure(int flashdauer);
|
||||
|
||||
|
||||
@@ -11,11 +11,48 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define OV9650_PID (0x96)
|
||||
#define OV7725_PID (0x77)
|
||||
#define OV2640_PID (0x26)
|
||||
#define OV3660_PID (0x36)
|
||||
#define OV5640_PID (0x56)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
OV9650_PID = 0x96,
|
||||
OV7725_PID = 0x77,
|
||||
OV2640_PID = 0x26,
|
||||
OV3660_PID = 0x3660,
|
||||
OV5640_PID = 0x5640,
|
||||
OV7670_PID = 0x76,
|
||||
NT99141_PID = 0x1410,
|
||||
GC2145_PID = 0x2145,
|
||||
GC032A_PID = 0x232a,
|
||||
GC0308_PID = 0x9b,
|
||||
} camera_pid_t;
|
||||
|
||||
typedef enum {
|
||||
CAMERA_OV7725,
|
||||
CAMERA_OV2640,
|
||||
CAMERA_OV3660,
|
||||
CAMERA_OV5640,
|
||||
CAMERA_OV7670,
|
||||
CAMERA_NT99141,
|
||||
CAMERA_GC2145,
|
||||
CAMERA_GC032A,
|
||||
CAMERA_GC0308,
|
||||
CAMERA_MODEL_MAX,
|
||||
CAMERA_NONE,
|
||||
} camera_model_t;
|
||||
|
||||
typedef enum {
|
||||
OV2640_SCCB_ADDR = 0x30,// 0x60 >> 1
|
||||
OV5640_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
OV3660_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
OV7725_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
OV7670_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
NT99141_SCCB_ADDR = 0x2A,// 0x54 >> 1
|
||||
GC2145_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
} camera_sccb_addr_t;
|
||||
|
||||
typedef enum {
|
||||
PIXFORMAT_RGB565, // 2BPP/RGB565
|
||||
@@ -56,6 +93,15 @@ typedef enum {
|
||||
FRAMESIZE_INVALID
|
||||
} framesize_t;
|
||||
|
||||
typedef struct {
|
||||
const camera_model_t model;
|
||||
const char *name;
|
||||
const camera_sccb_addr_t sccb_addr;
|
||||
const camera_pid_t pid;
|
||||
const framesize_t max_size;
|
||||
const bool support_jpeg;
|
||||
} camera_sensor_info_t;
|
||||
|
||||
typedef enum {
|
||||
ASPECT_RATIO_4X3,
|
||||
ASPECT_RATIO_3X2,
|
||||
@@ -99,11 +145,13 @@ typedef struct {
|
||||
|
||||
// Resolution table (in sensor.c)
|
||||
extern const resolution_info_t resolution[];
|
||||
// camera sensor table (in sensor.c)
|
||||
extern const camera_sensor_info_t camera_sensor[];
|
||||
|
||||
typedef struct {
|
||||
uint8_t MIDH;
|
||||
uint8_t MIDL;
|
||||
uint8_t PID;
|
||||
uint16_t PID;
|
||||
uint8_t VER;
|
||||
} sensor_id_t;
|
||||
|
||||
@@ -188,4 +236,10 @@ typedef struct _sensor {
|
||||
int (*set_xclk) (sensor_t *sensor, int timer, int xclk);
|
||||
} sensor_t;
|
||||
|
||||
camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SENSOR_H__ */
|
||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES tfmicro esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
|
||||
REQUIRES tflite-lib esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,14 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
@@ -58,6 +65,56 @@ struct file_server_data {
|
||||
|
||||
static const char *TAG_FILESERVER = "file_server";
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
esp_err_t get_tflite_file_handler(httpd_req_t *req)
|
||||
{
|
||||
DIR *verzeichnis;
|
||||
struct dirent *files;
|
||||
struct dirent *entry;
|
||||
struct stat entry_stat;
|
||||
|
||||
|
||||
std::string _filename, _fileext;
|
||||
size_t pos = 0;
|
||||
|
||||
const char verz_name[] = "/sdcard/config";
|
||||
printf("Suche TFLITE in /sdcard/config/\n");
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_set_type(req, "text/plain");
|
||||
|
||||
DIR *dir = opendir(verz_name);
|
||||
while ((entry = readdir(dir)) != NULL)
|
||||
{
|
||||
_filename = std::string(entry->d_name);
|
||||
printf("File: %s\t", _filename.c_str());
|
||||
|
||||
_fileext = _filename;
|
||||
pos = _fileext.find(".");
|
||||
if (pos != std::string::npos)
|
||||
_fileext = _fileext.erase(0, pos + 1);
|
||||
|
||||
printf(" Extension: %s\n", _fileext.c_str());
|
||||
|
||||
if ((_fileext == "tfl") || (_fileext == "tflite"))
|
||||
{
|
||||
_filename = "/config/" + _filename + "\t";
|
||||
httpd_resp_sendstr_chunk(req, _filename.c_str());
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
/* Handler to redirect incoming GET request for /index.html to /
|
||||
* This can be overridden by uploading file with same name */
|
||||
// static esp_err_t index_html_get_handler(httpd_req_t *req)
|
||||
@@ -310,6 +367,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
||||
}
|
||||
}
|
||||
|
||||
printf("uri: %s, filename: %s, filepath: %s\n", req->uri, filename, filepath);
|
||||
return http_resp_dir_html(req, filepath, filename, readonly);
|
||||
}
|
||||
|
||||
@@ -486,10 +544,10 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
|
||||
printf("Directory: %s, start_fn: %d, found: %d\n", directory.c_str(), start_fn, found);
|
||||
directory = directory.substr(start_fn, found - start_fn + 1);
|
||||
printf("Directory danach: %s\n", directory.c_str());
|
||||
printf("Directory danach 1: %s\n", directory.c_str());
|
||||
|
||||
directory = "/fileserver" + directory;
|
||||
printf("Directory danach: %s\n", directory.c_str());
|
||||
printf("Directory danach 2: %s\n", directory.c_str());
|
||||
|
||||
/* Redirect onto root to see the updated file list */
|
||||
httpd_resp_set_status(req, "303 See Other");
|
||||
@@ -500,11 +558,13 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
||||
httpd_resp_sendstr(req, "File uploaded successfully");
|
||||
|
||||
/*
|
||||
if (strcmp(filepath, CONFIG_FILE) == 0) {
|
||||
printf("New config foung. Reload handler.");
|
||||
printf("New config found. Reload handler.");
|
||||
gpio_handler_deinit();
|
||||
MQTTdestroy();
|
||||
}
|
||||
*/
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -606,10 +666,10 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
|
||||
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
|
||||
printf("Directory: %s, start_fn: %d, found: %d\n", directory.c_str(), start_fn, found);
|
||||
directory = directory.substr(start_fn, found - start_fn + 1);
|
||||
printf("Directory danach: %s\n", directory.c_str());
|
||||
printf("Directory danach 3: %s\n", directory.c_str());
|
||||
|
||||
directory = "/fileserver" + directory;
|
||||
printf("Directory danach: %s\n", directory.c_str());
|
||||
printf("Directory danach 4: %s\n", directory.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -795,4 +855,15 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_delete);
|
||||
|
||||
|
||||
/* URI handler for getting tflite files from server */
|
||||
/*
|
||||
httpd_uri_t file_tflite = {
|
||||
.uri = "/tflite", // Match all URIs of type /delete/path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = get_tflite_file_handler,
|
||||
.user_ctx = server_data // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_tflite);
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -5,4 +5,6 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path);
|
||||
|
||||
void unzip(std::string _in_zip_file, std::string _target_directory);
|
||||
|
||||
void delete_all_in_directory(std::string _directory);
|
||||
void delete_all_in_directory(std::string _directory);
|
||||
|
||||
esp_err_t get_tflite_file_handler(httpd_req_t *req);
|
||||
|
||||
@@ -5,7 +5,14 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
@@ -107,8 +114,12 @@ esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename)
|
||||
return httpd_resp_set_type(req, "text/html");
|
||||
} else if (IS_FILE_EXT(filename, ".jpeg")) {
|
||||
return httpd_resp_set_type(req, "image/jpeg");
|
||||
} else if (IS_FILE_EXT(filename, ".jpg")) {
|
||||
return httpd_resp_set_type(req, "image/jpeg");
|
||||
} else if (IS_FILE_EXT(filename, ".ico")) {
|
||||
return httpd_resp_set_type(req, "image/x-icon");
|
||||
} else if (IS_FILE_EXT(filename, ".js")) {
|
||||
return httpd_resp_set_type(req, "text/javascript");
|
||||
}
|
||||
/* This is a limited set only */
|
||||
/* For any other type always set as plain text */
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "server_file.h"
|
||||
#include "server_GPIO.h"
|
||||
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include "Helper.h"
|
||||
|
||||
@@ -130,11 +130,9 @@ bool ClassFlow::getNextLine(FILE* pfile, string *rt)
|
||||
*rt = trim(*rt);
|
||||
while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '[')) // Kommentarzeilen (; oder #) und Leerzeilen überspringen, es sei denn es ist ein neuer auskommentierter Paragraph
|
||||
{
|
||||
*rt = "";
|
||||
if (!fgets(zw, 1024, pfile))
|
||||
{
|
||||
*rt = "";
|
||||
return false;
|
||||
}
|
||||
printf("%s", zw);
|
||||
*rt = zw;
|
||||
*rt = trim(*rt);
|
||||
|
||||
@@ -1,487 +0,0 @@
|
||||
#include "ClassFlowAnalog.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <iomanip>
|
||||
#include <sys/types.h>
|
||||
#include <sstream> // std::stringstream
|
||||
|
||||
|
||||
// #define OHNETFLITE
|
||||
|
||||
#ifndef OHNETFLITE
|
||||
#include "CTfLiteClass.h"
|
||||
#endif
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
static const char* TAG = "flow_analog";
|
||||
|
||||
bool debugdetailanalog = false;
|
||||
|
||||
void ClassFlowAnalog::SetInitialParameter(void)
|
||||
{
|
||||
string cnnmodelfile = "";
|
||||
modelxsize = 1;
|
||||
modelysize = 1;
|
||||
ListFlowControll = NULL;
|
||||
previousElement = NULL;
|
||||
SaveAllFiles = false;
|
||||
disabled = false;
|
||||
extendedResolution = false;
|
||||
}
|
||||
|
||||
ClassFlowAnalog::ClassFlowAnalog(std::vector<ClassFlow*>* lfc) : ClassFlowImage(lfc, TAG)
|
||||
{
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
||||
{
|
||||
flowpostalignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowAnalog::AnzahlROIs(int _analog = 0)
|
||||
{
|
||||
int zw = ANALOG[_analog]->ROI.size();
|
||||
if (extendedResolution)
|
||||
zw++;
|
||||
|
||||
return zw;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowAnalog::getReadout(int _analog = 0)
|
||||
{
|
||||
string result = "";
|
||||
if (ANALOG[_analog]->ROI.size() == 0)
|
||||
return result;
|
||||
|
||||
|
||||
float zahl = ANALOG[_analog]->ROI[ANALOG[_analog]->ROI.size() - 1]->result;
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
|
||||
int prev = -1;
|
||||
|
||||
prev = ZeigerEval(ANALOG[_analog]->ROI[ANALOG[_analog]->ROI.size() - 1]->result, prev);
|
||||
result = std::to_string(prev);
|
||||
|
||||
if (extendedResolution)
|
||||
result = result + std::to_string(ergebnis_nachkomma);
|
||||
|
||||
for (int i = ANALOG[_analog]->ROI.size() - 2; i >= 0; --i)
|
||||
{
|
||||
prev = ZeigerEval(ANALOG[_analog]->ROI[i]->result, prev);
|
||||
result = std::to_string(prev) + result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ClassFlowAnalog::ZeigerEval(float zahl, int ziffer_vorgaenger)
|
||||
{
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
|
||||
int ergebnis, ergebnis_rating;
|
||||
|
||||
if (ziffer_vorgaenger == -1)
|
||||
return ergebnis_vorkomma % 10;
|
||||
|
||||
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
|
||||
if (ergebnis_nachkomma >= 5)
|
||||
ergebnis_rating-=5;
|
||||
else
|
||||
ergebnis_rating+=5;
|
||||
ergebnis = (int) round(zahl);
|
||||
if (ergebnis_rating < 0)
|
||||
ergebnis-=1;
|
||||
if (ergebnis == -1)
|
||||
ergebnis+=10;
|
||||
|
||||
ergebnis = ergebnis % 10;
|
||||
return ergebnis;
|
||||
}
|
||||
|
||||
bool ClassFlowAnalog::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> zerlegt;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
|
||||
if ((aktparamgraph.compare("[Analog]") != 0) && (aktparamgraph.compare(";[Analog]") != 0)) // Paragraph passt nich zu MakeImage
|
||||
return false;
|
||||
|
||||
if (aktparamgraph[0] == ';')
|
||||
{
|
||||
disabled = true;
|
||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
printf("[Analog] is disabled !!!\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->LogImageLocation = "/sdcard" + zerlegt[1];
|
||||
this->isLogImage = true;
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "LOGFILERETENTIONINDAYS") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->logfileRetentionInDays = std::stoi(zerlegt[1]);
|
||||
}
|
||||
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->cnnmodelfile = zerlegt[1];
|
||||
}
|
||||
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
||||
{
|
||||
this->modelxsize = std::stoi(zerlegt[1]);
|
||||
this->modelysize = std::stoi(zerlegt[2]);
|
||||
}
|
||||
if (zerlegt.size() >= 5)
|
||||
{
|
||||
analog* _analog = GetANALOG(zerlegt[0], true);
|
||||
roianalog* neuroi = _analog->ROI[_analog->ROI.size()-1];
|
||||
neuroi->posx = std::stoi(zerlegt[1]);
|
||||
neuroi->posy = std::stoi(zerlegt[2]);
|
||||
neuroi->deltax = std::stoi(zerlegt[3]);
|
||||
neuroi->deltay = std::stoi(zerlegt[4]);
|
||||
neuroi->result = -1;
|
||||
neuroi->image = NULL;
|
||||
neuroi->image_org = NULL;
|
||||
// ROI.push_back(neuroi);
|
||||
}
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
SaveAllFiles = true;
|
||||
}
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "EXTENDEDRESOLUTION") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
extendedResolution = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
|
||||
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
ANALOG[_ana]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
|
||||
ANALOG[_ana]->ROI[i]->image_org = new CImageBasis(ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
analog* ClassFlowAnalog::FindANALOG(string _name_number)
|
||||
{
|
||||
|
||||
for (int i = 0; i < ANALOG.size(); ++i)
|
||||
{
|
||||
if (ANALOG[i]->name == _name_number)
|
||||
return ANALOG[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
analog* ClassFlowAnalog::GetANALOG(string _name, bool _create = true)
|
||||
{
|
||||
string _analog, _roi;
|
||||
int _pospunkt = _name.find_first_of(".");
|
||||
// printf("Name: %s, Pospunkt: %d\n", _name.c_str(), _pospunkt);
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_analog = _name.substr(0, _pospunkt);
|
||||
_roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
_analog = "default";
|
||||
_roi = _name;
|
||||
}
|
||||
|
||||
analog *_ret = NULL;
|
||||
|
||||
for (int i = 0; i < ANALOG.size(); ++i)
|
||||
{
|
||||
if (ANALOG[i]->name == _analog)
|
||||
_ret = ANALOG[i];
|
||||
}
|
||||
|
||||
if (!_create) // nicht gefunden und soll auch nicht erzeugt werden
|
||||
return _ret;
|
||||
|
||||
|
||||
if (_ret == NULL)
|
||||
{
|
||||
_ret = new analog;
|
||||
_ret->name = _analog;
|
||||
ANALOG.push_back(_ret);
|
||||
}
|
||||
|
||||
roianalog* neuroi = new roianalog;
|
||||
neuroi->name = _roi;
|
||||
_ret->ROI.push_back(neuroi);
|
||||
|
||||
printf("GetANALOG - ANALOG %s - roi %s\n", _analog.c_str(), _roi.c_str());
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
string ClassFlowAnalog::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result, zw;
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
|
||||
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
|
||||
result = result + "Analog Pointers: <p> ";
|
||||
|
||||
htmlinfo = GetHTMLInfo();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
|
||||
zw = stream.str();
|
||||
|
||||
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ClassFlowAnalog::doFlow(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
if (!doAlignAndCut(time)){
|
||||
return false;
|
||||
};
|
||||
|
||||
if (debugdetailanalog) LogFile.WriteToFile("ClassFlowAnalog::doFlow nach Alignment");
|
||||
|
||||
doNeuralNetwork(time);
|
||||
|
||||
RemoveOldLogs();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassFlowAnalog::doAlignAndCut(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
|
||||
|
||||
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
|
||||
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
printf("Analog %d - Align&Cut\n", i);
|
||||
|
||||
caic->CutAndSave(ANALOG[_ana]->ROI[i]->posx, ANALOG[_ana]->ROI[i]->posy, ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, ANALOG[_ana]->ROI[i]->image_org);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (ANALOG[_ana]->name == "default")
|
||||
ANALOG[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".jpg"));
|
||||
else
|
||||
ANALOG[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".jpg"));
|
||||
}
|
||||
|
||||
ANALOG[_ana]->ROI[i]->image_org->Resize(modelxsize, modelysize, ANALOG[_ana]->ROI[i]->image);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (ANALOG[_ana]->name == "default")
|
||||
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
|
||||
else
|
||||
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassFlowAnalog::DrawROI(CImageBasis *_zw)
|
||||
{
|
||||
int r = 0;
|
||||
int g = 255;
|
||||
int b = 0;
|
||||
|
||||
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
|
||||
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
_zw->drawRect(ANALOG[_ana]->ROI[i]->posx, ANALOG[_ana]->ROI[i]->posy, ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, r, g, b, 1);
|
||||
_zw->drawCircle((int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), (int) (ANALOG[_ana]->ROI[i]->deltax/2), r, g, b, 2);
|
||||
_zw->drawLine((int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) ANALOG[_ana]->ROI[i]->posy, (int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay), r, g, b, 2);
|
||||
_zw->drawLine((int) ANALOG[_ana]->ROI[i]->posx, (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), (int) ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax, (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassFlowAnalog::doNeuralNetwork(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
string logPath = CreateLogFolder(time);
|
||||
|
||||
string input = "/sdcard/img_tmp/alg.jpg";
|
||||
string ioresize = "/sdcard/img_tmp/resize.bmp";
|
||||
string output;
|
||||
input = FormatFileName(input);
|
||||
|
||||
#ifndef OHNETFLITE
|
||||
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());
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
tflite->MakeAllocate();
|
||||
#endif
|
||||
|
||||
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
|
||||
{
|
||||
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
printf("Analog %d - TfLite\n", i);
|
||||
|
||||
float f1, f2;
|
||||
f1 = 0; f2 = 0;
|
||||
|
||||
#ifndef OHNETFLITE
|
||||
tflite->LoadInputImageBasis(ANALOG[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
if (debugdetailanalog) LogFile.WriteToFile("Nach Invoke");
|
||||
|
||||
|
||||
f1 = tflite->GetOutputValue(0);
|
||||
f2 = tflite->GetOutputValue(1);
|
||||
#endif
|
||||
|
||||
float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
|
||||
// printf("Result sin, cos, ziffer: %f, %f, %f\n", f1, f2, result);
|
||||
ANALOG[_ana]->ROI[i]->result = result * 10;
|
||||
|
||||
printf("Result Analog%i: %f\n", i, ANALOG[_ana]->ROI[i]->result);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
LogImage(logPath, ANALOG[_ana]->ROI[i]->name, &ANALOG[_ana]->ROI[i]->result, NULL, time, ANALOG[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef OHNETFLITE
|
||||
delete tflite;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowAnalog::GetHTMLInfo()
|
||||
{
|
||||
std::vector<HTMLInfo*> result;
|
||||
|
||||
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
|
||||
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
if (ANALOG[_ana]->name == "default")
|
||||
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
|
||||
else
|
||||
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
|
||||
|
||||
|
||||
HTMLInfo *zw = new HTMLInfo;
|
||||
if (ANALOG[_ana]->name == "default")
|
||||
{
|
||||
zw->filename = ANALOG[_ana]->ROI[i]->name + ".bmp";
|
||||
zw->filename_org = ANALOG[_ana]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
else
|
||||
{
|
||||
zw->filename = ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp";
|
||||
zw->filename_org = ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
|
||||
zw->val = ANALOG[_ana]->ROI[i]->result;
|
||||
zw->image = ANALOG[_ana]->ROI[i]->image;
|
||||
zw->image_org = ANALOG[_ana]->ROI[i]->image_org;
|
||||
|
||||
result.push_back(zw);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int ClassFlowAnalog::getAnzahlANALOG()
|
||||
{
|
||||
return ANALOG.size();
|
||||
}
|
||||
|
||||
string ClassFlowAnalog::getNameANALOG(int _analog)
|
||||
{
|
||||
if (_analog < ANALOG.size())
|
||||
return ANALOG[_analog]->name;
|
||||
|
||||
return "ANALOG DOES NOT EXIST";
|
||||
}
|
||||
|
||||
analog* ClassFlowAnalog::GetANALOG(int _analog)
|
||||
{
|
||||
if (_analog < ANALOG.size())
|
||||
return ANALOG[_analog];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClassFlowAnalog::UpdateNameNumbers(std::vector<std::string> *_name_numbers)
|
||||
{
|
||||
for (int _dig = 0; _dig < ANALOG.size(); _dig++)
|
||||
{
|
||||
std::string _name = ANALOG[_dig]->name;
|
||||
bool found = false;
|
||||
for (int i = 0; i < (*_name_numbers).size(); ++i)
|
||||
{
|
||||
if ((*_name_numbers)[i] == _name)
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
(*_name_numbers).push_back(_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
#pragma once
|
||||
#include "ClassFlowImage.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
// #include "CTfLiteClass.h"
|
||||
|
||||
struct roianalog {
|
||||
int posx, posy, deltax, deltay;
|
||||
float result;
|
||||
CImageBasis *image, *image_org;
|
||||
string name;
|
||||
};
|
||||
|
||||
struct analog {
|
||||
string name;
|
||||
std::vector<roianalog*> ROI;
|
||||
};
|
||||
|
||||
|
||||
class ClassFlowAnalog :
|
||||
public ClassFlowImage
|
||||
{
|
||||
protected:
|
||||
// std::vector<roianalog*> ROI;
|
||||
std::vector<analog*> ANALOG;
|
||||
|
||||
string cnnmodelfile;
|
||||
int modelxsize, modelysize;
|
||||
int ZeigerEval(float zahl, int ziffer_vorgaenger);
|
||||
bool SaveAllFiles;
|
||||
|
||||
|
||||
ClassFlowAlignment* flowpostalignment;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
|
||||
|
||||
public:
|
||||
bool extendedResolution;
|
||||
|
||||
ClassFlowAnalog(std::vector<ClassFlow*>* lfc);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getHTMLSingleStep(string host);
|
||||
string getReadout(int _analog);
|
||||
|
||||
void DrawROI(CImageBasis *_zw);
|
||||
|
||||
bool doNeuralNetwork(string time);
|
||||
bool doAlignAndCut(string time);
|
||||
std::vector<HTMLInfo*> GetHTMLInfo();
|
||||
int AnzahlROIs(int _analog);
|
||||
|
||||
int getAnzahlANALOG();
|
||||
analog* GetANALOG(int _analog);
|
||||
analog* GetANALOG(string _name, bool _create);
|
||||
analog* FindANALOG(string _name_number);
|
||||
string getNameANALOG(int _analog);
|
||||
|
||||
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
|
||||
|
||||
|
||||
string name(){return "ClassFlowAnalog";};
|
||||
};
|
||||
|
||||
661
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
Normal file
661
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
Normal file
@@ -0,0 +1,661 @@
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <iomanip>
|
||||
#include <sys/types.h>
|
||||
#include <sstream> // std::stringstream
|
||||
|
||||
#include "CTfLiteClass.h"
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
static const char* TAG = "flow_analog";
|
||||
|
||||
bool debugdetailgeneral = false;
|
||||
|
||||
ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype) : ClassFlowImage(NULL, TAG)
|
||||
{
|
||||
string cnnmodelfile = "";
|
||||
modelxsize = 1;
|
||||
modelysize = 1;
|
||||
ListFlowControll = NULL;
|
||||
previousElement = NULL;
|
||||
SaveAllFiles = false;
|
||||
disabled = false;
|
||||
isLogImageSelect = false;
|
||||
CNNType = AutoDetect;
|
||||
CNNType = _cnntype;
|
||||
flowpostalignment = _flowalign;
|
||||
}
|
||||
|
||||
string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution = false)
|
||||
{
|
||||
string result = "";
|
||||
if (GENERAL[_analog]->ROI.size() == 0)
|
||||
return result;
|
||||
|
||||
if (CNNType == Analogue)
|
||||
{
|
||||
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);
|
||||
result = std::to_string(prev);
|
||||
|
||||
if (_extendedResolution && (CNNType != Digital))
|
||||
result = result + std::to_string(ergebnis_nachkomma);
|
||||
|
||||
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
|
||||
{
|
||||
prev = ZeigerEval(GENERAL[_analog]->ROI[i]->result_float, prev);
|
||||
result = std::to_string(prev) + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (CNNType == Digital)
|
||||
{
|
||||
for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i)
|
||||
{
|
||||
if (GENERAL[_analog]->ROI[i]->result_klasse >= 10)
|
||||
result = result + "N";
|
||||
else
|
||||
result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (CNNType == DigitalHyprid)
|
||||
{
|
||||
int zif_akt = -1;
|
||||
|
||||
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
|
||||
if (zahl >= 0) // NaN?
|
||||
{
|
||||
if (_extendedResolution)
|
||||
{
|
||||
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);
|
||||
zif_akt = ergebnis_vorkomma;
|
||||
}
|
||||
else
|
||||
{
|
||||
zif_akt = ZeigerEvalHybrid(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, -1, -1);
|
||||
result = std::to_string(zif_akt);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
zif_akt = ZeigerEvalHybrid(GENERAL[_analog]->ROI[i]->result_float, GENERAL[_analog]->ROI[i+1]->result_float, zif_akt);
|
||||
result = std::to_string(zif_akt) + result;
|
||||
}
|
||||
else
|
||||
{
|
||||
zif_akt = -1;
|
||||
result = "N" + result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger)
|
||||
{
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
|
||||
if (zahl_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;
|
||||
else
|
||||
return ((int) trunc(zahl) + 10) % 10;
|
||||
}
|
||||
|
||||
if (zahl_vorgaenger > 9.2) // Ziffernwechsel beginnt
|
||||
{
|
||||
if (eval_vorgaenger == 0) // Wechsel hat schon stattgefunden
|
||||
{
|
||||
return ((int) round(zahl) + 10) % 10; // Annahme, dass die neue Zahl schon in der Nähe des Ziels ist
|
||||
}
|
||||
else
|
||||
{
|
||||
if (zahl_vorgaenger <= 9.5) // Wechsel startet gerade, aber beginnt erst
|
||||
{
|
||||
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||
return ((int) round(zahl) + 10) % 10;
|
||||
else
|
||||
return ((int) trunc(zahl) + 10) % 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((int) trunc(zahl) + 10) % 10; // Wechsel schon weiter fortgeschritten, d.h. über 2 als Nachkomma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||
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;
|
||||
|
||||
if (ziffer_vorgaenger == -1)
|
||||
return ergebnis_vorkomma % 10;
|
||||
|
||||
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
|
||||
if (ergebnis_nachkomma >= 5)
|
||||
ergebnis_rating-=5;
|
||||
else
|
||||
ergebnis_rating+=5;
|
||||
ergebnis = (int) round(zahl);
|
||||
if (ergebnis_rating < 0)
|
||||
ergebnis-=1;
|
||||
if (ergebnis == -1)
|
||||
ergebnis+=10;
|
||||
|
||||
ergebnis = (ergebnis + 10) % 10;
|
||||
return ergebnis;
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::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) != "[ANALOG]") && (toUpper(aktparamgraph) != ";[ANALOG]")
|
||||
&& (toUpper(aktparamgraph) != "[DIGIT]") && (toUpper(aktparamgraph) != ";[DIGIT]")
|
||||
&& (toUpper(aktparamgraph) != "[DIGITS]") && (toUpper(aktparamgraph) != ";[DIGITS]")
|
||||
) // Paragraph passt nicht
|
||||
return false;
|
||||
|
||||
if (aktparamgraph[0] == ';')
|
||||
{
|
||||
disabled = true;
|
||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
printf("[Analog/Digit] is disabled !!!\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->LogImageLocation = "/sdcard" + zerlegt[1];
|
||||
this->isLogImage = true;
|
||||
}
|
||||
if ((zerlegt[0] == "LogImageSelect") && (zerlegt.size() > 1))
|
||||
{
|
||||
LogImageSelect = zerlegt[1];
|
||||
isLogImageSelect = true;
|
||||
}
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "LOGFILERETENTIONINDAYS") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->logfileRetentionInDays = std::stoi(zerlegt[1]);
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "MODELTYPE") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "DIGITHYPRID")
|
||||
CNNType = DigitalHyprid;
|
||||
}
|
||||
|
||||
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->cnnmodelfile = zerlegt[1];
|
||||
}
|
||||
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
||||
{
|
||||
this->modelxsize = std::stoi(zerlegt[1]);
|
||||
this->modelysize = std::stoi(zerlegt[2]);
|
||||
}
|
||||
if (zerlegt.size() >= 5)
|
||||
{
|
||||
general* _analog = GetGENERAL(zerlegt[0], true);
|
||||
roi* neuroi = _analog->ROI[_analog->ROI.size()-1];
|
||||
neuroi->posx = std::stoi(zerlegt[1]);
|
||||
neuroi->posy = std::stoi(zerlegt[2]);
|
||||
neuroi->deltax = std::stoi(zerlegt[3]);
|
||||
neuroi->deltay = std::stoi(zerlegt[4]);
|
||||
neuroi->result_float = -1;
|
||||
neuroi->image = NULL;
|
||||
neuroi->image_org = NULL;
|
||||
}
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
SaveAllFiles = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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_org = new CImageBasis(GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
general* ClassFlowCNNGeneral::FindGENERAL(string _name_number)
|
||||
{
|
||||
for (int i = 0; i < GENERAL.size(); ++i)
|
||||
if (GENERAL[i]->name == _name_number)
|
||||
return GENERAL[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true)
|
||||
{
|
||||
string _analog, _roi;
|
||||
int _pospunkt = _name.find_first_of(".");
|
||||
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_analog = _name.substr(0, _pospunkt);
|
||||
_roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
_analog = "default";
|
||||
_roi = _name;
|
||||
}
|
||||
|
||||
general *_ret = NULL;
|
||||
|
||||
for (int i = 0; i < GENERAL.size(); ++i)
|
||||
if (GENERAL[i]->name == _analog)
|
||||
_ret = GENERAL[i];
|
||||
|
||||
if (!_create) // nicht gefunden und soll auch nicht erzeugt werden
|
||||
return _ret;
|
||||
|
||||
if (_ret == NULL)
|
||||
{
|
||||
_ret = new general;
|
||||
_ret->name = _analog;
|
||||
GENERAL.push_back(_ret);
|
||||
}
|
||||
|
||||
roi* neuroi = new roi;
|
||||
neuroi->name = _roi;
|
||||
|
||||
_ret->ROI.push_back(neuroi);
|
||||
|
||||
printf("GetGENERAL - GENERAL %s - roi %s\n", _analog.c_str(), _roi.c_str());
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
string ClassFlowCNNGeneral::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result, zw;
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
|
||||
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
|
||||
result = result + "Analog Pointers: <p> ";
|
||||
|
||||
htmlinfo = GetHTMLInfo();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
|
||||
zw = stream.str();
|
||||
|
||||
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::doFlow(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
if (!doAlignAndCut(time)){
|
||||
return false;
|
||||
};
|
||||
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::doFlow nach Alignment");
|
||||
|
||||
doNeuralNetwork(time);
|
||||
|
||||
RemoveOldLogs();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::doAlignAndCut(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
printf("General %d - Align&Cut\n", i);
|
||||
|
||||
caic->CutAndSave(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, GENERAL[_ana]->ROI[i]->image_org);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||
else
|
||||
GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||
}
|
||||
|
||||
GENERAL[_ana]->ROI[i]->image_org->Resize(modelxsize, modelysize, GENERAL[_ana]->ROI[i]->image);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||
else
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
|
||||
{
|
||||
if (CNNType == Analogue)
|
||||
{
|
||||
int r = 0;
|
||||
int g = 255;
|
||||
int b = 0;
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int _dig = 0; _dig < GENERAL.size(); ++_dig)
|
||||
for (int i = 0; i < GENERAL[_dig]->ROI.size(); ++i)
|
||||
_zw->drawRect(GENERAL[_dig]->ROI[i]->posx, GENERAL[_dig]->ROI[i]->posy, GENERAL[_dig]->ROI[i]->deltax, GENERAL[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
string logPath = CreateLogFolder(time);
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
printf("General %d - TfLite\n", i);
|
||||
|
||||
switch (CNNType) {
|
||||
case Analogue:
|
||||
{
|
||||
float f1, f2;
|
||||
f1 = 0; f2 = 0;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
|
||||
|
||||
f1 = tflite->GetOutputValue(0);
|
||||
f2 = tflite->GetOutputValue(1);
|
||||
float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
|
||||
GENERAL[_ana]->ROI[i]->result_float = result * 10;
|
||||
printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
|
||||
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;
|
||||
GENERAL[_ana]->ROI[i]->result_klasse = tflite->GetClassFromImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
printf("Result General(Digit)%i: %d\n", i, GENERAL[_ana]->ROI[i]->result_klasse);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case DigitalHyprid:
|
||||
{
|
||||
int _num, _nachkomma;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
|
||||
|
||||
_num = tflite->GetOutClassification(0, 10);
|
||||
_nachkomma = tflite->GetOutClassification(11, 21);
|
||||
|
||||
|
||||
string _zwres = "Nach Invoke - Nummer: " + to_string(_num) + " Nachkomma: " + to_string(_nachkomma);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
|
||||
|
||||
if ((_num == 10) || (_nachkomma == 10)) // NaN detektiert
|
||||
GENERAL[_ana]->ROI[i]->result_float = -1;
|
||||
else
|
||||
GENERAL[_ana]->ROI[i]->result_float = fmod((double) _num + (((double)_nachkomma)-5)/10 + (double) 10, 10);
|
||||
|
||||
printf("Result General(DigitalHyprid)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
|
||||
_zwres = "Result General(DigitalHyprid)" + to_string(i) + ": " + to_string(GENERAL[_ana]->ROI[i]->result_float);
|
||||
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);
|
||||
} break;
|
||||
case DigitalHyprid10:
|
||||
{
|
||||
int _num, _nachkomma;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
|
||||
|
||||
_num = tflite->GetOutClassification(0, 9);
|
||||
_nachkomma = tflite->GetOutClassification(10, 19);
|
||||
|
||||
|
||||
string _zwres = "Nach Invoke - Nummer: " + to_string(_num) + " Nachkomma: " + to_string(_nachkomma);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
|
||||
|
||||
GENERAL[_ana]->ROI[i]->result_float = fmod((double) _num + (((double)_nachkomma)-5)/10 + (double) 10, 10);
|
||||
|
||||
printf("Result General(DigitalHyprid)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
|
||||
_zwres = "Result General(DigitalHyprid)" + to_string(i) + ": " + to_string(GENERAL[_ana]->ROI[i]->result_float);
|
||||
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);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete tflite;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
|
||||
{
|
||||
if (!(CNNType == Digital))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowCNNGeneral::GetHTMLInfo()
|
||||
{
|
||||
std::vector<HTMLInfo*> result;
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||
else
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||
|
||||
|
||||
HTMLInfo *zw = new HTMLInfo;
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
{
|
||||
zw->filename = GENERAL[_ana]->ROI[i]->name + ".bmp";
|
||||
zw->filename_org = GENERAL[_ana]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
else
|
||||
{
|
||||
zw->filename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp";
|
||||
zw->filename_org = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
|
||||
if (CNNType == Digital)
|
||||
zw->val = GENERAL[_ana]->ROI[i]->result_klasse;
|
||||
else
|
||||
zw->val = GENERAL[_ana]->ROI[i]->result_float;
|
||||
zw->image = GENERAL[_ana]->ROI[i]->image;
|
||||
zw->image_org = GENERAL[_ana]->ROI[i]->image_org;
|
||||
|
||||
result.push_back(zw);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ClassFlowCNNGeneral::getAnzahlGENERAL()
|
||||
{
|
||||
return GENERAL.size();
|
||||
}
|
||||
|
||||
string ClassFlowCNNGeneral::getNameGENERAL(int _analog)
|
||||
{
|
||||
if (_analog < GENERAL.size())
|
||||
return GENERAL[_analog]->name;
|
||||
|
||||
return "GENERAL DOES NOT EXIST";
|
||||
}
|
||||
|
||||
general* ClassFlowCNNGeneral::GetGENERAL(int _analog)
|
||||
{
|
||||
if (_analog < GENERAL.size())
|
||||
return GENERAL[_analog];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClassFlowCNNGeneral::UpdateNameNumbers(std::vector<std::string> *_name_numbers)
|
||||
{
|
||||
for (int _dig = 0; _dig < GENERAL.size(); _dig++)
|
||||
{
|
||||
std::string _name = GENERAL[_dig]->name;
|
||||
bool found = false;
|
||||
for (int i = 0; i < (*_name_numbers).size(); ++i)
|
||||
{
|
||||
if ((*_name_numbers)[i] == _name)
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
(*_name_numbers).push_back(_name);
|
||||
}
|
||||
}
|
||||
69
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
Normal file
69
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef __CLASSCNNGENERAL__
|
||||
#define __CLASSCNNGENERAL__
|
||||
|
||||
#include"ClassFlowDefineTypes.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
|
||||
|
||||
enum t_CNNType {
|
||||
AutoDetect,
|
||||
Analogue,
|
||||
Digital,
|
||||
DigitalHyprid,
|
||||
DigitalHyprid10,
|
||||
None
|
||||
};
|
||||
|
||||
class ClassFlowCNNGeneral :
|
||||
public ClassFlowImage
|
||||
{
|
||||
protected:
|
||||
t_CNNType CNNType;
|
||||
std::vector<general*> GENERAL;
|
||||
|
||||
string cnnmodelfile;
|
||||
int modelxsize, modelysize;
|
||||
bool isLogImageSelect;
|
||||
string LogImageSelect;
|
||||
ClassFlowAlignment* flowpostalignment;
|
||||
// ClassFlowPostProcessing *flowpostprocessing = NULL;
|
||||
bool SaveAllFiles;
|
||||
// bool extendedResolution;
|
||||
|
||||
int ZeigerEval(float zahl, int ziffer_vorgaenger);
|
||||
int ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger);
|
||||
|
||||
|
||||
bool doNeuralNetwork(string time);
|
||||
bool doAlignAndCut(string time);
|
||||
|
||||
public:
|
||||
ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype = AutoDetect);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
|
||||
string getHTMLSingleStep(string host);
|
||||
string getReadout(int _analog, bool _extendedResolution);
|
||||
|
||||
void DrawROI(CImageBasis *_zw);
|
||||
|
||||
std::vector<HTMLInfo*> GetHTMLInfo();
|
||||
|
||||
int getAnzahlGENERAL();
|
||||
general* GetGENERAL(int _analog);
|
||||
general* GetGENERAL(string _name, bool _create);
|
||||
general* FindGENERAL(string _name_number);
|
||||
string getNameGENERAL(int _analog);
|
||||
|
||||
bool isExtendedResolution(int _number = 0);
|
||||
|
||||
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
|
||||
|
||||
t_CNNType getCNNType(){return CNNType;};
|
||||
|
||||
string name(){return "ClassFlowCNNGeneral";};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,12 +6,23 @@
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "time_sntp.h"
|
||||
#include "Helper.h"
|
||||
#include "server_ota.h"
|
||||
|
||||
|
||||
//#include "CImg.h"
|
||||
|
||||
#include "server_help.h"
|
||||
|
||||
//#define DEBUG_DETAIL_ON
|
||||
@@ -30,12 +41,10 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
_classname = "ClassFlowAlignment";
|
||||
}
|
||||
if ((_stepname.compare(0, 7, "[Digits") == 0) || (_stepname.compare(0, 8, ";[Digits") == 0)) {
|
||||
// if ((_stepname.compare("[Digits]") == 0) || (_stepname.compare(";[Digits]") == 0)){
|
||||
// printf("Digits!!!\n");
|
||||
_classname = "ClassFlowDigit";
|
||||
_classname = "ClassFlowCNNGeneral";
|
||||
}
|
||||
if ((_stepname.compare("[Analog]") == 0) || (_stepname.compare(";[Analog]") == 0)){
|
||||
_classname = "ClassFlowAnalog";
|
||||
_classname = "ClassFlowCNNGeneral";
|
||||
}
|
||||
if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)){
|
||||
_classname = "ClassFlowMQTT";
|
||||
@@ -51,11 +60,33 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||
|
||||
std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
||||
{
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare("ClassFlowDigit") == 0)
|
||||
return ((ClassFlowDigit*) (FlowControll[i]))->GetHTMLInfo();
|
||||
if (_input.compare("ClassFlowMakeImage") == 0)
|
||||
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("ClassFlowPostProcessing") == 0)
|
||||
return ("Processing");
|
||||
|
||||
return "Unkown Status";
|
||||
}
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||
{
|
||||
if (flowdigit)
|
||||
{
|
||||
printf("ClassFlowControll::GetAllDigital - flowdigit != NULL\n");
|
||||
return flowdigit->GetHTMLInfo();
|
||||
}
|
||||
|
||||
std::vector<HTMLInfo*> empty;
|
||||
return empty;
|
||||
@@ -63,14 +94,43 @@ std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllAnalog()
|
||||
{
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare("ClassFlowAnalog") == 0)
|
||||
return ((ClassFlowAnalog*) (FlowControll[i]))->GetHTMLInfo();
|
||||
if (flowanalog)
|
||||
return flowanalog->GetHTMLInfo();
|
||||
|
||||
std::vector<HTMLInfo*> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
t_CNNType ClassFlowControll::GetTypeDigital()
|
||||
{
|
||||
if (flowdigit)
|
||||
return flowdigit->getCNNType();
|
||||
|
||||
return t_CNNType::None;
|
||||
}
|
||||
|
||||
t_CNNType ClassFlowControll::GetTypeAnalog()
|
||||
{
|
||||
if (flowanalog)
|
||||
return flowanalog->getCNNType();
|
||||
|
||||
return t_CNNType::None;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
string ClassFlowControll::GetMQTTMainTopic()
|
||||
{
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0)
|
||||
return ((ClassFlowMQTT*) (FlowControll[i]))->GetMQTTMainTopic();
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClassFlowControll::SetInitialParameter(void)
|
||||
{
|
||||
@@ -82,7 +142,7 @@ void ClassFlowControll::SetInitialParameter(void)
|
||||
flowpostprocessing = NULL;
|
||||
disabled = false;
|
||||
aktRunNr = 0;
|
||||
aktstatus = "Startup";
|
||||
aktstatus = "Booting ...";
|
||||
|
||||
}
|
||||
|
||||
@@ -110,20 +170,20 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
}
|
||||
if (toUpper(_type).compare("[ANALOG]") == 0)
|
||||
{
|
||||
cfc = new ClassFlowAnalog(&FlowControll);
|
||||
flowanalog = (ClassFlowAnalog*) cfc;
|
||||
cfc = new ClassFlowCNNGeneral(flowalignment);
|
||||
flowanalog = (ClassFlowCNNGeneral*) cfc;
|
||||
}
|
||||
if (toUpper(_type).compare(0, 7, "[DIGITS") == 0)
|
||||
{
|
||||
cfc = new ClassFlowDigit(&FlowControll);
|
||||
flowdigit = (ClassFlowDigit*) cfc;
|
||||
cfc = new ClassFlowCNNGeneral(flowalignment);
|
||||
flowdigit = (ClassFlowCNNGeneral*) cfc;
|
||||
}
|
||||
if (toUpper(_type).compare("[MQTT]") == 0)
|
||||
cfc = new ClassFlowMQTT(&FlowControll);
|
||||
|
||||
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
|
||||
{
|
||||
cfc = new ClassFlowPostProcessing(&FlowControll);
|
||||
cfc = new ClassFlowPostProcessing(&FlowControll, flowanalog, flowdigit);
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) cfc;
|
||||
}
|
||||
|
||||
@@ -168,14 +228,17 @@ void ClassFlowControll::InitFlow(std::string config)
|
||||
cfc = CreateClassFlow(line);
|
||||
if (cfc)
|
||||
{
|
||||
printf("Start ReadParameter\n");
|
||||
printf("Start ReadParameter (%s)\n", line.c_str());
|
||||
cfc->ReadParameter(pFile, line);
|
||||
}
|
||||
else
|
||||
{
|
||||
fgets(zw, 1024, pFile);
|
||||
printf("%s", zw);
|
||||
line = std::string(zw);
|
||||
line = "";
|
||||
if (fgets(zw, 1024, pFile) && !feof(pFile))
|
||||
{
|
||||
printf("Read: %s", zw);
|
||||
line = std::string(zw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,8 +246,8 @@ void ClassFlowControll::InitFlow(std::string config)
|
||||
|
||||
}
|
||||
|
||||
std::string ClassFlowControll::getActStatus(){
|
||||
return aktstatus;
|
||||
std::string* ClassFlowControll::getActStatus(){
|
||||
return &aktstatus;
|
||||
}
|
||||
|
||||
void ClassFlowControll::doFlowMakeImageOnly(string time){
|
||||
@@ -193,9 +256,9 @@ void ClassFlowControll::doFlowMakeImageOnly(string time){
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
{
|
||||
if (FlowControll[i]->name() == "ClassFlowMakeImage") {
|
||||
zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||
aktstatus = zw_time + ": " + FlowControll[i]->name();
|
||||
string zw = "FlowControll.doFlowMakeImageOnly - " + FlowControll[i]->name();
|
||||
// zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||
zw_time = gettimestring("%H:%M:%S");
|
||||
aktstatus = TranslateAktstatus(FlowControll[i]->name()) + " (" + zw_time + ")";
|
||||
FlowControll[i]->doFlow(time);
|
||||
}
|
||||
}
|
||||
@@ -215,8 +278,11 @@ bool ClassFlowControll::doFlow(string time)
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
{
|
||||
zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||
aktstatus = zw_time + ": " + FlowControll[i]->name();
|
||||
zw_time = gettimestring("%H:%M:%S");
|
||||
aktstatus = TranslateAktstatus(FlowControll[i]->name()) + " (" + zw_time + ")";
|
||||
|
||||
// zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||
// aktstatus = zw_time + ": " + FlowControll[i]->name();
|
||||
|
||||
|
||||
string zw = "FlowControll.doFlow - " + FlowControll[i]->name();
|
||||
@@ -225,7 +291,7 @@ bool ClassFlowControll::doFlow(string time)
|
||||
if (!FlowControll[i]->doFlow(time)){
|
||||
repeat++;
|
||||
LogFile.WriteToFile("Fehler im vorheriger Schritt - wird zum " + to_string(repeat) + ". Mal wiederholt");
|
||||
i = -1; // vorheriger Schritt muss wiederholt werden (vermutlich Bilder aufnehmen)
|
||||
if (i) i -= 1; // vorheriger Schritt muss wiederholt werden (vermutlich Bilder aufnehmen)
|
||||
result = false;
|
||||
if (repeat > 5) {
|
||||
LogFile.WriteToFile("Wiederholung 5x nicht erfolgreich --> reboot");
|
||||
@@ -243,53 +309,49 @@ bool ClassFlowControll::doFlow(string time)
|
||||
#endif
|
||||
|
||||
}
|
||||
zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||
aktstatus = zw_time + ": Flow is done";
|
||||
zw_time = gettimestring("%H:%M:%S");
|
||||
aktstatus = "Flow finished (" + zw_time + ")";
|
||||
return result;
|
||||
}
|
||||
|
||||
void ClassFlowControll::UpdateAktStatus(std::string _flow)
|
||||
{
|
||||
aktstatus = gettimestring("%Y%m%d-%H%M%S");
|
||||
aktstatus = aktstatus + "\t" + std::to_string(aktRunNr) + "\t";
|
||||
|
||||
if (_flow == "ClassFlowMakeImage")
|
||||
aktstatus = aktstatus + "Taking Raw Image";
|
||||
else
|
||||
if (_flow == "ClassFlowAlignment")
|
||||
aktstatus = aktstatus + "Aligning Image";
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getReadoutAll(int _type)
|
||||
{
|
||||
std::vector<NumberPost*> numbers = flowpostprocessing->GetNumbers();
|
||||
std::string out = "";
|
||||
|
||||
for (int i = 0; i < numbers.size(); ++i)
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
out = out + numbers[i]->name + "\t";
|
||||
switch (_type) {
|
||||
case READOUT_TYPE_VALUE:
|
||||
out = out + numbers[i]->ReturnValue;
|
||||
break;
|
||||
case READOUT_TYPE_PREVALUE:
|
||||
out = out + numbers[i]->ReturnPreValue;
|
||||
break;
|
||||
case READOUT_TYPE_RAWVALUE:
|
||||
out = out + numbers[i]->ReturnRawValue;
|
||||
break;
|
||||
case READOUT_TYPE_ERROR:
|
||||
out = out + numbers[i]->ErrorMessageText;
|
||||
break;
|
||||
}
|
||||
if (i < numbers.size()-1)
|
||||
out = out + "\r\n";
|
||||
}
|
||||
std::vector<NumberPost*> *numbers = flowpostprocessing->GetNumbers();
|
||||
|
||||
// printf("OUT: %s", out.c_str());
|
||||
for (int i = 0; i < (*numbers).size(); ++i)
|
||||
{
|
||||
out = out + (*numbers)[i]->name + "\t";
|
||||
switch (_type) {
|
||||
case READOUT_TYPE_VALUE:
|
||||
out = out + (*numbers)[i]->ReturnValue;
|
||||
break;
|
||||
case READOUT_TYPE_PREVALUE:
|
||||
if (flowpostprocessing->PreValueUse)
|
||||
{
|
||||
if ((*numbers)[i]->PreValueOkay)
|
||||
out = out + (*numbers)[i]->ReturnPreValue;
|
||||
else
|
||||
out = out + "PreValue too old";
|
||||
}
|
||||
else
|
||||
out = out + "PreValue deactivated";
|
||||
break;
|
||||
case READOUT_TYPE_RAWVALUE:
|
||||
out = out + (*numbers)[i]->ReturnRawValue;
|
||||
break;
|
||||
case READOUT_TYPE_ERROR:
|
||||
out = out + (*numbers)[i]->ErrorMessageText;
|
||||
break;
|
||||
}
|
||||
if (i < (*numbers).size()-1)
|
||||
out = out + "\r\n";
|
||||
}
|
||||
// printf("OUT: %s", out.c_str());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -328,7 +390,7 @@ string ClassFlowControll::GetPrevalue(std::string _number)
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers)
|
||||
std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern)
|
||||
{
|
||||
float zw;
|
||||
char* p;
|
||||
@@ -350,7 +412,7 @@ std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
flowpostprocessing->SetPreValue(zw, _numbers);
|
||||
flowpostprocessing->SetPreValue(zw, _numbers, _extern);
|
||||
return _newvalue;
|
||||
}
|
||||
|
||||
@@ -442,6 +504,7 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowControll::CleanTempFolder() {
|
||||
const char* folderPath = "/sdcard/img_tmp";
|
||||
|
||||
@@ -497,53 +560,59 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
{
|
||||
_send = flowalignment->ImageBasis;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (_fn == "alg_roi.jpg")
|
||||
else
|
||||
{
|
||||
CImageBasis* _imgzw = new CImageBasis(flowalignment->ImageBasis);
|
||||
flowalignment->DrawRef(_imgzw);
|
||||
if (flowdigit) flowdigit->DrawROI(_imgzw);
|
||||
if (flowanalog) flowanalog->DrawROI(_imgzw);
|
||||
_send = _imgzw;
|
||||
Dodelete = true;
|
||||
}
|
||||
if (_fn == "alg_roi.jpg")
|
||||
{
|
||||
CImageBasis* _imgzw = new CImageBasis(flowalignment->ImageBasis);
|
||||
flowalignment->DrawRef(_imgzw);
|
||||
if (flowdigit) flowdigit->DrawROI(_imgzw);
|
||||
if (flowanalog) flowanalog->DrawROI(_imgzw);
|
||||
_send = _imgzw;
|
||||
Dodelete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
htmlinfo = GetAllDigital();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
htmlinfo = GetAllDigital();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
if (!_send)
|
||||
{
|
||||
htmlinfo = GetAllAnalog();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
htmlinfo = GetAllAnalog();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
}
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
if (_send)
|
||||
{
|
||||
@@ -561,4 +630,37 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getJSON()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
#pragma once
|
||||
#ifndef __FLOWCONTROLL__
|
||||
#define __FLOWCONTROLL__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "ClassFlowMakeImage.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "ClassFlowDigit.h"
|
||||
#include "ClassFlowAnalog.h"
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "ClassFlowMQTT.h"
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
|
||||
|
||||
#define READOUT_TYPE_VALUE 0
|
||||
@@ -24,8 +25,9 @@ protected:
|
||||
std::vector<ClassFlow*> FlowControll;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
ClassFlowAlignment* flowalignment;
|
||||
ClassFlowAnalog* flowanalog;
|
||||
ClassFlowDigit* flowdigit;
|
||||
ClassFlowCNNGeneral* flowanalog;
|
||||
ClassFlowCNNGeneral* flowdigit;
|
||||
// ClassFlowDigit* flowdigit;
|
||||
ClassFlowMakeImage* flowmakeimage;
|
||||
ClassFlow* CreateClassFlow(std::string _type);
|
||||
|
||||
@@ -36,8 +38,6 @@ protected:
|
||||
std::string aktstatus;
|
||||
int aktRunNr;
|
||||
|
||||
void UpdateAktStatus(std::string _flow);
|
||||
|
||||
public:
|
||||
void InitFlow(std::string config);
|
||||
bool doFlow(string time);
|
||||
@@ -45,9 +45,14 @@ public:
|
||||
bool getStatusSetupModus(){return SetupModeActive;};
|
||||
string getReadout(bool _rawvalue, bool _noerror);
|
||||
string getReadoutAll(int _type);
|
||||
string UpdatePrevalue(std::string _newvalue, std::string _numbers);
|
||||
string UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern);
|
||||
string GetPrevalue(std::string _number = "");
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
string getJSON();
|
||||
|
||||
string TranslateAktstatus(std::string _input);
|
||||
|
||||
string GetMQTTMainTopic();
|
||||
|
||||
esp_err_t GetJPGStream(std::string _fn, httpd_req_t *req);
|
||||
esp_err_t SendRawJPG(httpd_req_t *req);
|
||||
@@ -56,14 +61,19 @@ public:
|
||||
|
||||
bool isAutoStart(long &_intervall);
|
||||
|
||||
std::string getActStatus();
|
||||
std::string* getActStatus();
|
||||
|
||||
std::vector<HTMLInfo*> GetAllDigital();
|
||||
std::vector<HTMLInfo*> GetAllAnalog();
|
||||
|
||||
t_CNNType GetTypeDigital();
|
||||
t_CNNType GetTypeAnalog();
|
||||
|
||||
int CleanTempFolder();
|
||||
|
||||
string name(){return "ClassFlowControll";};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user