diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5f4f737489..e242973103 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,92 +6,26 @@ on: pull_request: jobs: - build-master: - runs-on: ubuntu-latest - strategy: - matrix: - idf_target: ["esp32", "esp32s2", "esp32s3"] - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - name: esp-idf build - uses: espressif/esp-idf-ci-action@main - with: - target: ${{ matrix.idf_target }} - path: 'examples' - - build-release-v5_0: - name: Build for ${{ matrix.idf_target }} on ${{ matrix.idf_ver }} - runs-on: ubuntu-latest - strategy: - matrix: - idf_ver: ["release-v5.0"] - idf_target: ["esp32", "esp32s2", "esp32s3"] - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - name: esp-idf build - uses: espressif/esp-idf-ci-action@main - with: - esp_idf_version: ${{ matrix.idf_ver }} - target: ${{ matrix.idf_target }} - path: 'examples' - - build-release-v4_4: + build-examples: name: Build for ${{ matrix.idf_target }} on ${{ matrix.idf_ver }} runs-on: ubuntu-latest strategy: matrix: - idf_ver: ["v4.4"] - idf_target: ["esp32", "esp32s2", "esp32s3"] - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - name: esp-idf build - uses: espressif/esp-idf-ci-action@main - with: - esp_idf_version: ${{ matrix.idf_ver }} - target: ${{ matrix.idf_target }} - 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 + idf_ver: ["release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "latest"] + idf_target: ["esp32", "esp32s2", "esp32s3", "esp32c2", "esp32c3"] + container: espressif/idf:${{ matrix.idf_ver }} 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' + - uses: actions/checkout@v1 + with: + submodules: 'true' + - name: esp-idf build + env: + IDF_TARGET: ${{ matrix.idf_target }} + shell: bash + working-directory: examples/camera_example + run: | + . ${IDF_PATH}/export.sh + export PEDANTIC_FLAGS="-DIDF_CI_BUILD -Werror -Werror=deprecated-declarations -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function" + export EXTRA_CFLAGS="${PEDANTIC_FLAGS} -Wstrict-prototypes" + export EXTRA_CXXFLAGS="${PEDANTIC_FLAGS}" + idf.py build diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index f550e696de..4f95e43a1b 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -11,7 +11,7 @@ jobs: with: submodules: "recursive" - name: Upload component to the component registry - uses: espressif/github-actions/upload_components@master + uses: espressif/upload-components-ci-action@v1 with: name: "esp32-camera" namespace: "espressif" diff --git a/.gitignore b/.gitignore index 32125b791b..77f2db2e6e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ .vscode **/build **/sdkconfig -**/sdkconfig.old \ No newline at end of file +**/sdkconfig.old +**/dependencies.lock +**/managed_components/** \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a9267ba9a..53e29fb10e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,32 +1,30 @@ # get IDF version for comparison set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}") +set(priv_requires "") + # set conversion sources -set(COMPONENT_SRCS +set(srcs conversions/yuv.c conversions/to_jpg.cpp conversions/to_bmp.c conversions/jpge.cpp - conversions/esp_jpg_decode.c ) -set(COMPONENT_PRIV_INCLUDEDIRS +set(priv_include_dirs conversions/private_include ) -set(COMPONENT_ADD_INCLUDEDIRS +set(include_dirs driver/include conversions/include ) -set(COMPONENT_REQUIRES driver) - # set driver sources only for supported platforms if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3") - list(APPEND COMPONENT_SRCS + list(APPEND srcs driver/esp_camera.c driver/cam_hal.c - driver/sccb.c driver/sensor.c sensors/ov2640.c sensors/ov3660.c @@ -42,46 +40,69 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST sensors/sc101iot.c sensors/sc030iot.c sensors/sc031gs.c + sensors/mega_ccm.c + sensors/hm1055.c + sensors/hm0360.c ) - list(APPEND COMPONENT_PRIV_INCLUDEDIRS + list(APPEND priv_include_dirs driver/private_include sensors/private_include target/private_include ) if(IDF_TARGET STREQUAL "esp32") - list(APPEND COMPONENT_SRCS + list(APPEND srcs target/xclk.c target/esp32/ll_cam.c ) endif() if(IDF_TARGET STREQUAL "esp32s2") - list(APPEND COMPONENT_SRCS + list(APPEND srcs target/xclk.c target/esp32s2/ll_cam.c - target/esp32s2/tjpgd.c ) - list(APPEND COMPONENT_PRIV_INCLUDEDIRS + list(APPEND priv_include_dirs target/esp32s2/private_include ) endif() if(IDF_TARGET STREQUAL "esp32s3") - list(APPEND COMPONENT_SRCS + list(APPEND srcs target/esp32s3/ll_cam.c ) endif() - set(COMPONENT_PRIV_REQUIRES freertos nvs_flash) + list(APPEND priv_requires freertos nvs_flash esp_mm) set(min_version_for_esp_timer "4.2") if (idf_version VERSION_GREATER_EQUAL min_version_for_esp_timer) - list(APPEND COMPONENT_PRIV_REQUIRES esp_timer) + list(APPEND priv_requires esp_timer) + endif() + + # include the SCCB I2C driver + # this uses either the legacy I2C API or the newer version from IDF v5.4 + # as this features a method to obtain the I2C driver from a port number + if (idf_version VERSION_GREATER_EQUAL "5.4" AND NOT CONFIG_SCCB_HARDWARE_I2C_DRIVER_LEGACY) + list(APPEND srcs driver/sccb-ng.c) + else() + list(APPEND srcs driver/sccb.c) endif() endif() -register_component() +set(req driver) +if (idf_version VERSION_GREATER_EQUAL "6.0") + list(APPEND priv_requires esp_driver_gpio esp_driver_spi esp_driver_i2c) + list(APPEND req esp_driver_ledc) +endif() + +idf_component_register( + SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_INCLUDE_DIRS ${priv_include_dirs} + REQUIRES ${req} + PRIV_REQUIRES ${priv_requires} +) diff --git a/Kconfig b/Kconfig index 2632c8235f..5961e61d3d 100755 --- a/Kconfig +++ b/Kconfig @@ -116,6 +116,41 @@ menu "Camera configuration" SC031GS is a global shutter CMOS sensor with high frame rate and single-frame HDR. Enable this option if you want to use the SC031GS. Disable this option to save memory. + + config HM1055_SUPPORT + bool "Support HM1055 VGA" + default y + help + Enable this option if you want to use the HM1055. + Disable this option to save memory. + + config HM0360_SUPPORT + bool "Support HM0360 VGA" + default y + help + Enable this option if you want to use the HM0360. + Disable this option to save memory. + + config MEGA_CCM_SUPPORT + bool "Support MEGA CCM 5MP" + default y + help + Enable this option if you want to use the MEGA CCM. + Disable this option to save memory. + + choice SCCB_HARDWARE_I2C_DRIVER_SELECTION + prompt "I2C driver selection for SCCB" + default SCCB_HARDWARE_I2C_DRIVER_NEW + help + Select the I2C driver to use for SCCB communication. + NOTE: new driver is only supported for ESP-IDF >= 5.4. + + config SCCB_HARDWARE_I2C_DRIVER_LEGACY + bool "Legacy I2C driver" + config SCCB_HARDWARE_I2C_DRIVER_NEW + bool "New I2C driver" + + endchoice choice SCCB_HARDWARE_I2C_PORT bool "I2C peripheral to use for SCCB" @@ -152,7 +187,7 @@ menu "Camera configuration" config CAMERA_TASK_STACK_SIZE int "CAM task stack size" - default 2048 + default 4096 help Camera task stack size @@ -179,6 +214,41 @@ menu "Camera configuration" Maximum value of DMA buffer Larger values may fail to allocate due to insufficient contiguous memory blocks, and smaller value may cause DMA interrupt to be too frequent. + config CAMERA_PSRAM_DMA + bool "Enable PSRAM DMA mode by default" + depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default n + help + Enable DMA transfers directly from PSRAM on supported targets + (ESP32-S2 and ESP32-S3) by default. + + choice CAMERA_JPEG_MODE_FRAME_SIZE_OPTION + prompt "JPEG mode frame size option" + default CAMERA_JPEG_MODE_FRAME_SIZE_AUTO + help + Select whether to use automatic calculation for JPEG mode frame size or specify a custom value. + + config CAMERA_JPEG_MODE_FRAME_SIZE_AUTO + bool "Use automatic calculation (width * height / 5)" + help + Use the default calculation for JPEG mode frame size. + Note: In very low resolutions like QQVGA, the default calculation tends to result in insufficient buffer size. + + config CAMERA_JPEG_MODE_FRAME_SIZE_CUSTOM + bool "Specify custom frame size" + help + Specify a custom frame size in bytes for JPEG mode. + + endchoice + + config CAMERA_JPEG_MODE_FRAME_SIZE + int "Custom JPEG mode frame size (bytes)" + default 8192 + depends on CAMERA_JPEG_MODE_FRAME_SIZE_CUSTOM + help + This option sets the custom frame size in JPEG mode. + Specify the desired buffer size in bytes. + config CAMERA_CONVERTER_ENABLED bool "Enable camera RGB/YUV converter" depends on IDF_TARGET_ESP32S3 @@ -210,4 +280,11 @@ menu "Camera configuration" Full color range mode has a wider color range, so details in the image show more clearly. Please confirm the color range mode of the current camera sensor, incorrect color range mode may cause color difference in the final converted image. Full range mode is used by default. If this option is not selected, the format conversion function will be done using the limited range mode. + + config LCD_CAM_ISR_IRAM_SAFE + bool "Execute camera ISR from IRAM" + depends on (IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3) + default n + help + If this option is enabled, camera ISR will execute from IRAM. endmenu diff --git a/README.md b/README.md index aeae6232d5..ea71806286 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ESP32 Camera Driver -[![Build examples](https://github.com/espressif/esp32-camera/actions/workflows/build.yml/badge.svg)](https://github.com/espressif/esp32-camera/actions/workflows/build.yml) +[![Build examples](https://github.com/espressif/esp32-camera/actions/workflows/build.yml/badge.svg)](https://github.com/espressif/esp32-camera/actions/workflows/build.yml) [![Component Registry](https://components.espressif.com/components/espressif/esp32-camera/badge.svg)](https://components.espressif.com/components/espressif/esp32-camera) ## General Information 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. @@ -22,13 +22,15 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi | OV7725 | 640 x 480 | color | Raw RGB
GRB 422
RGB565/555/444
YCbCr 422 | 1/4" | | NT99141 | 1280 x 720 | color | YCbCr 422
RGB565/555/444
Raw
CCIR656
JPEG compression | 1/4" | | GC032A | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/10" | -| GC0308 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/6.5" | +| GC0308 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
RGB565
Grayscale | 1/6.5" | | GC2145 | 1600 x 1200 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/5" | | BF3005 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/4" | -| BF20A6 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer | 1/10" | +| BF20A6 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
Only Y | 1/10" | | SC101IOT| 1280 x 720 | color | YUV/YCbCr422
Raw RGB | 1/4.2" | | SC030IOT| 640 x 480 | color | YUV/YCbCr422
RAW Bayer | 1/6.5" | | SC031GS | 640 x 480 | monochrome | RAW MONO
Grayscale | 1/6" | +| HM0360 | 656 x 496 | monochrome | RAW MONO
Grayscale | 1/6" | +| HM1055 | 1280 x 720 | color | 8/10-bit Raw
YUV/YCbCr422
RGB565/555/444 | 1/6" | ## Important to Remember @@ -36,17 +38,31 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi - Using YUV or RGB puts a lot of strain on the chip because writing to PSRAM is not particularly fast. The result is that image data might be missing. This is particularly true if WiFi is enabled. If you need RGB data, it is recommended that JPEG is captured and then turned into RGB using `fmt2rgb888` or `fmt2bmp`/`frame2bmp`. - When 1 frame buffer is used, the driver will wait for the current frame to finish (VSYNC) and start I2S DMA. After the frame is acquired, I2S will be stopped and the frame buffer returned to the application. This approach gives more control over the system, but results in longer time to get the frame. - When 2 or more frame bufers are used, I2S is running in continuous mode and each frame is pushed to a queue that the application can access. This approach puts more strain on the CPU/Memory, but allows for double the frame rate. Please use only with JPEG. +- The Kconfig option `CONFIG_CAMERA_PSRAM_DMA` enables PSRAM DMA mode on ESP32-S2 and ESP32-S3 devices. This flag defaults to false. +- You can switch PSRAM DMA mode at runtime using `esp_camera_set_psram_mode()`. ## Installation Instructions -### Using esp-idf +### Using with ESP-IDF -- Clone or download and extract the repository to the components folder of your ESP-IDF project +- Add a dependency on `espressif/esp32-camera` component: + ```bash + idf.py add-dependency "espressif/esp32-camera" + ``` + (or add it manually in idf_component.yml of your project) - Enable PSRAM in `menuconfig` (also set Flash and PSRAM frequiencies to 80MHz) - Include `esp_camera.h` in your code -### Using PlatformIO +These instructions also work for PlatformIO, if you are using `framework=espidf`. + +### Using with Arduino + +#### Arduino IDE + +If you are using the arduino-esp32 core in Arduino IDE, no installation is needed! You can use esp32-camera right away. + +#### PlatformIO The easy way -- on the `env` section of `platformio.ini`, add the following: @@ -68,41 +84,15 @@ Enable PSRAM on `menuconfig` or type it direclty on `sdkconfig`. Check the [offi CONFIG_ESP32_SPIRAM_SUPPORT=y ``` -***Arduino*** The easy-way (content above) only seems to work if you're using `framework=arduino` which seems to take a bunch of the guesswork out (thanks Arduino!) but also suck up a lot more memory and flash, almost crippling the performance. If you plan to use the `framework=espidf` then read the sections below carefully!! - -## Platform.io lib/submodule (for framework=espidf) +## Examples -It's probably easier to just skip the platform.io library registry version and link the git repo as a submodule. (i.e. using code outside the platform.io library management). In this example we will install this as a submodule inside the platform.io $project/lib folder: -``` -cd $project\lib -git submodule add -b master https://github.com/espressif/esp32-camera.git -``` +This component comes with a basic example illustrating how to get frames from the camera. You can try out the example using the following command: -Then in `platformio.ini` file ``` -build_flags = - -I../lib/esp32-camera +idf.py create-project-from-example "espressif/esp32-camera:camera_example" ``` -After that `#include "esp_camera.h"` statement will be available. Now the module is included, and you're hopefully back to the same place as the easy-Arduino way. - -**Warning about platform.io/espidf and fresh (not initialized) git repos** -There is a sharp-edge on you'll discover in the platform.io build process (in espidf v3.3 & 4.0.1) where a project which has only had `git init` but nothing committed will crash platform.io build process with highly non-useful output. The cause is due to lack of a version (making you think you did something wrong, when you didn't at all) - the output is horribly non-descript. Solution: the devs want you to create a file called version.txt with a number in it, or simply commit any file to the projects git repo and use git. This happens because platform.io build process tries to be too clever and determine the build version number from the git repo - it's a sharp edge you'll only encounter if you're experimenting on a new project with no commits .. like wtf is my camera not working let's try a 'clean project'?! - -## Platform.io Kconfig -Kconfig is used by the platform.io menuconfig (accessed by running: `pio run -t menuconfig`) to interactively manage the various #ifdef statements throughout the espidf and supporting libraries (i.e. this repo: esp32-camera and arduino-esp32.git). The menuconfig process generates the `sdkconfig` file which is ultimately used behind the scenes by espidf compile+build process. -**Make sure to append or symlink** [this `Kconfig`](./Kconfig) content into the `Kconfig` of your project. - -You symlink (or copy) the included Kconfig into your platform.io projects src directory. The file should be named `Kconfig.projbuild` in your projects src\ directory or you could also add the library path to a CMakefile.txt and hope the `Kconfig` (or `Kconfig.projbuild`) gets discovered by the menuconfig process, though this unpredictable for me. - -The unpredictable wonky behavior in platform.io build process around Kconfig naming (Kconfig vs. Kconfig.projbuild) occurs between espidf versions 3.3 and 4.0 - but if you don't see "Camera configuration" in your `pio run -t menuconfig` then there is no point trying to test camera code (it may compile, but it probably won't work!) and it seems the platform.io devs (when they built their wrapper around the espidf menuconfig) didn't implement it properly. You've probably already figured out you can't use the espidf build tools since the files are in totally different locations and also different versions with sometimes different syntax. This is one of those times you might consider changing the `platformio.ini` from `platform=espressif32` to `platform=https://github.com/platformio/platform-espressif32.git#develop` to get a more recent version of the espidf 4.0 tools. - -However with a bit of patience and experimenting you'll figure the Kconfig out. Once Kconfig (or Kconfig.projbuild) is working then you will be able to choose the configurations according to your setup or the camera libraries will be compiled. Although you might also need to delete your .pio/build directory before the options appear .. again, the `pio run -t menuconfig` doens't always notice the new Kconfig files! - -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! - - -## Examples +This command will download the example into `camera_example` directory. It comes already pre-configured with the correct settings in menuconfig. ### Initialization @@ -147,15 +137,15 @@ static camera_config_t camera_config = { .pin_href = CAM_PIN_HREF, .pin_pclk = CAM_PIN_PCLK, - .xclk_freq_hz = 20000000,//EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode + .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_JPEG,//YUV422,GRAYSCALE,RGB565,JPEG - .frame_size = FRAMESIZE_UXGA,//QQVGA-QXGA Do not use sizes above QVGA when not JPEG + .frame_size = FRAMESIZE_UXGA,//QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates. - .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 + .jpeg_quality = 12, //0-63, for OV series camera sensors, lower number means higher quality + .fb_count = 1, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode. .grab_mode = CAMERA_GRAB_WHEN_EMPTY//CAMERA_GRAB_LATEST. Sets when buffers should be filled }; @@ -261,14 +251,14 @@ esp_err_t jpg_httpd_handler(httpd_req_t *req){ #define PART_BOUNDARY "123456789000000000000987654321" static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; -static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; +static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %zu\r\n\r\n"; esp_err_t jpg_stream_httpd_handler(httpd_req_t *req){ camera_fb_t * fb = NULL; esp_err_t res = ESP_OK; - size_t _jpg_buf_len; - uint8_t * _jpg_buf; - char * part_buf[64]; + size_t jpg_buf_len = 0; + uint8_t * jpg_buf = NULL; + char part_buf[64]; static int64_t last_frame = 0; if(!last_frame) { last_frame = esp_timer_get_time(); @@ -287,30 +277,36 @@ esp_err_t jpg_stream_httpd_handler(httpd_req_t *req){ break; } if(fb->format != PIXFORMAT_JPEG){ - bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len); + bool jpeg_converted = frame2jpg(fb, 80, &jpg_buf, &jpg_buf_len); if(!jpeg_converted){ ESP_LOGE(TAG, "JPEG compression failed"); esp_camera_fb_return(fb); res = ESP_FAIL; + break; } } else { - _jpg_buf_len = fb->len; - _jpg_buf = fb->buf; + jpg_buf_len = fb->len; + jpg_buf = fb->buf; } if(res == ESP_OK){ res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); } if(res == ESP_OK){ - size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len); - - res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); + int hlen = snprintf(part_buf, sizeof(part_buf), _STREAM_PART, jpg_buf_len); + if(hlen < 0 || hlen >= sizeof(part_buf)){ + ESP_LOGE(TAG, "Header truncated (%d bytes needed >= %zu buffer)", + hlen, sizeof(part_buf)); + res = ESP_FAIL; + } else { + res = httpd_resp_send_chunk(req, part_buf, (size_t)hlen); + } } if(res == ESP_OK){ - res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len); + res = httpd_resp_send_chunk(req, (const char *)jpg_buf, jpg_buf_len); } if(fb->format != PIXFORMAT_JPEG){ - free(_jpg_buf); + free(jpg_buf); } esp_camera_fb_return(fb); if(res != ESP_OK){ @@ -320,9 +316,10 @@ esp_err_t jpg_stream_httpd_handler(httpd_req_t *req){ int64_t frame_time = fr_end - last_frame; last_frame = fr_end; frame_time /= 1000; + float fps = frame_time > 0 ? 1000.0f / (float)frame_time : 0.0f; ESP_LOGI(TAG, "MJPG: %uKB %ums (%.1ffps)", - (uint32_t)(_jpg_buf_len/1024), - (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time); + (uint32_t)(jpg_buf_len/1024), + (uint32_t)frame_time, fps); } last_frame = 0; diff --git a/component.mk b/component.mk deleted file mode 100755 index 8db15eb883..0000000000 --- a/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -COMPONENT_ADD_INCLUDEDIRS := driver/include conversions/include -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 diff --git a/conversions/esp_jpg_decode.c b/conversions/esp_jpg_decode.c deleted file mode 100644 index 52833a733d..0000000000 --- a/conversions/esp_jpg_decode.c +++ /dev/null @@ -1,136 +0,0 @@ -// 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 "esp_jpg_decode.h" - -#include "esp_system.h" -#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+ -#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 -#include "esp32/rom/tjpgd.h" -#elif CONFIG_IDF_TARGET_ESP32S2 -#include "tjpgd.h" -#elif CONFIG_IDF_TARGET_ESP32S3 -#include "esp32s3/rom/tjpgd.h" -#elif CONFIG_IDF_TARGET_ESP32C3 -#include "esp32c3/rom/tjpgd.h" -#elif CONFIG_IDF_TARGET_ESP32H2 -#include "esp32h2/rom/tjpgd.h" -#else -#error Target CONFIG_IDF_TARGET is not supported -#endif -#else // ESP32 Before IDF 4.0 -#include "rom/tjpgd.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 = "esp_jpg_decode"; -#endif - -typedef struct { - jpg_scale_t scale; - jpg_reader_cb reader; - jpg_writer_cb writer; - void * arg; - size_t len; - size_t index; -} esp_jpg_decoder_t; - -static const char * jd_errors[] = { - "Succeeded", - "Interrupted by output function", - "Device error or wrong termination of input stream", - "Insufficient memory pool for the image", - "Insufficient stream input buffer", - "Parameter error", - "Data format error", - "Right format but not supported", - "Not supported JPEG standard" -}; - -static unsigned int _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect) -{ - uint16_t x = rect->left; - uint16_t y = rect->top; - uint16_t w = rect->right + 1 - x; - uint16_t h = rect->bottom + 1 - y; - uint8_t *data = (uint8_t *)bitmap; - - esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device; - - if (jpeg->writer) { - return jpeg->writer(jpeg->arg, x, y, w, h, data); - } - return 0; -} - -static unsigned int _jpg_read(JDEC *decoder, uint8_t *buf, unsigned int len) -{ - esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device; - if (jpeg->len && len > (jpeg->len - jpeg->index)) { - len = jpeg->len - jpeg->index; - } - if (len) { - len = jpeg->reader(jpeg->arg, jpeg->index, buf, len); - if (!len) { - ESP_LOGE(TAG, "Read Fail at %u/%u", jpeg->index, jpeg->len); - } - jpeg->index += len; - } - return len; -} - -esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg) -{ - static uint8_t work[3100]; - JDEC decoder; - esp_jpg_decoder_t jpeg; - - jpeg.len = len; - jpeg.reader = reader; - jpeg.writer = writer; - jpeg.arg = arg; - jpeg.scale = scale; - jpeg.index = 0; - - JRESULT jres = jd_prepare(&decoder, _jpg_read, work, 3100, &jpeg); - if(jres != JDR_OK){ - ESP_LOGE(TAG, "JPG Header Parse Failed! %s", jd_errors[jres]); - return ESP_FAIL; - } - - uint16_t output_width = decoder.width / (1 << (uint8_t)(jpeg.scale)); - uint16_t output_height = decoder.height / (1 << (uint8_t)(jpeg.scale)); - - //output start - writer(arg, 0, 0, output_width, output_height, NULL); - //output write - jres = jd_decomp(&decoder, _jpg_write, (uint8_t)jpeg.scale); - //output end - writer(arg, output_width, output_height, output_width, output_height, NULL); - - if (jres != JDR_OK) { - ESP_LOGE(TAG, "JPG Decompression Failed! %s", jd_errors[jres]); - return ESP_FAIL; - } - //check if all data has been consumed. - if (len && jpeg.index < len) { - _jpg_read(&decoder, NULL, len - jpeg.index); - } - - return ESP_OK; -} - diff --git a/conversions/include/esp_jpg_decode.h b/conversions/include/esp_jpg_decode.h deleted file mode 100644 index f13536edf4..0000000000 --- a/conversions/include/esp_jpg_decode.h +++ /dev/null @@ -1,43 +0,0 @@ -// 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. -#ifndef _ESP_JPG_DECODE_H_ -#define _ESP_JPG_DECODE_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include "esp_err.h" - -typedef enum { - JPG_SCALE_NONE, - JPG_SCALE_2X, - JPG_SCALE_4X, - JPG_SCALE_8X, - JPG_SCALE_MAX = JPG_SCALE_8X -} jpg_scale_t; - -typedef size_t (* jpg_reader_cb)(void * arg, size_t index, uint8_t *buf, size_t len); -typedef bool (* jpg_writer_cb)(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data); - -esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg); - -#ifdef __cplusplus -} -#endif - -#endif /* _ESP_JPG_DECODE_H_ */ diff --git a/conversions/include/img_converters.h b/conversions/include/img_converters.h index f736200a9b..6e71ce93fd 100644 --- a/conversions/include/img_converters.h +++ b/conversions/include/img_converters.h @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2025 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. @@ -22,7 +22,7 @@ extern "C" { #include #include #include "esp_camera.h" -#include "esp_jpg_decode.h" +#include "jpeg_decoder.h" typedef size_t (* jpg_out_cb)(void * arg, size_t index, const void* data, size_t len); @@ -121,7 +121,13 @@ 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); +// Macros for backwards compatibility +#define JPG_SCALE_NONE JPEG_IMAGE_SCALE_0 +#define JPG_SCALE_2X JPEG_IMAGE_SCALE_1_2 +#define JPG_SCALE_4X JPEG_IMAGE_SCALE_1_4 +#define JPG_SCALE_8X JPEG_IMAGE_SCALE_1_8 +#define JPG_SCALE_MAX JPEG_IMAGE_SCALE_1_8 +bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, esp_jpeg_image_scale_t scale); #ifdef __cplusplus } diff --git a/conversions/jpge.cpp b/conversions/jpge.cpp index dd6790e63b..a8586cea91 100644 --- a/conversions/jpge.cpp +++ b/conversions/jpge.cpp @@ -30,7 +30,7 @@ namespace jpge { return b; } // check if SPIRAM is enabled and allocate on SPIRAM if allocatable -#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) +#if ((CONFIG_SPIRAM || CONFIG_SPIRAM_SUPPORT) && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) return heap_caps_malloc(nSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); #else return NULL; diff --git a/conversions/to_bmp.c b/conversions/to_bmp.c index 27072f7113..97e790b555 100644 --- a/conversions/to_bmp.c +++ b/conversions/to_bmp.c @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2025 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. @@ -13,12 +13,13 @@ // limitations under the License. #include #include +#include #include "img_converters.h" #include "soc/efuse_reg.h" #include "esp_heap_caps.h" #include "yuv.h" #include "sdkconfig.h" -#include "esp_jpg_decode.h" +#include "jpeg_decoder.h" #include "esp_system.h" @@ -31,6 +32,7 @@ static const char* TAG = "to_bmp"; #endif static const int BMP_HEADER_LEN = 54; +static uint8_t work[3100]; // 3.1kB for JPEG decoder, static for legacy reasons typedef struct { uint32_t filesize; @@ -49,151 +51,54 @@ typedef struct { uint32_t mostimpcolor; } bmp_header_t; -typedef struct { - uint16_t width; - uint16_t height; - uint16_t data_offset; - const uint8_t *input; - uint8_t *output; -} rgb_jpg_decoder; - static void *_malloc(size_t size) { // check if SPIRAM is enabled and allocate on SPIRAM if allocatable -#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) +#if ((CONFIG_SPIRAM || CONFIG_SPIRAM_SUPPORT) && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); #endif // try allocating in internal memory return malloc(size); } -//output buffer and image width -static bool _rgb_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 t = y * jw; - size_t b = t + (h * jw); - size_t l = x * 3; - uint8_t *out = jpeg->output+jpeg->data_offset; - uint8_t *o = out; - size_t iy, ix; - - w = w * 3; - - for(iy=t; iywidth = 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> 3); - o[ix2+1] = c>>8; - o[ix2] = c&0xff; - } - data+=w; - } - return true; -} - -//input buffer -static unsigned int _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len) +static bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, esp_jpeg_image_scale_t scale) { - rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg; - if(buf) { - memcpy(buf, jpeg->input + index, len); - } - return len; -} - -static bool jpg2rgb888(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, _rgb_write, (void*)&jpeg) != ESP_OK){ + esp_jpeg_image_cfg_t jpeg_cfg = { + .indata = (uint8_t *)src, + .indata_size = src_len, + .outbuf = out, + .outbuf_size = UINT32_MAX, // @todo: this is very bold assumption, keeping this like this for now, not to break existing code + .out_format = JPEG_IMAGE_FORMAT_RGB888, + .out_scale = scale, + .flags.swap_color_bytes = 0, + .advanced.working_buffer = work, + .advanced.working_buffer_size = sizeof(work), + }; + esp_jpeg_image_output_t output_img = {}; + + if(esp_jpeg_decode(&jpeg_cfg, &output_img) != ESP_OK){ return false; } return true; } -bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale) +bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, esp_jpeg_image_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){ + esp_jpeg_image_cfg_t jpeg_cfg = { + .indata = (uint8_t *)src, + .indata_size = src_len, + .outbuf = out, + .outbuf_size = UINT32_MAX, // @todo: this is very bold assumption, keeping this like this for now, not to break existing code + .out_format = JPEG_IMAGE_FORMAT_RGB565, + .out_scale = scale, + .flags.swap_color_bytes = 0, + .advanced.working_buffer = work, + .advanced.working_buffer_size = sizeof(work), + }; + + esp_jpeg_image_output_t output_img = {}; + + if(esp_jpeg_decode(&jpeg_cfg, &output_img) != ESP_OK){ return false; } return true; @@ -201,49 +106,76 @@ bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t s bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_len) { + esp_jpeg_image_cfg_t jpeg_cfg = { + .indata = (uint8_t *)src, + .indata_size = src_len, + .out_format = JPEG_IMAGE_FORMAT_RGB888, + .out_scale = JPEG_IMAGE_SCALE_0, + .flags.swap_color_bytes = 0, + .advanced.working_buffer = work, + .advanced.working_buffer_size = sizeof(work), + }; + + bool ret = false; + uint8_t *output = NULL; + esp_jpeg_image_output_t output_img = {}; + if (esp_jpeg_get_image_info(&jpeg_cfg, &output_img) != ESP_OK) { + ESP_LOGE(TAG, "Failed to get image info"); + goto fail; + } - rgb_jpg_decoder jpeg; - jpeg.width = 0; - jpeg.height = 0; - jpeg.input = src; - jpeg.output = NULL; - jpeg.data_offset = BMP_HEADER_LEN; - - if(esp_jpg_decode(src_len, JPG_SCALE_NONE, _jpg_read, _rgb_write, (void*)&jpeg) != ESP_OK){ - return false; + // @todo here we allocate memory and we assume that the user will free it + // this is not the best way to do it, but we need to keep the API + // compatible with the previous version + const size_t output_size = output_img.output_len + BMP_HEADER_LEN; + output = _malloc(output_size); + if (!output) { + ESP_LOGE(TAG, "Failed to allocate output buffer"); + goto fail; } - size_t output_size = jpeg.width*jpeg.height*3; + // Start writing decoded data after the BMP header + jpeg_cfg.outbuf = output + BMP_HEADER_LEN; + jpeg_cfg.outbuf_size = output_img.output_len; + if(esp_jpeg_decode(&jpeg_cfg, &output_img) != ESP_OK){ + ESP_LOGE(TAG, "JPEG decode failed"); + goto fail; + } - jpeg.output[0] = 'B'; - jpeg.output[1] = 'M'; - bmp_header_t * bitmap = (bmp_header_t*)&jpeg.output[2]; + output[0] = 'B'; + output[1] = 'M'; + bmp_header_t * bitmap = (bmp_header_t*)&output[2]; bitmap->reserved = 0; - bitmap->filesize = output_size+BMP_HEADER_LEN; + bitmap->filesize = output_size; bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN; bitmap->dibheadersize = 40; - bitmap->width = jpeg.width; - bitmap->height = -jpeg.height;//set negative for top to bottom + bitmap->width = output_img.width; + bitmap->height = -output_img.height; //set negative for top to bottom bitmap->planes = 1; bitmap->bitsperpixel = 24; bitmap->compression = 0; - bitmap->imagesize = output_size; + bitmap->imagesize = output_img.output_len; bitmap->ypixelpermeter = 0x0B13 ; //2835 , 72 DPI bitmap->xpixelpermeter = 0x0B13 ; //2835 , 72 DPI bitmap->numcolorspallette = 0; bitmap->mostimpcolor = 0; - *out = jpeg.output; - *out_len = output_size+BMP_HEADER_LEN; + *out = output; + *out_len = output_size; + ret = true; - return true; +fail: + if (!ret && output) { + free(output); + } + return ret; } bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf) { int pix_count = 0; if(format == PIXFORMAT_JPEG) { - return jpg2rgb888(src_buf, src_len, rgb_buf, JPG_SCALE_NONE); + return jpg2rgb888(src_buf, src_len, rgb_buf, JPEG_IMAGE_SCALE_0); } else if(format == PIXFORMAT_RGB888) { memcpy(rgb_buf, src_buf, src_len); } else if(format == PIXFORMAT_RGB565) { diff --git a/conversions/to_jpg.cpp b/conversions/to_jpg.cpp index 24cc2989cd..a4ac804d1d 100644 --- a/conversions/to_jpg.cpp +++ b/conversions/to_jpg.cpp @@ -37,7 +37,7 @@ static void *_malloc(size_t size) } // check if SPIRAM is enabled and is allocatable -#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) +#if ((CONFIG_SPIRAM || CONFIG_SPIRAM_SUPPORT) && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); #endif return NULL; diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 27f66b684d..768d93ae87 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -14,7 +14,10 @@ #include #include +#include #include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "ll_cam.h" #include "cam_hal.h" @@ -22,6 +25,13 @@ #include "rom/ets_sys.h" #else #include "esp_timer.h" +#include "esp_cache.h" +#include "hal/cache_hal.h" +#include "hal/cache_ll.h" +#include "esp_idf_version.h" +#ifndef ESP_CACHE_MSYNC_FLAG_DIR_M2C +#define ESP_CACHE_MSYNC_FLAG_DIR_M2C 0 +#endif #if CONFIG_IDF_TARGET_ESP32 #include "esp32/rom/ets_sys.h" // will be removed in idf v5.0 #elif CONFIG_IDF_TARGET_ESP32S2 @@ -30,47 +40,177 @@ #include "esp32s3/rom/ets_sys.h" #endif #endif // ESP_IDF_VERSION_MAJOR -#define ESP_CAMERA_ETS_PRINTF ets_printf -#if CONFIG_CAM_TASK_STACK_SIZE -#define CAM_TASK_STACK CONFIG_CAM_TASK_STACK_SIZE +#if CONFIG_LOG_DEFAULT_LEVEL_NONE +#define ESP_CAMERA_ETS_PRINTF(f, ...) +#else +#define ESP_CAMERA_ETS_PRINTF(f, ...) ets_printf(f, ##__VA_ARGS__) +#endif + +#if CONFIG_CAMERA_TASK_STACK_SIZE +#define CAM_TASK_STACK CONFIG_CAMERA_TASK_STACK_SIZE #else -#define CAM_TASK_STACK (2*1024) +#define CAM_TASK_STACK (4*1024) #endif static const char *TAG = "cam_hal"; static cam_obj_t *cam_obj = NULL; +#if defined(CONFIG_CAMERA_PSRAM_DMA) +#define CAMERA_PSRAM_DMA_ENABLED CONFIG_CAMERA_PSRAM_DMA +#else +#define CAMERA_PSRAM_DMA_ENABLED 0 +#endif + +static volatile bool g_psram_dma_mode = CAMERA_PSRAM_DMA_ENABLED; +static portMUX_TYPE g_psram_dma_lock = portMUX_INITIALIZER_UNLOCKED; + +/* At top of cam_hal.c – one switch for noisy ISR prints */ +#ifndef CAM_LOG_SPAM_EVERY_FRAME +#define CAM_LOG_SPAM_EVERY_FRAME 0 /* set to 1 to restore old behaviour */ +#endif + +/* Number of bytes copied to SRAM for SOI validation when capturing + * directly to PSRAM. Tunable to probe more of the frame start if needed. */ +#ifndef CAM_SOI_PROBE_BYTES +#define CAM_SOI_PROBE_BYTES 32 +#endif +/* + * PSRAM DMA may bypass the CPU cache. Always call esp_cache_msync() on + * PSRAM regions that the CPU will read so cached reads see the data written + * by DMA. + */ + +static inline size_t dcache_line_size(void) +{ +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) + /* cache_hal_get_cache_line_size() added extra argument from IDF 5.2 */ + return cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); +#else + /* Older releases only expose the ROM helper, all current targets + * have a 32‑byte DCache line */ + return 32; +#endif +} + +/* + * Invalidate CPU data cache lines that cover a region in PSRAM which + * has just been written by DMA. This guarantees subsequent CPU reads + * fetch the fresh data from PSRAM rather than stale cache contents. + * Both address and length are aligned to the data cache line size. + */ +static inline void cam_drop_psram_cache(void *addr, size_t len) +{ + size_t line = dcache_line_size(); + if (line == 0) { + line = 32; /* sane fallback */ + } + uintptr_t start = (uintptr_t)addr & ~(line - 1); + size_t sync_len = (len + ((uintptr_t)addr - start) + line - 1) & ~(line - 1); + esp_cache_msync((void *)start, sync_len, + ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_INVALIDATE); +} + +/* Throttle repeated warnings printed from tight loops / ISRs. + * + * counter – static DRAM/IRAM uint16_t you pass in + * first – literal C string shown on first hit and as prefix of summaries + */ +#if CONFIG_LOG_DEFAULT_LEVEL >= 2 +#define CAM_WARN_THROTTLE(counter, first) \ + do { \ + if (++(counter) == 1) { \ + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: %s\r\n"), first); \ + } else if ((counter) % 100 == 0) { \ + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: %s - 100 additional misses\r\n"), first); \ + } \ + if ((counter) == 10000) (counter) = 1; \ + } while (0) +#else +#define CAM_WARN_THROTTLE(counter, first) do { (void)(counter); } while (0) +#endif -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 +/* JPEG markers (byte-order independent). */ +static const uint8_t JPEG_SOI_MARKER[] = {0xFF, 0xD8, 0xFF}; /* SOI = FF D8 FF */ +#define JPEG_SOI_MARKER_LEN (3) +static const uint8_t JPEG_EOI_BYTES[] = {0xFF, 0xD9}; /* EOI = FF D9 */ +#define JPEG_EOI_MARKER_LEN (2) + +/* Compute the scan window for JPEG EOI detection in PSRAM. */ +static inline size_t eoi_probe_window(size_t half, size_t frame_len) +{ + size_t w = half + (JPEG_EOI_MARKER_LEN - 1); + return w > frame_len ? frame_len : w; +} 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", (int) i); + static uint16_t warn_soi_miss_cnt = 0; + if (length < JPEG_SOI_MARKER_LEN) { + CAM_WARN_THROTTLE(warn_soi_miss_cnt, + "NO-SOI - JPEG start marker missing (len < 3b)"); + return -1; + } + + for (uint32_t i = 0; i <= length - JPEG_SOI_MARKER_LEN; i++) { + if (memcmp(&inbuf[i], JPEG_SOI_MARKER, JPEG_SOI_MARKER_LEN) == 0) { + //ESP_LOGW(TAG, "SOI: %d", (int) i); + return i; + } + } + + CAM_WARN_THROTTLE(warn_soi_miss_cnt, + "NO-SOI - JPEG start marker missing"); + return -1; +} + +static int cam_verify_jpeg_eoi(const uint8_t *inbuf, uint32_t length, bool search_forward) +{ + if (length < JPEG_EOI_MARKER_LEN) { + return -1; + } + + if (search_forward) { + /* Scan forward to honor the earliest marker in the buffer. This avoids + * returning an EOI that belongs to a larger previous frame when the tail + * of that frame still resides in PSRAM. JPEG data is pseudo random, so + * the first marker byte appears rarely; test four positions per load to + * reduce memory traffic. */ + const uint8_t *pat = JPEG_EOI_BYTES; + const uint32_t A = pat[0] * 0x01010101u; + const uint32_t ONE = 0x01010101u; + const uint32_t HIGH = 0x80808080u; + uint32_t i = 0; + while (i + 4 <= length) { + uint32_t w; + memcpy(&w, inbuf + i, 4); /* unaligned load is allowed */ + uint32_t x = w ^ A; /* identify bytes equal to first marker byte */ + uint32_t m = (~x & (x - ONE)) & HIGH; /* mask has high bit set for candidate bytes */ + while (m) { /* handle only candidates to avoid unnecessary memcmp calls */ + unsigned off = __builtin_ctz(m) >> 3; + uint32_t pos = i + off; + if (pos + JPEG_EOI_MARKER_LEN <= length && + memcmp(inbuf + pos, pat, JPEG_EOI_MARKER_LEN) == 0) { + return pos; + } + m &= m - 1; /* clear processed candidate */ + } + i += 4; + } + for (; i + JPEG_EOI_MARKER_LEN <= length; i++) { + if (memcmp(inbuf + i, pat, JPEG_EOI_MARKER_LEN) == 0) { 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; + const uint8_t *dptr = inbuf + length - JPEG_EOI_MARKER_LEN; + while (dptr >= inbuf) { + if (memcmp(dptr, JPEG_EOI_BYTES, JPEG_EOI_MARKER_LEN) == 0) { + return dptr - inbuf; + } + if (dptr == inbuf) { + break; } dptr--; } @@ -112,7 +252,13 @@ void IRAM_ATTR ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType if (xQueueSendFromISR(cam->event_queue, (void *)&cam_event, HPTaskAwoken) != pdTRUE) { ll_cam_stop(cam); cam->state = CAM_STATE_IDLE; - ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: EV-%s-OVF\r\n"), cam_event==CAM_IN_SUC_EOF_EVENT ? DRAM_STR("EOF") : DRAM_STR("VSYNC")); +#if CAM_LOG_SPAM_EVERY_FRAME + ESP_DRAM_LOGD(TAG, "EV-%s-OVF", cam_event==CAM_IN_SUC_EOF_EVENT ? "EOF" : "VSYNC"); +#else + static uint16_t ovf_cnt = 0; + CAM_WARN_THROTTLE(ovf_cnt, + cam_event==CAM_IN_SUC_EOF_EVENT ? "EV-EOF-OVF" : "EV-VSYNC-OVF"); +#endif } } @@ -150,21 +296,74 @@ static void cam_task(void *arg) 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"); + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: FB-OVF\r\n")); 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); + } else { + // stop if the next DMA copy would exceed the framebuffer slot + // size, since we're called only after the copy occurs + // This effectively reduces maximum usable frame buffer size + // by one DMA operation, as we can't predict here, if the next + // cam event will be a VSYNC + if (cnt + 1 >= cam_obj->frame_copy_cnt) { + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: DMA overflow\r\n")); + ll_cam_stop(cam_obj); + cam_obj->state = CAM_STATE_IDLE; + continue; + } } + //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; + if (cam_obj->jpeg_mode && cnt == 0) { + if (cam_obj->psram_mode) { + /* dma_half_buffer_size already in BYTES (see ll_cam_memcpy()) */ + size_t probe_len = cam_obj->dma_half_buffer_size; + /* clamp to avoid copying past the end of soi_probe */ + if (probe_len > CAM_SOI_PROBE_BYTES) { + probe_len = CAM_SOI_PROBE_BYTES; + } + /* Invalidate cache lines for the DMA buffer before probing */ + cam_drop_psram_cache(frame_buffer_event->buf, probe_len); + + uint8_t soi_probe[CAM_SOI_PROBE_BYTES]; + memcpy(soi_probe, frame_buffer_event->buf, probe_len); + int soi_off = cam_verify_jpeg_soi(soi_probe, probe_len); + if (soi_off != 0) { + static uint16_t warn_psram_soi_cnt = 0; + if (soi_off > 0) { + CAM_WARN_THROTTLE(warn_psram_soi_cnt, + "NO-SOI - JPEG start marker not at pos 0 (PSRAM)"); + } else { + CAM_WARN_THROTTLE(warn_psram_soi_cnt, + "NO-SOI - JPEG start marker missing (PSRAM)"); + } + ll_cam_stop(cam_obj); + cam_obj->state = CAM_STATE_IDLE; + continue; + } + } else { + int soi_off = cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len); + if (soi_off != 0) { + static uint16_t warn_soi_bad_cnt = 0; + if (soi_off > 0) { + CAM_WARN_THROTTLE(warn_soi_bad_cnt, + "NO-SOI - JPEG start marker not at pos 0"); + } else { + CAM_WARN_THROTTLE(warn_soi_bad_cnt, + "NO-SOI - JPEG start marker missing"); + } + ll_cam_stop(cam_obj); + cam_obj->state = CAM_STATE_IDLE; + continue; + } + } } + cnt++; } else if (cam_event == CAM_VSYNC_EVENT) { @@ -175,7 +374,7 @@ static void cam_task(void *arg) 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"); + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: FB-OVF\r\n")); cnt--; } else { frame_buffer_event->len += ll_cam_memcpy(cam_obj, @@ -198,7 +397,7 @@ static void cam_task(void *arg) } 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, (unsigned) cam_obj->fb_size); + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: FB-SIZE: %u != %u\r\n"), frame_buffer_event->len, (unsigned) cam_obj->fb_size); } } //send frame @@ -209,14 +408,14 @@ static void cam_task(void *arg) //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"); + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: FBQ-SND\r\n")); } //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"); + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: FBQ-RCV\r\n")); } } } @@ -263,6 +462,9 @@ static esp_err_t cam_dma_config(const camera_config_t *config) 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 + if (cam_obj->psram_mode) { + cam_obj->frame_copy_cnt++; + } ESP_LOGI(TAG, "buffer_size: %d, half_buffer_size: %d, node_buffer_size: %d, node_cnt: %d, total_cnt: %d", (int) cam_obj->dma_buffer_size, (int) cam_obj->dma_half_buffer_size, (int) cam_obj->dma_node_buffer_size, @@ -271,7 +473,7 @@ static esp_err_t cam_dma_config(const camera_config_t *config) 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_obj->frames = (cam_frame_t *)heap_caps_aligned_calloc(alignof(cam_frame_t), 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; @@ -281,6 +483,7 @@ static esp_err_t cam_dma_config(const camera_config_t *config) if (cam_obj->fb_size < cam_obj->recv_size) { fb_size = cam_obj->recv_size; } + fb_size += cam_obj->dma_half_buffer_size; } /* Allocate memory for frame buffer */ @@ -367,19 +570,25 @@ esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint 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_CHECK_GOTO(ret == ESP_OK, "ll_cam_set_sample_mode failed", err); + cam_obj->jpeg_mode = config->pixel_format == PIXFORMAT_JPEG; -#if CONFIG_IDF_TARGET_ESP32 - cam_obj->psram_mode = false; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + cam_obj->psram_mode = g_psram_dma_mode; #else - cam_obj->psram_mode = (config->xclk_freq_hz == 16000000); + cam_obj->psram_mode = false; #endif + ESP_LOGI(TAG, "PSRAM DMA mode %s", cam_obj->psram_mode ? "enabled" : "disabled"); 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){ +#ifdef CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_AUTO cam_obj->recv_size = cam_obj->width * cam_obj->height / 5; +#else + cam_obj->recv_size = CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE; +#endif 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; @@ -389,7 +598,11 @@ esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint 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)); + size_t queue_size = cam_obj->dma_half_buffer_cnt - 1; + if (queue_size == 0) { + queue_size = 1; + } + cam_obj->event_queue = xQueueCreate(queue_size, 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; @@ -435,6 +648,9 @@ esp_err_t cam_deinit(void) if (cam_obj->frame_buffer_queue) { vQueueDelete(cam_obj->frame_buffer_queue); } + + ll_cam_deinit(cam_obj); + if (cam_obj->dma) { free(cam_obj->dma); } @@ -451,8 +667,6 @@ esp_err_t cam_deinit(void) free(cam_obj->frames); } - ll_cam_deinit(cam_obj); - free(cam_obj); cam_obj = NULL; return ESP_OK; @@ -472,30 +686,103 @@ void cam_start(void) 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); + const TickType_t start = xTaskGetTickCount(); +#if CONFIG_IDF_TARGET_ESP32S3 + uint16_t dma_reset_counter = 0; + static const uint8_t MAX_GDMA_RESETS = 3; +#else + /* throttle repeated NULL frame warnings */ + static uint16_t warn_null_cnt = 0; +#endif + /* throttle repeated NO-EOI warnings */ + static uint16_t warn_eoi_miss_cnt = 0; + + for (;;) + { + TickType_t elapsed = xTaskGetTickCount() - start; /* TickType_t is unsigned so rollover is safe */ + if (elapsed >= timeout) { + ESP_LOGW(TAG, "Failed to get frame: timeout"); + return NULL; + } + TickType_t remaining = timeout - elapsed; + + if (xQueueReceive(cam_obj->frame_buffer_queue, (void *)&dma_buffer, remaining) == pdFALSE) { + continue; + } + + if (!dma_buffer) { + /* Work-around for ESP32-S3 GDMA freeze when Wi-Fi STA starts. + * See esp32-camera commit 984999f (issue #620). */ +#if CONFIG_IDF_TARGET_ESP32S3 + if (dma_reset_counter < MAX_GDMA_RESETS) { + ll_cam_dma_reset(cam_obj); + dma_reset_counter++; + continue; /* retry with queue timeout */ + } + if (dma_reset_counter == MAX_GDMA_RESETS) { + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: Giving up GDMA reset after %u tries\r\n"), + (unsigned) dma_reset_counter); + dma_reset_counter++; /* suppress further logs */ + } +#else + /* Early warning for misbehaving sensors on other chips */ + CAM_WARN_THROTTLE(warn_null_cnt, + "Unexpected NULL frame on " CONFIG_IDF_TARGET); +#endif + vTaskDelay(1); /* immediate yield once resets are done */ + continue; /* go to top of loop */ + } + + if (cam_obj->jpeg_mode) { + /* find the end marker for JPEG. Data after that can be discarded */ + int offset_e = -1; + if (cam_obj->psram_mode) { + /* Search forward from (JPEG_EOI_MARKER_LEN - 1) bytes before the final + * DMA block. We prefer forward search to pick the earliest EOI in the + * last DMA node, avoiding stale markers from a larger prior frame. */ + size_t probe_len = eoi_probe_window(cam_obj->dma_node_buffer_size, + dma_buffer->len); + if (probe_len < JPEG_EOI_MARKER_LEN) { + goto skip_eoi_check; + } + uint8_t *probe_start = dma_buffer->buf + dma_buffer->len - probe_len; + cam_drop_psram_cache(probe_start, probe_len); + int off = cam_verify_jpeg_eoi(probe_start, probe_len, true); + if (off >= 0) { + offset_e = dma_buffer->len - probe_len + off; + } + } else { + offset_e = cam_verify_jpeg_eoi(dma_buffer->buf, dma_buffer->len, false); + } + if (offset_e >= 0) { - // adjust buffer length - dma_buffer->len = offset_e + sizeof(JPEG_EOI_MARKER); + dma_buffer->len = offset_e + JPEG_EOI_MARKER_LEN; + if (cam_obj->psram_mode) { + /* DMA may bypass cache, ensure full frame is visible */ + cam_drop_psram_cache(dma_buffer->buf, dma_buffer->len); + } 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 + +skip_eoi_check: + + CAM_WARN_THROTTLE(warn_eoi_miss_cnt, + "NO-EOI - JPEG end marker missing"); + cam_give(dma_buffer); + continue; /* wait for another frame */ + } else if (cam_obj->psram_mode && + cam_obj->in_bytes_per_pixel != cam_obj->fb_bytes_per_pixel) { + /* currently used only for YUV to GRAYSCALE */ dma_buffer->len = ll_cam_memcpy(cam_obj, dma_buffer->buf, dma_buffer->buf, dma_buffer->len); } + + if (cam_obj->psram_mode) { + /* DMA may bypass cache, ensure full frame is visible to the app */ + cam_drop_psram_cache(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) @@ -507,3 +794,26 @@ void cam_give(camera_fb_t *dma_buffer) } } } + +void cam_give_all(void) { + for (int x = 0; x < cam_obj->frame_cnt; x++) { + cam_obj->frames[x].en = 1; + } +} + +bool cam_get_available_frames(void) +{ + return 0 < uxQueueMessagesWaiting(cam_obj->frame_buffer_queue); +} + +void cam_set_psram_mode(bool enable) +{ + portENTER_CRITICAL(&g_psram_dma_lock); + g_psram_dma_mode = enable; + portEXIT_CRITICAL(&g_psram_dma_lock); +} + +bool cam_get_psram_mode(void) +{ + return g_psram_dma_mode; +} diff --git a/driver/esp_camera.c b/driver/esp_camera.c index a71b7e1722..4dc8f5a443 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -69,6 +69,15 @@ #if CONFIG_SC031GS_SUPPORT #include "sc031gs.h" #endif +#if CONFIG_MEGA_CCM_SUPPORT +#include "mega_ccm.h" +#endif +#if CONFIG_HM1055_SUPPORT +#include "hm1055.h" +#endif +#if CONFIG_HM0360_SUPPORT +#include "hm0360.h" +#endif #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" @@ -86,6 +95,7 @@ typedef struct { static const char *CAMERA_SENSOR_NVS_KEY = "sensor"; static const char *CAMERA_PIXFORMAT_NVS_KEY = "pixformat"; static camera_state_t *s_state = NULL; +static camera_config_t s_saved_config; #if CONFIG_IDF_TARGET_ESP32S3 // LCD_CAM module of ESP32-S3 will generate xclk #define CAMERA_ENABLE_OUT_CLOCK(v) @@ -102,46 +112,55 @@ typedef struct { static const sensor_func_t g_sensors[] = { #if CONFIG_OV7725_SUPPORT - {ov7725_detect, ov7725_init}, + {esp32_camera_ov7725_detect, esp32_camera_ov7725_init}, #endif #if CONFIG_OV7670_SUPPORT - {ov7670_detect, ov7670_init}, + {esp32_camera_ov7670_detect, esp32_camera_ov7670_init}, #endif #if CONFIG_OV2640_SUPPORT - {ov2640_detect, ov2640_init}, + {esp32_camera_ov2640_detect, esp32_camera_ov2640_init}, #endif #if CONFIG_OV3660_SUPPORT - {ov3660_detect, ov3660_init}, + {esp32_camera_ov3660_detect, esp32_camera_ov3660_init}, #endif #if CONFIG_OV5640_SUPPORT - {ov5640_detect, ov5640_init}, + {esp32_camera_ov5640_detect, esp32_camera_ov5640_init}, #endif #if CONFIG_NT99141_SUPPORT - {nt99141_detect, nt99141_init}, + {esp32_camera_nt99141_detect, esp32_camera_nt99141_init}, #endif #if CONFIG_GC2145_SUPPORT - {gc2145_detect, gc2145_init}, + {esp32_camera_gc2145_detect, esp32_camera_gc2145_init}, #endif #if CONFIG_GC032A_SUPPORT - {gc032a_detect, gc032a_init}, + {esp32_camera_gc032a_detect, esp32_camera_gc032a_init}, #endif #if CONFIG_GC0308_SUPPORT - {gc0308_detect, gc0308_init}, + {esp32_camera_gc0308_detect, esp32_camera_gc0308_init}, #endif #if CONFIG_BF3005_SUPPORT - {bf3005_detect, bf3005_init}, + {esp32_camera_bf3005_detect, esp32_camera_bf3005_init}, #endif #if CONFIG_BF20A6_SUPPORT - {bf20a6_detect, bf20a6_init}, + {esp32_camera_bf20a6_detect, esp32_camera_bf20a6_init}, #endif #if CONFIG_SC101IOT_SUPPORT - {sc101iot_detect, sc101iot_init}, + {esp32_camera_sc101iot_detect, esp32_camera_sc101iot_init}, #endif #if CONFIG_SC030IOT_SUPPORT - {sc030iot_detect, sc030iot_init}, + {esp32_camera_sc030iot_detect, esp32_camera_sc030iot_init}, #endif #if CONFIG_SC031GS_SUPPORT - {sc031gs_detect, sc031gs_init}, + {esp32_camera_sc031gs_detect, esp32_camera_sc031gs_init}, +#endif +#if CONFIG_MEGA_CCM_SUPPORT + {esp32_camera_mega_ccm_detect, esp32_camera_mega_ccm_init}, +#endif +#if CONFIG_HM1055_SUPPORT + {esp32_camera_hm1055_detect, esp32_camera_hm1055_init}, +#endif +#if CONFIG_HM0360_SUPPORT + {esp32_camera_hm0360_detect, esp32_camera_hm0360_init}, #endif }; @@ -153,7 +172,7 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out return ESP_ERR_INVALID_STATE; } - s_state = (camera_state_t *) calloc(sizeof(camera_state_t), 1); + s_state = (camera_state_t *) calloc(1, sizeof(camera_state_t)); if (!s_state) { return ESP_ERR_NO_MEM; } @@ -206,30 +225,39 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out ESP_LOGD(TAG, "Searching for camera address"); vTaskDelay(10 / portTICK_PERIOD_MS); - uint8_t slv_addr = SCCB_Probe(); - - if (slv_addr == 0) { - ret = ESP_ERR_NOT_FOUND; - goto err; - } - - 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; + int camera_model_id; + uint8_t slv_addr = 0x0; /** - * 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 + * This loop probes each known sensor until a supported camera is detected */ - 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; + for(camera_model_id = 0; *out_camera_model == CAMERA_NONE && camera_model_id < CAMERA_MODEL_MAX ; camera_model_id++) { + slv_addr = camera_sensor[camera_model_id].sccb_addr; + + if (ESP_OK != SCCB_Probe(slv_addr)) { + continue; + } + + 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)) { + ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x", + id->PID, id->VER, id->MIDH, id->MIDL); + 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; + } } } } @@ -240,12 +268,11 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out goto err; } - 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_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr); ESP_LOGD(TAG, "Doing SW reset of sensor"); vTaskDelay(10 / portTICK_PERIOD_MS); - + return s_state->sensor.reset(&s_state->sensor); err : CAMERA_DISABLE_OUT_CLOCK(); @@ -272,6 +299,7 @@ static pixformat_t get_output_data_format(camera_conv_mode_t conv_mode) esp_err_t esp_camera_init(const camera_config_t *config) { esp_err_t err; + s_saved_config = *config; err = cam_init(config); if (err != ESP_OK) { ESP_LOGE(TAG, "Camera init failed with error 0x%x", err); @@ -476,3 +504,47 @@ esp_err_t esp_camera_load_from_nvs(const char *key) return ret; } } + +void esp_camera_return_all(void) { + if (s_state == NULL) { + return; + } + cam_give_all(); +} + +bool esp_camera_available_frames(void) +{ + if (s_state == NULL) { + return false; + } + return cam_get_available_frames(); +} + +esp_err_t esp_camera_reconfigure(const camera_config_t *config) +{ + if (!config) { + return ESP_ERR_INVALID_ARG; + } + if (s_state) { + esp_err_t err = esp_camera_deinit(); + if (err != ESP_OK) { + return err; + } + } + s_saved_config = *config; + return esp_camera_init(&s_saved_config); +} + +esp_err_t esp_camera_set_psram_mode(bool enable) +{ + cam_set_psram_mode(enable); + if (!s_state) { + return ESP_ERR_INVALID_STATE; + } + return esp_camera_reconfigure(&s_saved_config); +} + +bool esp_camera_get_psram_mode(void) +{ + return cam_get_psram_mode(); +} diff --git a/driver/include/esp_camera.h b/driver/include/esp_camera.h index ee84b307ba..fa2f24cda9 100755 --- a/driver/include/esp_camera.h +++ b/driver/include/esp_camera.h @@ -72,6 +72,12 @@ #include "sys/time.h" #include "sdkconfig.h" +/** + * @brief define for if chip supports camera + */ +#define ESP_CAMERA_SUPPORTED (CONFIG_IDF_TARGET_ESP32 | CONFIG_IDF_TARGET_ESP32S3 | \ + CONFIG_IDF_TARGET_ESP32S2) + #ifdef __cplusplus extern "C" { #endif @@ -85,7 +91,7 @@ typedef enum { } camera_grab_mode_t; /** - * @brief Camera frame buffer location + * @brief Camera frame buffer location */ typedef enum { CAMERA_FB_IN_PSRAM, /*!< Frame buffer is placed in external PSRAM */ @@ -99,7 +105,7 @@ typedef enum { typedef enum { CONV_DISABLE, RGB565_TO_YUV422, - + YUV422_TO_RGB565, YUV422_TO_YUV420 } camera_conv_mode_t; @@ -132,7 +138,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. EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode */ + int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. */ 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 */ @@ -172,8 +178,6 @@ typedef struct { /** * @brief Initialize the camera driver * - * @note call camera_probe before calling this function - * * This function detects and configures camera over I2C interface, * allocates framebuffer and DMA buffers, * initializes parallel I2S input, and sets up DMA descriptors. @@ -194,14 +198,14 @@ esp_err_t esp_camera_init(const camera_config_t* config); * - ESP_OK on success * - ESP_ERR_INVALID_STATE if the driver hasn't been initialized yet */ -esp_err_t esp_camera_deinit(); +esp_err_t esp_camera_deinit(void); /** * @brief Obtain pointer to a frame buffer. * * @return pointer to the frame buffer */ -camera_fb_t* esp_camera_fb_get(); +camera_fb_t* esp_camera_fb_get(void); /** * @brief Return the frame buffer to be reused again. @@ -215,22 +219,62 @@ void esp_camera_fb_return(camera_fb_t * fb); * * @return pointer to the sensor */ -sensor_t * esp_camera_sensor_get(); +sensor_t * esp_camera_sensor_get(void); /** * @brief Save camera settings to non-volatile-storage (NVS) - * - * @param key A unique nvs key name for the camera settings + * + * @param key A unique nvs key name for the camera settings */ esp_err_t esp_camera_save_to_nvs(const char *key); /** * @brief Load camera settings from non-volatile-storage (NVS) - * - * @param key A unique nvs key name for the camera settings + * + * @param key A unique nvs key name for the camera settings */ esp_err_t esp_camera_load_from_nvs(const char *key); +/** + * @brief Return all frame buffers to be reused again. + */ +void esp_camera_return_all(void); + +/** + * @brief Check if there are available frames to be immediately acquired + */ +bool esp_camera_available_frames(void); + +/** + * @brief Enable or disable PSRAM DMA mode at runtime. + * + * @param enable True to enable PSRAM DMA mode, false to disable it. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if the camera is not initialized + * - Propagated error from reinitialization on failure + */ +esp_err_t esp_camera_set_psram_mode(bool enable); + +/** + * @brief Reinitialize the camera with a new configuration. + * + * @param config Updated camera configuration structure + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if config is NULL + * - Propagated error from deinit or init if they fail + */ +esp_err_t esp_camera_reconfigure(const camera_config_t *config); + +/** + * @brief Get current PSRAM DMA mode state. + * + * @return True if PSRAM DMA is enabled, false otherwise. + */ +bool esp_camera_get_psram_mode(void); + + #ifdef __cplusplus } #endif diff --git a/driver/include/sensor.h b/driver/include/sensor.h index 6ab12a21aa..eb02c7a366 100755 --- a/driver/include/sensor.h +++ b/driver/include/sensor.h @@ -31,6 +31,9 @@ typedef enum { SC101IOT_PID = 0xda4a, SC030IOT_PID = 0x9a46, SC031GS_PID = 0x0031, + MEGA_CCM_PID =0x039E, + HM1055_PID = 0x0955, + HM0360_PID = 0x0360 } camera_pid_t; typedef enum { @@ -48,6 +51,9 @@ typedef enum { CAMERA_SC101IOT, CAMERA_SC030IOT, CAMERA_SC031GS, + CAMERA_MEGA_CCM, + CAMERA_HM1055, + CAMERA_HM0360, CAMERA_MODEL_MAX, CAMERA_NONE, } camera_model_t; @@ -67,6 +73,9 @@ typedef enum { SC101IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1 SC030IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1 SC031GS_SCCB_ADDR = 0x30, + MEGA_CCM_SCCB_ADDR = 0x1F, // 0x3E >> 1 + HM1055_SCCB_ADDR = 0x24, + HM0360_SCCB_ADDR = 0x12, } camera_sccb_addr_t; typedef enum { @@ -79,15 +88,18 @@ typedef enum { PIXFORMAT_RAW, // RAW PIXFORMAT_RGB444, // 3BP2P/RGB444 PIXFORMAT_RGB555, // 3BP2P/RGB555 + PIXFORMAT_RAW8, // RAW 8-bit } pixformat_t; typedef enum { FRAMESIZE_96X96, // 96x96 FRAMESIZE_QQVGA, // 160x120 + FRAMESIZE_128X128, // 128x128 FRAMESIZE_QCIF, // 176x144 FRAMESIZE_HQVGA, // 240x176 FRAMESIZE_240X240, // 240x240 FRAMESIZE_QVGA, // 320x240 + FRAMESIZE_320X320, // 320x320 FRAMESIZE_CIF, // 400x296 FRAMESIZE_HVGA, // 480x320 FRAMESIZE_VGA, // 640x480 @@ -106,6 +118,7 @@ typedef enum { FRAMESIZE_WQXGA, // 2560x1600 FRAMESIZE_P_FHD, // 1080x1920 FRAMESIZE_QSXGA, // 2560x1920 + FRAMESIZE_5MP, // 2592x1944 FRAMESIZE_INVALID } framesize_t; diff --git a/driver/private_include/cam_hal.h b/driver/private_include/cam_hal.h index c8e38ed476..8751c53e2c 100644 --- a/driver/private_include/cam_hal.h +++ b/driver/private_include/cam_hal.h @@ -55,6 +55,13 @@ camera_fb_t *cam_take(TickType_t timeout); void cam_give(camera_fb_t *dma_buffer); +void cam_give_all(void); + +bool cam_get_available_frames(void); + +void cam_set_psram_mode(bool enable); +bool cam_get_psram_mode(void); + #ifdef __cplusplus } #endif diff --git a/driver/private_include/sccb.h b/driver/private_include/sccb.h index 9e4453386a..c8613da124 100755 --- a/driver/private_include/sccb.h +++ b/driver/private_include/sccb.h @@ -12,9 +12,11 @@ int SCCB_Init(int pin_sda, int pin_scl); int SCCB_Use_Port(int sccb_i2c_port); int SCCB_Deinit(void); -uint8_t SCCB_Probe(); +int SCCB_Probe(uint8_t slv_addr); 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); +int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data); uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg); -uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data); +int SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data); +uint16_t SCCB_Read_Addr16_Val16(uint8_t slv_addr, uint16_t reg); +int SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data); #endif // __SCCB_H__ diff --git a/driver/private_include/xclk.h b/driver/private_include/xclk.h index 3d721a6132..922cd7029d 100755 --- a/driver/private_include/xclk.h +++ b/driver/private_include/xclk.h @@ -1,9 +1,10 @@ #pragma once #include "esp_system.h" +#include "esp_camera.h" esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz); -esp_err_t camera_enable_out_clock(); +esp_err_t camera_enable_out_clock(const camera_config_t *config); -void camera_disable_out_clock(); +void camera_disable_out_clock(void); diff --git a/driver/sccb-ng.c b/driver/sccb-ng.c new file mode 100755 index 0000000000..ddf5746101 --- /dev/null +++ b/driver/sccb-ng.c @@ -0,0 +1,334 @@ +/* + * This file is part of the OpenMV project. + * Copyright (c) 2013/2014 Ibrahim Abdelkader + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * SCCB (I2C like) driver with the new esp-idf I2C API. + * + */ +#include +#include +#include +#include +#include "sccb.h" +#include "sensor.h" +#include +#include "sdkconfig.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 = "sccb-ng"; +#endif + +#define LITTLETOBIG(x) ((x << 8) | (x >> 8)) + +#include "esp_private/i2c_platform.h" +#include "driver/i2c_master.h" +#include "driver/i2c_types.h" + +// support IDF 5.x +#ifndef portTICK_RATE_MS +#define portTICK_RATE_MS portTICK_PERIOD_MS +#endif + +#define TIMEOUT_MS 1000 /*!< I2C timeout duration */ +#define SCCB_FREQ CONFIG_SCCB_CLK_FREQ /*!< I2C master frequency */ +#if CONFIG_SCCB_HARDWARE_I2C_PORT1 +const int SCCB_I2C_PORT_DEFAULT = 1; +#else +const int SCCB_I2C_PORT_DEFAULT = 0; +#endif + +#define MAX_DEVICES UINT8_MAX-1 + +/* + The legacy I2C driver used addresses to differentiate between devices, whereas the new driver uses + i2c_master_dev_handle_t structs which are registed to the bus. + To avoid re-writing all camera dependant code, we simply translate the devices address to the corresponding + device_handle. This keeps all interfaces to the drivers identical. + To perform this conversion the following local struct is used. +*/ +typedef struct +{ + i2c_master_dev_handle_t dev_handle; + uint16_t address; +} device_t; + +static device_t devices[MAX_DEVICES]; +static uint8_t device_count = 0; +static int sccb_i2c_port; +static bool sccb_owns_i2c_port; + +i2c_master_dev_handle_t *get_handle_from_address(uint8_t slv_addr) +{ + for (uint8_t i = 0; i < device_count; i++) + { + + if (slv_addr == devices[i].address) + { + return &(devices[i].dev_handle); + } + } + + ESP_LOGE(TAG, "Device with address %02x not found", slv_addr); + return NULL; +} + +int SCCB_Install_Device(uint8_t slv_addr) +{ + esp_err_t ret; + i2c_master_bus_handle_t bus_handle; + + if (device_count >= MAX_DEVICES) + { + ESP_LOGE(TAG, "cannot add more than %d devices", MAX_DEVICES); + return ESP_FAIL; + } + + ret = i2c_master_get_bus_handle(sccb_i2c_port, &bus_handle); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to get SCCB I2C Bus handle for port %d", sccb_i2c_port); + return ret; + } + + i2c_device_config_t dev_cfg = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = slv_addr, // not yet set + .scl_speed_hz = SCCB_FREQ, + }; + + ret = i2c_master_bus_add_device(bus_handle, &dev_cfg, &(devices[device_count].dev_handle)); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to install SCCB I2C device: %s", esp_err_to_name(ret)); + return -1; + } + + devices[device_count].address = slv_addr; + device_count++; + return 0; +} + +int SCCB_Init(int pin_sda, int pin_scl) +{ + ESP_LOGI(TAG, "pin_sda %d pin_scl %d", pin_sda, pin_scl); + // i2c_config_t conf; + esp_err_t ret; + + sccb_i2c_port = SCCB_I2C_PORT_DEFAULT; + sccb_owns_i2c_port = true; + ESP_LOGI(TAG, "sccb_i2c_port=%d", sccb_i2c_port); + + i2c_master_bus_config_t i2c_mst_config = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .i2c_port = SCCB_I2C_PORT_DEFAULT, + .scl_io_num = pin_scl, + .sda_io_num = pin_sda, + .glitch_ignore_cnt = 7, + .flags.enable_internal_pullup = 1}; + + i2c_master_bus_handle_t bus_handle; + ret = i2c_new_master_bus(&i2c_mst_config, &bus_handle); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to install SCCB I2C master bus on port %d: %s", sccb_i2c_port, esp_err_to_name(ret)); + return ret; + } + + return ESP_OK; +} + +int SCCB_Use_Port(int i2c_num) +{ // sccb use an already initialized I2C port + if (sccb_owns_i2c_port) + { + SCCB_Deinit(); + } + if (i2c_num < 0 || i2c_num > I2C_NUM_MAX) + { + return ESP_ERR_INVALID_ARG; + } + sccb_i2c_port = i2c_num; + + return ESP_OK; +} + +int SCCB_Deinit(void) +{ + esp_err_t ret; + + for (uint8_t i = 0; i < device_count; i++) + { + ret = i2c_master_bus_rm_device(devices[i].dev_handle); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to remove SCCB I2C Device"); + return ret; + } + + devices[i].dev_handle = NULL; + devices[i].address = 0; + } + device_count = 0; + + if (!sccb_owns_i2c_port) + { + return ESP_OK; + } + sccb_owns_i2c_port = false; + + i2c_master_bus_handle_t bus_handle; + ret = i2c_master_get_bus_handle(sccb_i2c_port, &bus_handle); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to get SCCB I2C Bus handle for port %d", sccb_i2c_port); + return ret; + } + + ret = i2c_del_master_bus(bus_handle); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to get delete SCCB I2C Master Bus at port %d", sccb_i2c_port); + return ret; + } + + return ESP_OK; +} + +int SCCB_Probe(uint8_t slv_addr) +{ + esp_err_t ret; + i2c_master_bus_handle_t bus_handle; + + ret = i2c_master_get_bus_handle(sccb_i2c_port, &bus_handle); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to get SCCB I2C Bus handle for port %d", sccb_i2c_port); + return ret; + } + + ret = i2c_master_probe(bus_handle, slv_addr, TIMEOUT_MS); + + if (ret == ESP_OK) + { + return SCCB_Install_Device(slv_addr); + } + + return ret; +} + +uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint8_t tx_buffer[1]; + uint8_t rx_buffer[1]; + + tx_buffer[0] = reg; + + esp_err_t ret = i2c_master_transmit_receive(dev_handle, tx_buffer, 1, rx_buffer, 1, TIMEOUT_MS); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, rx_buffer[0], ret); + } + + return rx_buffer[0]; +} + +int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint8_t tx_buffer[2]; + tx_buffer[0] = reg; + tx_buffer[1] = data; + + esp_err_t ret = i2c_master_transmit(dev_handle, tx_buffer, 2, TIMEOUT_MS); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret); + } + + return ret == ESP_OK ? 0 : -1; +} + +uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint8_t rx_buffer[1]; + + uint16_t reg_htons = LITTLETOBIG(reg); + uint8_t *reg_u8 = (uint8_t *)®_htons; + + esp_err_t ret = i2c_master_transmit_receive(dev_handle, reg_u8, 2, rx_buffer, 1, TIMEOUT_MS); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, rx_buffer[0]); + } + + return rx_buffer[0]; +} + +int SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint8_t tx_buffer[3]; + tx_buffer[0] = reg >> 8; + tx_buffer[1] = reg & 0x00ff; + tx_buffer[2] = data; + + esp_err_t ret = i2c_master_transmit(dev_handle, tx_buffer, 3, TIMEOUT_MS); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data); + } + return ret == ESP_OK ? 0 : -1; +} + +uint16_t SCCB_Read_Addr16_Val16(uint8_t slv_addr, uint16_t reg) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint8_t rx_buffer[2]; + + uint16_t reg_htons = LITTLETOBIG(reg); + uint8_t *reg_u8 = (uint8_t *)®_htons; + + esp_err_t ret = i2c_master_transmit_receive(dev_handle, reg_u8, 2, rx_buffer, 2, TIMEOUT_MS); + uint16_t data = ((uint16_t)rx_buffer[0] << 8) | (uint16_t)rx_buffer[1]; + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data); + } + + return data; +} + +int SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint8_t tx_buffer[4]; + tx_buffer[0] = reg >> 8; + tx_buffer[1] = reg & 0x00ff; + tx_buffer[2] = data >> 8; + tx_buffer[3] = data & 0x00ff; + + esp_err_t ret = i2c_master_transmit(dev_handle, tx_buffer, 4, TIMEOUT_MS); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data); + } + return ret == ESP_OK ? 0 : -1; +} diff --git a/driver/sccb.c b/driver/sccb.c index 945bd9bdec..63bddac957 100755 --- a/driver/sccb.c +++ b/driver/sccb.c @@ -6,6 +6,7 @@ * SCCB (I2C like) driver. * */ + #include #include #include @@ -56,7 +57,7 @@ int SCCB_Init(int pin_sda, int pin_scl) sccb_i2c_port = SCCB_I2C_PORT_DEFAULT; sccb_owns_i2c_port = true; - ESP_LOGI(TAG, "sccb_i2c_port=%d\n", sccb_i2c_port); + ESP_LOGI(TAG, "sccb_i2c_port=%d", sccb_i2c_port); conf.mode = I2C_MODE_MASTER; conf.sda_io_num = pin_sda; @@ -92,26 +93,15 @@ int SCCB_Deinit(void) return i2c_driver_delete(sccb_i2c_port); } -uint8_t SCCB_Probe(void) +int SCCB_Probe(uint8_t slv_addr) { - uint8_t slave_addr = 0x0; - - 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); - 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) { - return slave_addr; - } - } - return 0; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( slv_addr << 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); + return ret; } uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) @@ -139,7 +129,7 @@ uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) return data; } -uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data) +int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data) { esp_err_t ret = ESP_FAIL; i2c_cmd_handle_t cmd = i2c_cmd_link_create(); @@ -184,7 +174,7 @@ uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg) return data; } -uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) +int SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) { static uint16_t i = 0; esp_err_t ret = ESP_FAIL; @@ -204,3 +194,57 @@ uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) } return ret == ESP_OK ? 0 : -1; } + +uint16_t SCCB_Read_Addr16_Val16(uint8_t slv_addr, uint16_t reg) +{ + uint16_t data = 0; + uint8_t *data_u8 = (uint8_t *)&data; + esp_err_t ret = ESP_FAIL; + uint16_t reg_htons = LITTLETOBIG(reg); + uint8_t *reg_u8 = (uint8_t *)®_htons; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if(ret != ESP_OK) return -1; + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN); + i2c_master_read_byte(cmd, &data_u8[1], ACK_VAL); + i2c_master_read_byte(cmd, &data_u8[0], NACK_VAL); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "W [%04x]=%04x fail\n", reg, data); + } + return data; +} + +int SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data) +{ + esp_err_t ret = ESP_FAIL; + uint16_t reg_htons = LITTLETOBIG(reg); + uint8_t *reg_u8 = (uint8_t *)®_htons; + uint16_t data_htons = LITTLETOBIG(data); + uint8_t *data_u8 = (uint8_t *)&data_htons; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN); + i2c_master_write_byte(cmd, data_u8[0], ACK_CHECK_EN); + i2c_master_write_byte(cmd, data_u8[1], ACK_CHECK_EN); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "W [%04x]=%04x fail\n", reg, data); + } + return ret == ESP_OK ? 0 : -1; +} diff --git a/driver/sensor.c b/driver/sensor.c index 23071f6c2c..453a15fca2 100644 --- a/driver/sensor.c +++ b/driver/sensor.c @@ -17,15 +17,20 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = { {CAMERA_SC101IOT, "SC101IOT", SC101IOT_SCCB_ADDR, SC101IOT_PID, FRAMESIZE_HD, false}, {CAMERA_SC030IOT, "SC030IOT", SC030IOT_SCCB_ADDR, SC030IOT_PID, FRAMESIZE_VGA, false}, {CAMERA_SC031GS, "SC031GS", SC031GS_SCCB_ADDR, SC031GS_PID, FRAMESIZE_VGA, false}, + {CAMERA_MEGA_CCM, "MEGA_CCM", MEGA_CCM_SCCB_ADDR, MEGA_CCM_PID, FRAMESIZE_5MP, true}, + {CAMERA_HM1055, "HM1055", HM1055_SCCB_ADDR, HM1055_PID, FRAMESIZE_HD, false}, + {CAMERA_HM0360, "HM0360", HM0360_SCCB_ADDR, HM0360_PID, FRAMESIZE_VGA, false}, }; const resolution_info_t resolution[FRAMESIZE_INVALID] = { { 96, 96, ASPECT_RATIO_1X1 }, /* 96x96 */ { 160, 120, ASPECT_RATIO_4X3 }, /* QQVGA */ + { 128, 128, ASPECT_RATIO_1X1 }, /* 128x128 */ { 176, 144, ASPECT_RATIO_5X4 }, /* QCIF */ { 240, 176, ASPECT_RATIO_4X3 }, /* HQVGA */ { 240, 240, ASPECT_RATIO_1X1 }, /* 240x240 */ { 320, 240, ASPECT_RATIO_4X3 }, /* QVGA */ + { 320, 320, ASPECT_RATIO_1X1 }, /* 320x320 */ { 400, 296, ASPECT_RATIO_4X3 }, /* CIF */ { 480, 320, ASPECT_RATIO_3X2 }, /* HVGA */ { 640, 480, ASPECT_RATIO_4X3 }, /* VGA */ @@ -44,6 +49,7 @@ const resolution_info_t resolution[FRAMESIZE_INVALID] = { { 2560, 1600, ASPECT_RATIO_16X10 }, /* WQXGA */ { 1088, 1920, ASPECT_RATIO_9X16 }, /* Portrait FHD */ { 2560, 1920, ASPECT_RATIO_4X3 }, /* QSXGA */ + { 2592, 1944, ASPECT_RATIO_4X3 }, /* 5MP */ }; camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id) diff --git a/examples/Makefile b/examples/Makefile deleted file mode 100644 index f06df0ecc9..0000000000 --- a/examples/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# -# This is a project Makefile. It is assumed the directory this Makefile resides in is a -# project subdirectory. -# - -PROJECT_NAME := camera_example - -EXTRA_COMPONENT_DIRS := ../ - -include $(IDF_PATH)/make/project.mk - diff --git a/examples/CMakeLists.txt b/examples/camera_example/CMakeLists.txt similarity index 67% rename from examples/CMakeLists.txt rename to examples/camera_example/CMakeLists.txt index 0a03968845..c44adc7b70 100644 --- a/examples/CMakeLists.txt +++ b/examples/camera_example/CMakeLists.txt @@ -2,8 +2,6 @@ # 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) +set(COMPONENTS main) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(camera_example) \ No newline at end of file +project(camera_example) diff --git a/examples/camera_example/main/CMakeLists.txt b/examples/camera_example/main/CMakeLists.txt new file mode 100644 index 0000000000..b3b4892124 --- /dev/null +++ b/examples/camera_example/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS take_picture.c + PRIV_INCLUDE_DIRS . + PRIV_REQUIRES nvs_flash esp_psram) diff --git a/examples/camera_example/main/camera_pinout.h b/examples/camera_example/main/camera_pinout.h new file mode 100644 index 0000000000..97b5d2fd36 --- /dev/null +++ b/examples/camera_example/main/camera_pinout.h @@ -0,0 +1,102 @@ +// WROVER-KIT PIN Map +#ifdef BOARD_WROVER_KIT + +#define CAM_PIN_PWDN -1 //power down is not used +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_XCLK 21 +#define CAM_PIN_SIOD 26 +#define CAM_PIN_SIOC 27 + +#define CAM_PIN_D7 35 +#define CAM_PIN_D6 34 +#define CAM_PIN_D5 39 +#define CAM_PIN_D4 36 +#define CAM_PIN_D3 19 +#define CAM_PIN_D2 18 +#define CAM_PIN_D1 5 +#define CAM_PIN_D0 4 +#define CAM_PIN_VSYNC 25 +#define CAM_PIN_HREF 23 +#define CAM_PIN_PCLK 22 + +#endif + +// ESP32Cam (AiThinker) PIN Map +#ifdef BOARD_ESP32CAM_AITHINKER + +#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 +#define CAM_PIN_SIOC 27 + +#define CAM_PIN_D7 35 +#define CAM_PIN_D6 34 +#define CAM_PIN_D5 39 +#define CAM_PIN_D4 36 +#define CAM_PIN_D3 21 +#define CAM_PIN_D2 19 +#define CAM_PIN_D1 18 +#define CAM_PIN_D0 5 +#define CAM_PIN_VSYNC 25 +#define CAM_PIN_HREF 23 +#define CAM_PIN_PCLK 22 + +#endif +// ESP32S3 (WROOM) PIN Map +#ifdef BOARD_ESP32S3_WROOM +#define CAM_PIN_PWDN 38 +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_VSYNC 6 +#define CAM_PIN_HREF 7 +#define CAM_PIN_PCLK 13 +#define CAM_PIN_XCLK 15 +#define CAM_PIN_SIOD 4 +#define CAM_PIN_SIOC 5 +#define CAM_PIN_D0 11 +#define CAM_PIN_D1 9 +#define CAM_PIN_D2 8 +#define CAM_PIN_D3 10 +#define CAM_PIN_D4 12 +#define CAM_PIN_D5 18 +#define CAM_PIN_D6 17 +#define CAM_PIN_D7 16 +#endif +// ESP32S3 (GOOUU TECH) +#ifdef BOARD_ESP32S3_GOOUUU +#define CAM_PIN_PWDN -1 +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_VSYNC 6 +#define CAM_PIN_HREF 7 +#define CAM_PIN_PCLK 13 +#define CAM_PIN_XCLK 15 +#define CAM_PIN_SIOD 4 +#define CAM_PIN_SIOC 5 +#define CAM_PIN_D0 11 +#define CAM_PIN_D1 9 +#define CAM_PIN_D2 8 +#define CAM_PIN_D3 10 +#define CAM_PIN_D4 12 +#define CAM_PIN_D5 18 +#define CAM_PIN_D6 17 +#define CAM_PIN_D7 16 +#endif +// ESP32S3 (XIAO) +#ifdef BOARD_ESP32S3_XIAO +#define CAM_PIN_PWDN -1 +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_VSYNC 38 +#define CAM_PIN_HREF 47 +#define CAM_PIN_PCLK 13 +#define CAM_PIN_XCLK 10 +#define CAM_PIN_SIOD 40 +#define CAM_PIN_SIOC 39 +#define CAM_PIN_D0 15 +#define CAM_PIN_D1 17 +#define CAM_PIN_D2 18 +#define CAM_PIN_D3 16 +#define CAM_PIN_D4 14 +#define CAM_PIN_D5 12 +#define CAM_PIN_D6 11 +#define CAM_PIN_D7 48 +#endif diff --git a/examples/camera_example/main/idf_component.yml b/examples/camera_example/main/idf_component.yml new file mode 100644 index 0000000000..efa9638bc0 --- /dev/null +++ b/examples/camera_example/main/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + espressif/esp32-camera: + version: "*" + override_path: "../../../" + diff --git a/examples/main/take_picture.c b/examples/camera_example/main/take_picture.c similarity index 67% rename from examples/main/take_picture.c rename to examples/camera_example/main/take_picture.c index 783fcc0149..e775ad7d80 100644 --- a/examples/main/take_picture.c +++ b/examples/camera_example/main/take_picture.c @@ -7,10 +7,14 @@ // 1. Board setup (Uncomment): // #define BOARD_WROVER_KIT // #define BOARD_ESP32CAM_AITHINKER +// #define BOARD_ESP32S3_WROOM +// #define BOARD_ESP32S3_XIAO +// #define BOARD_ESP32S3_GOOUUU +// #define BOARD_ESP32S3_XIAO /** * 2. Kconfig setup - * + * * If you have a Kconfig file, copy the content from * https://github.com/espressif/esp32-camera/blob/master/Kconfig into it. * In case you haven't, copy and paste this Kconfig file inside the src directory. @@ -20,9 +24,9 @@ /** * 3. Enable PSRAM on sdkconfig: - * + * * CONFIG_ESP32_SPIRAM_SUPPORT=y - * + * * More info on * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#config-esp32-spiram-support */ @@ -47,54 +51,11 @@ #define BOARD_WROVER_KIT 1 -// WROVER-KIT PIN Map -#ifdef BOARD_WROVER_KIT - -#define CAM_PIN_PWDN -1 //power down is not used -#define CAM_PIN_RESET -1 //software reset will be performed -#define CAM_PIN_XCLK 21 -#define CAM_PIN_SIOD 26 -#define CAM_PIN_SIOC 27 - -#define CAM_PIN_D7 35 -#define CAM_PIN_D6 34 -#define CAM_PIN_D5 39 -#define CAM_PIN_D4 36 -#define CAM_PIN_D3 19 -#define CAM_PIN_D2 18 -#define CAM_PIN_D1 5 -#define CAM_PIN_D0 4 -#define CAM_PIN_VSYNC 25 -#define CAM_PIN_HREF 23 -#define CAM_PIN_PCLK 22 - -#endif - -// ESP32Cam (AiThinker) PIN Map -#ifdef BOARD_ESP32CAM_AITHINKER - -#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 -#define CAM_PIN_SIOC 27 - -#define CAM_PIN_D7 35 -#define CAM_PIN_D6 34 -#define CAM_PIN_D5 39 -#define CAM_PIN_D4 36 -#define CAM_PIN_D3 21 -#define CAM_PIN_D2 19 -#define CAM_PIN_D1 18 -#define CAM_PIN_D0 5 -#define CAM_PIN_VSYNC 25 -#define CAM_PIN_HREF 23 -#define CAM_PIN_PCLK 22 - -#endif +#include "camera_pinout.h" static const char *TAG = "example:take_picture"; +#if ESP_CAMERA_SUPPORTED static camera_config_t camera_config = { .pin_pwdn = CAM_PIN_PWDN, .pin_reset = CAM_PIN_RESET, @@ -120,14 +81,15 @@ static camera_config_t camera_config = { .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_RGB565, //YUV422,GRAYSCALE,RGB565,JPEG - .frame_size = FRAMESIZE_QVGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG + .frame_size = FRAMESIZE_QVGA, //QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates. - .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 + .jpeg_quality = 12, //0-63, for OV series camera sensors, lower number means higher quality + .fb_count = 1, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode. + .fb_location = CAMERA_FB_IN_PSRAM, .grab_mode = CAMERA_GRAB_WHEN_EMPTY, }; -static esp_err_t init_camera() +static esp_err_t init_camera(void) { //initialize the camera esp_err_t err = esp_camera_init(&camera_config); @@ -139,9 +101,11 @@ static esp_err_t init_camera() return ESP_OK; } +#endif -void app_main() +void app_main(void) { +#if ESP_CAMERA_SUPPORTED if(ESP_OK != init_camera()) { return; } @@ -157,4 +121,8 @@ void app_main() vTaskDelay(5000 / portTICK_RATE_MS); } +#else + ESP_LOGE(TAG, "Camera support is not available for this chip"); + return; +#endif } diff --git a/examples/sdkconfig.defaults b/examples/camera_example/sdkconfig.defaults similarity index 96% rename from examples/sdkconfig.defaults rename to examples/camera_example/sdkconfig.defaults index e5ac4557ac..000a453e65 100644 --- a/examples/sdkconfig.defaults +++ b/examples/camera_example/sdkconfig.defaults @@ -9,9 +9,9 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_SPIRAM=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 - diff --git a/examples/main/CMakeLists.txt b/examples/main/CMakeLists.txt deleted file mode 100644 index 1735fb184b..0000000000 --- a/examples/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -set(COMPONENT_SRCS take_picture.c) -set(COMPONENT_ADD_INCLUDEDIRS .) -register_component() \ No newline at end of file diff --git a/examples/main/component.mk b/examples/main/component.mk deleted file mode 100644 index 0b9d7585e7..0000000000 --- a/examples/main/component.mk +++ /dev/null @@ -1,5 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) - diff --git a/idf_component.yml b/idf_component.yml index 2b98f8d06c..0ebeddb392 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,2 +1,10 @@ description: ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors. url: https://github.com/espressif/esp32-camera +issues: https://github.com/espressif/esp32-camera/issues +documentation: https://github.com/espressif/esp32-camera/tree/main/README.md +repository: https://github.com/espressif/esp32-camera.git +dependencies: + idf: ">=5.1" + esp_jpeg: + version: "^1.3.1" + public: true diff --git a/sensors/bf20a6.c b/sensors/bf20a6.c index b1179c30f8..99c48f9f80 100644 --- a/sensors/bf20a6.c +++ b/sensors/bf20a6.c @@ -161,6 +161,12 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) case PIXFORMAT_RAW: set_reg_bits(sensor, 0x12, 0, 1, 0x1); break; + case PIXFORMAT_GRAYSCALE: + write_reg(sensor->slv_addr, 0x12, 0x23); + write_reg(sensor->slv_addr, 0x3a, 0x00); + write_reg(sensor->slv_addr, 0xe1, 0x92); + write_reg(sensor->slv_addr, 0xe3, 0x02); + break; default: ESP_LOGW(TAG, "set_pix unsupport format"); ret = -1; @@ -341,7 +347,7 @@ static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val) return -1; } -int bf20a6_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_bf20a6_detect(int slv_addr, sensor_id_t *id) { if (BF20A6_SCCB_ADDR == slv_addr) { uint8_t MIDL = SCCB_Read(slv_addr, SENSOR_ID_LOW); @@ -357,7 +363,7 @@ int bf20a6_detect(int slv_addr, sensor_id_t *id) return 0; } -int bf20a6_init(sensor_t *sensor) +int esp32_camera_bf20a6_init(sensor_t *sensor) { sensor->init_status = init_status; sensor->reset = reset; diff --git a/sensors/bf3005.c b/sensors/bf3005.c index 5a6166e49d..b492f35331 100644 --- a/sensors/bf3005.c +++ b/sensors/bf3005.c @@ -476,7 +476,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int bf3005_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_bf3005_detect(int slv_addr, sensor_id_t *id) { if (BF3005_SCCB_ADDR == slv_addr) { uint16_t PID = SCCB_Read(slv_addr, 0xFC); @@ -493,7 +493,7 @@ int bf3005_detect(int slv_addr, sensor_id_t *id) return 0; } -int bf3005_init(sensor_t *sensor) +int esp32_camera_bf3005_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; diff --git a/sensors/gc0308.c b/sensors/gc0308.c index f19025ebb3..469f0385e2 100644 --- a/sensors/gc0308.c +++ b/sensors/gc0308.c @@ -160,6 +160,10 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) write_reg(sensor->slv_addr, 0xfe, 0x00); ret = set_reg_bits(sensor->slv_addr, 0x24, 0, 0x0f, 2); //yuv422 Y Cb Y Cr break; + case PIXFORMAT_GRAYSCALE: + write_reg(sensor->slv_addr, 0xfe, 0x00); + ret = write_reg(sensor->slv_addr, 0x24, 0xb1); + break; default: ESP_LOGW(TAG, "unsupport format"); ret = -1; @@ -253,8 +257,8 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) 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, 0xf9, (col_s + w) / 4); + write_reg(sensor->slv_addr, 0xfa, (row_s + h) / 4); write_reg(sensor->slv_addr, 0x05, H8(row_s)); write_reg(sensor->slv_addr, 0x06, L8(row_s)); @@ -275,7 +279,8 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) static int set_contrast(sensor_t *sensor, int contrast) { - if (contrast != 0) { + if (contrast > 0) { + sensor->status.contrast = contrast; write_reg(sensor->slv_addr, 0xfe, 0x00); write_reg(sensor->slv_addr, 0xb3, contrast); } @@ -366,7 +371,7 @@ static int init_status(sensor_t *sensor) { write_reg(sensor->slv_addr, 0xfe, 0x00); sensor->status.brightness = 0; - sensor->status.contrast = 0; + sensor->status.contrast = 50; sensor->status.saturation = 0; sensor->status.sharpness = 0; sensor->status.denoise = 0; @@ -406,7 +411,7 @@ static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val) return -1; } -int gc0308_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_gc0308_detect(int slv_addr, sensor_id_t *id) { if (GC0308_SCCB_ADDR == slv_addr) { write_reg(slv_addr, 0xfe, 0x00); @@ -421,7 +426,7 @@ int gc0308_detect(int slv_addr, sensor_id_t *id) return 0; } -int gc0308_init(sensor_t *sensor) +int esp32_camera_gc0308_init(sensor_t *sensor) { sensor->init_status = init_status; sensor->reset = reset; diff --git a/sensors/gc032a.c b/sensors/gc032a.c index 612e17b1ed..669960dc64 100644 --- a/sensors/gc032a.c +++ b/sensors/gc032a.c @@ -328,7 +328,7 @@ static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val) return -1; } -int gc032a_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_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); @@ -344,7 +344,7 @@ int gc032a_detect(int slv_addr, sensor_id_t *id) return 0; } -int gc032a_init(sensor_t *sensor) +int esp32_camera_gc032a_init(sensor_t *sensor) { sensor->init_status = init_status; sensor->reset = reset; diff --git a/sensors/gc2145.c b/sensors/gc2145.c index 3a066cd941..1ab50c21f5 100644 --- a/sensors/gc2145.c +++ b/sensors/gc2145.c @@ -414,7 +414,7 @@ static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val) return -1; } -int gc2145_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_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); @@ -430,7 +430,7 @@ int gc2145_detect(int slv_addr, sensor_id_t *id) return 0; } -int gc2145_init(sensor_t *sensor) +int esp32_camera_gc2145_init(sensor_t *sensor) { sensor->init_status = init_status; sensor->reset = reset; diff --git a/sensors/hm0360.c b/sensors/hm0360.c new file mode 100755 index 0000000000..7013902e92 --- /dev/null +++ b/sensors/hm0360.c @@ -0,0 +1,475 @@ +/* + * + * HM0360 driver. + * + */ +#include +#include +#include +#include +#include "sccb.h" +#include "xclk.h" +#include "hm0360.h" +#include "hm0360_regs.h" +#include "hm0360_settings.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 = "HM0360"; +#endif + +// #define REG_DEBUG_ON + +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); + +static int read_reg(uint8_t slv_addr, const uint16_t reg) +{ + int ret = SCCB_Read16(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 check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask) +{ + return (read_reg(slv_addr, reg) & mask) == mask; +} + +static int read_reg16(uint8_t slv_addr, const uint16_t reg) +{ + int ret = 0, ret2 = 0; + + ret = read_reg(slv_addr, reg); + if (ret >= 0) { + ret = (ret & 0xFF) << 8; + ret2 = read_reg(slv_addr, reg + 1); + if (ret2 < 0) { + ret = ret2; + } else { + ret |= ret2 & 0xFF; + } + } + + 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_Write16(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_LOGD(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value); + ret = SCCB_Write16(slv_addr, reg, value); + } else { + ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value); + ret = SCCB_Write16(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 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 write_reg16(uint8_t slv_addr, const uint16_t reg, uint16_t value) +{ + if (write_reg(slv_addr, reg, value >> 8) || write_reg(slv_addr, reg + 1, value)) { + return -1; + } + return 0; +} + +static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value, uint16_t y_value) +{ + if (write_reg16(slv_addr, reg, x_value) || write_reg16(slv_addr, reg + 2, y_value)) { + return -1; + } + return 0; +} + +#define write_reg_bits(slv_addr, reg, mask, enable) set_reg_bits(slv_addr, reg, 0, mask, (enable) ? (mask) : 0) + +static int reset(sensor_t *sensor) +{ + vTaskDelay(100 / portTICK_PERIOD_MS); + int ret = 0; + + // Software Reset: clear all registers and reset them to their default values + ret = write_reg(sensor->slv_addr, SW_RESET, 0x00); + if (ret) { + ESP_LOGE(TAG, "Software Reset FAILED!"); + return ret; + } + + vTaskDelay(100 / portTICK_PERIOD_MS); + ret = write_regs(sensor->slv_addr, sensor_default_regs); + if (ret == 0) { + ESP_LOGD(TAG, "Camera defaults loaded"); + vTaskDelay(100 / portTICK_PERIOD_MS); + } + + return ret; +} + +static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) +{ + int ret = 0; + sensor->pixformat = pixformat; + + switch (pixformat) { + case PIXFORMAT_GRAYSCALE: + break; + default: + ESP_LOGE(TAG, "Only support GRAYSCALE"); + return -1; + } + + return ret; +} + +static int set_framesize(sensor_t *sensor, framesize_t framesize) +{ + int ret = 0; + + sensor->status.framesize = framesize; + ret = write_regs(sensor->slv_addr, sensor_default_regs); + + if (framesize == FRAMESIZE_QQVGA) { + ESP_LOGI(TAG, "Set FRAMESIZE_QQVGA"); + ret |= write_regs(sensor->slv_addr, sensor_framesize_QQVGA); + ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 1); + } else if (framesize == FRAMESIZE_QVGA) { + ESP_LOGI(TAG, "Set FRAMESIZE_QVGA"); + ret |= write_regs(sensor->slv_addr, sensor_framesize_QVGA); + ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 1); + } else if (framesize == FRAMESIZE_VGA) { + ESP_LOGI(TAG, "Set FRAMESIZE_VGA"); + ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 0); + } else { + ESP_LOGI(TAG, "Dont suppost this size, Set FRAMESIZE_VGA"); + ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 0); + } + + if (ret == 0) { + _set_pll(sensor, 0, 0, 0, 0, 0, 0, 0, 0); + ret |= write_reg(sensor->slv_addr, 0x0104, 0x01); + } + + return ret; +} + +static int set_hmirror(sensor_t *sensor, int enable) +{ + if (set_reg_bits(sensor->slv_addr, 0x0101, 0, 0x01, enable)) { + return -1; + } + + ESP_LOGD(TAG, "Set h-mirror to: %d", enable); + + return 0; +} + +static int set_vflip(sensor_t *sensor, int enable) +{ + if (set_reg_bits(sensor->slv_addr, 0x0101, 1, 0x01, enable)) { + return -1; + } + + ESP_LOGD(TAG, "Set v-flip to: %d", enable); + + return 0; +} + +static int set_colorbar(sensor_t *sensor, int enable) +{ + if (set_reg_bits(sensor->slv_addr, 0x0601, 0, 0x01, enable)) { + return -1; + } + + ESP_LOGD(TAG, "Set color-bar to: %d", enable); + + return 0; +} + +static int set_exposure_ctrl(sensor_t *sensor, int enable) +{ + if (set_reg_bits(sensor->slv_addr, 0x2000, 0, 0x01, enable)) { + return -1; + } + + ESP_LOGD(TAG, "Set exposure to: %d", enable); + + return 0; +} + +static int set_brightness(sensor_t *sensor, int level) +{ + uint8_t ae_mean; + + switch (level) { + case 0: + ae_mean = 60; + break; + case 1: + ae_mean = 80; + break; + case 2: + ae_mean = 100; + break; + case 3: + ae_mean = 127; + break; + default: + ae_mean = 80; + } + + return write_reg(sensor->slv_addr, AE_TARGET_MEAN, ae_mean); +} + +static int get_reg(sensor_t *sensor, int reg, int mask) +{ + int ret = 0, ret2 = 0; + + if (mask > 0xFF) { + ret = read_reg16(sensor->slv_addr, reg); + if (ret >= 0 && mask > 0xFFFF) { + ret2 = read_reg(sensor->slv_addr, reg + 2); + if (ret2 >= 0) { + ret = (ret << 8) | ret2; + } else { + ret = ret2; + } + } + } 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, ret2 = 0; + + if (mask > 0xFF) { + ret = read_reg16(sensor->slv_addr, reg); + if (ret >= 0 && mask > 0xFFFF) { + ret2 = read_reg(sensor->slv_addr, reg + 2); + if (ret2 >= 0) { + ret = (ret << 8) | ret2; + } else { + ret = ret2; + } + } + } else { + ret = read_reg(sensor->slv_addr, reg); + } + + if (ret < 0) { + return ret; + } + + value = (ret & ~mask) | (value & mask); + if (mask > 0xFFFF) { + ret = write_reg16(sensor->slv_addr, reg, value >> 8); + if (ret >= 0) { + ret = write_reg(sensor->slv_addr, reg + 2, value & 0xFF); + } + } else if (mask > 0xFF) { + ret = write_reg16(sensor->slv_addr, reg, value); + } else { + ret = write_reg(sensor->slv_addr, reg, value); + } + + return ret; +} + +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); + if (ret == 0) { + ESP_LOGD(TAG, "Set xclk to %d", xclk); + } + return ret; +} + +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) +{ + (void)bypass; + (void)multiplier; + (void)sys_div; + (void)root_2x; + (void)pre_div; + (void)seld5; + (void)pclk_manual; + (void)pclk_div; + uint8_t value = 0; + uint8_t pll_cfg = 0; + + if (sensor->xclk_freq_hz <= 6000000) { + value = 0x03; + } else if (sensor->xclk_freq_hz <= 12000000) { + value = 0x02; + } else if (sensor->xclk_freq_hz <= 18000000) { + value = 0x01; + } else { // max is 48000000 + value = 0x00; + } + + int ret = read_reg(sensor->slv_addr, PLL1CFG); + if (ret < 0) { + return ret; + } + if (ret > 0xFF) { + /* + * Guard against unexpected wide register values. If read_reg + * ever returns a 16-bit result, reject values that don't fit + * in a single byte to avoid truncation. + */ + return -ERANGE; + } + + pll_cfg = (uint8_t)ret; + return write_reg(sensor->slv_addr, PLL1CFG, (pll_cfg & 0xFC) | value); +} + +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; +} + +static int init_status(sensor_t *sensor) +{ + (void) write_addr_reg; + + 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.awb = 0; + sensor->status.aec = 0; + sensor->status.hmirror = check_reg_mask(sensor->slv_addr, 0x101, 0x01); + sensor->status.vflip = check_reg_mask(sensor->slv_addr, 0x101, 0x02); + sensor->status.lenc = 0; + sensor->status.awb_gain = 0; + sensor->status.agc_gain = 0; + sensor->status.aec_value = 0; + + return 0; +} + +int esp32_camera_hm0360_detect(int slv_addr, sensor_id_t *id) +{ + if (HM1055_SCCB_ADDR == slv_addr) { + uint8_t h = SCCB_Read16(slv_addr, MODEL_ID_H); + uint8_t l = SCCB_Read16(slv_addr, MODEL_ID_L); + uint16_t PID = (h << 8) | l; + if (HM0360_PID == PID) { + id->PID = PID; + id->VER = SCCB_Read16(slv_addr, SILICON_REV); + return PID; + } else { + ESP_LOGD(TAG, "Mismatch PID=0x%x", PID); + } + } + return 0; +} + +int esp32_camera_hm0360_init(sensor_t *sensor) +{ + sensor->reset = reset; + sensor->set_pixformat = set_pixformat; + sensor->set_framesize = set_framesize; + sensor->set_contrast = set_dummy; + sensor->set_brightness = set_brightness; + sensor->set_saturation = set_dummy; + sensor->set_sharpness = set_dummy; + sensor->set_gainceiling = set_gainceiling_dummy; + sensor->set_quality = set_dummy; + sensor->set_colorbar = set_colorbar; + sensor->set_gain_ctrl = set_dummy; + sensor->set_exposure_ctrl = set_exposure_ctrl; + sensor->set_whitebal = set_dummy; + sensor->set_hmirror = set_hmirror; + sensor->set_vflip = set_vflip; + sensor->init_status = init_status; + sensor->set_aec2 = 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_agc_gain = set_dummy; + sensor->set_raw_gma = set_dummy; + sensor->set_lenc = set_dummy; + sensor->set_denoise = set_dummy; + + sensor->get_reg = get_reg; + sensor->set_reg = set_reg; + sensor->set_res_raw = NULL; + sensor->set_pll = _set_pll; + sensor->set_xclk = set_xclk; + return 0; +} diff --git a/sensors/hm1055.c b/sensors/hm1055.c new file mode 100755 index 0000000000..2529297fd5 --- /dev/null +++ b/sensors/hm1055.c @@ -0,0 +1,832 @@ +/* + * + * HM1055 driver. + * + */ +#include +#include +#include +#include "sccb.h" +#include "xclk.h" +#include "hm1055.h" +#include "hm1055_regs.h" +#include "hm1055_settings.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 = "HM1055"; +#endif + +// #define REG_DEBUG_ON + +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); + +static int read_reg(uint8_t slv_addr, const uint16_t reg) +{ + int ret = SCCB_Read16(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 check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask) +{ + return (read_reg(slv_addr, reg) & mask) == mask; +} + +static int read_reg16(uint8_t slv_addr, const uint16_t reg) +{ + int ret = 0, ret2 = 0; + ret = read_reg(slv_addr, reg); + if (ret >= 0) + { + ret = (ret & 0xFF) << 8; + ret2 = read_reg(slv_addr, reg + 1); + if (ret2 < 0) + { + ret = ret2; + } + else + { + ret |= ret2 & 0xFF; + } + } + 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_Write16(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_LOGD(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value); + ret = SCCB_Write16(slv_addr, reg, value); + } + else + { + ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value); + ret = SCCB_Write16(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 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 write_reg16(uint8_t slv_addr, const uint16_t reg, uint16_t value) +{ + if (write_reg(slv_addr, reg, value >> 8) || write_reg(slv_addr, reg + 1, value)) + { + return -1; + } + return 0; +} + +static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value, uint16_t y_value) +{ + if (write_reg16(slv_addr, reg, x_value) || write_reg16(slv_addr, reg + 2, y_value)) + { + return -1; + } + return 0; +} + +#define write_reg_bits(slv_addr, reg, mask, enable) set_reg_bits(slv_addr, reg, 0, mask, (enable) ? (mask) : 0) + +static int set_ae_level(sensor_t *sensor, int level); + +static int reset(sensor_t *sensor) +{ + vTaskDelay(100 / portTICK_PERIOD_MS); + int ret = 0; + // Software Reset: clear all registers and reset them to their default values + ret = write_reg(sensor->slv_addr, SFTRST, 0x55); + if (ret) + { + ESP_LOGE(TAG, "Software Reset FAILED!"); + return ret; + } + vTaskDelay(100 / portTICK_PERIOD_MS); + ret = write_regs(sensor->slv_addr, sensor_default_regs); + if (ret == 0) + { + ESP_LOGD(TAG, "Camera defaults loaded"); + vTaskDelay(100 / portTICK_PERIOD_MS); + set_ae_level(sensor, 0); + } + return ret; +} + +static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) +{ + int ret = 0; + + switch (pixformat) + { + case PIXFORMAT_RAW: + ret = write_reg(sensor->slv_addr, PORTCTRL, 0x20); + break; + case PIXFORMAT_YUV422: + ret = write_reg(sensor->slv_addr, PORTCTRL, 0x30); + break; + case PIXFORMAT_RGB565: + case PIXFORMAT_RGB888: + ret = write_reg(sensor->slv_addr, PORTCTRL, 0x40); + break; + case PIXFORMAT_RGB555: + ret = write_reg(sensor->slv_addr, PORTCTRL, 0x50); + break; + case PIXFORMAT_RGB444: + ret = write_reg(sensor->slv_addr, PORTCTRL, 0x60); + break; + default: + break; + } + + if (ret == 0) + { + sensor->pixformat = pixformat; + ESP_LOGD(TAG, "Set pixformat: %d", pixformat); + } + + return ret; +} + +static int set_framesize(sensor_t *sensor, framesize_t framesize) +{ + int ret = 0; + + sensor->status.framesize = framesize; + ESP_LOGD(TAG, "Set framesize: %d", framesize); + ret = write_regs(sensor->slv_addr, sensor_default_regs); + if (framesize == FRAMESIZE_QQVGA) + { + ESP_LOGD(TAG, "Set FRAMESIZE_QQVGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QQVGA); + } + else if (framesize == FRAMESIZE_QCIF) + { + ESP_LOGD(TAG, "Set FRAMESIZE_QCIF"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QCIF); + } + else if (framesize == FRAMESIZE_240X240) + { + ESP_LOGD(TAG, "Set FRAMESIZE_240X240"); + ret = write_regs(sensor->slv_addr, sensor_framesize_240X240); + } + else if (framesize == FRAMESIZE_QVGA) + { + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA); + } + else if (framesize == FRAMESIZE_CIF) + { + ESP_LOGD(TAG, "Set FRAMESIZE_CIF"); + ret = write_regs(sensor->slv_addr, sensor_framesize_CIF); + } + else if (framesize == FRAMESIZE_VGA) + { + ESP_LOGD(TAG, "Set FRAMESIZE_VGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA); + } + else if (framesize == FRAMESIZE_SVGA) + { + ESP_LOGD(TAG, "Set FRAMESIZE_SVGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_SVGA); + } + else if (framesize == FRAMESIZE_HD) + { + ESP_LOGD(TAG, "Set FRAMESIZE_HD"); + ret = write_regs(sensor->slv_addr, sensor_framesize_HD); + ret = _set_pll(sensor, 0, 288, 1, 0, 0, 0, 1, 16); + } + else + { + ESP_LOGD(TAG, "Dont suppost this size, Set FRAMESIZE_VGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA); + } + + if (ret == 0) + { + ret = write_reg(sensor->slv_addr, CMU, 0x01) || write_reg(sensor->slv_addr, TGRDCFG, 0x01); + } + + return ret; +} + +static int set_hmirror(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = write_reg_bits(sensor->slv_addr, RDCFG, 0x02, enable); + if (ret == 0) + { + ESP_LOGD(TAG, "Set hmirror to: %d", enable); + sensor->status.hmirror = enable; + } + return ret; +} + +static int set_vflip(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = write_reg_bits(sensor->slv_addr, RDCFG, 0x01, enable); + if (ret == 0) + { + ESP_LOGD(TAG, "Set vflip to: %d", enable); + sensor->status.vflip = enable; + } + return ret; +} + +static int set_quality(sensor_t *sensor, int qs) +{ + return 0; +} + +static int set_colorbar(sensor_t *sensor, int enable) +{ + return 0; +} + +static int set_gain_ctrl(sensor_t *sensor, int enable) +{ + return 0; +} + +static int set_exposure_ctrl(sensor_t *sensor, int enable) +{ + int ret = 0; + + ret = write_reg_bits(sensor->slv_addr, AEWBCFG, 0x01, enable); + + if (ret == 0) + { + ESP_LOGD(TAG, "Set aec to: %d", enable); + sensor->status.aec = enable; + } + + return ret; +} + +static int set_whitebal(sensor_t *sensor, int enable) +{ + int ret = 0; + + ret = write_reg_bits(sensor->slv_addr, AEWBCFG, 0x02, enable); + + if (ret == 0) + { + ESP_LOGD(TAG, "Set awb to: %d", enable); + sensor->status.awb = enable; + } + return ret; +} + +// Gamma enable +static int set_raw_gma_dsp(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = write_reg_bits(sensor->slv_addr, ISPCTRL1, 0x04, enable); + if (ret == 0) + { + ESP_LOGD(TAG, "Set raw_gma to: %d", enable); + sensor->status.raw_gma = enable; + } + return 0; +} + +static int set_lenc_dsp(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = write_reg_bits(sensor->slv_addr, ISPCTRL3, 0x40, enable); + if (ret == 0) + { + ESP_LOGD(TAG, "Set lenc to: %d", enable); + sensor->status.lenc = enable; + } + return -1; +} +// real gain +static int set_agc_gain(sensor_t *sensor, int gain) +{ + int ret = 0; + if (gain < 0 || gain > 7) + { + return -1; + } + + ret = write_reg(sensor->slv_addr, AGAIN, gain); + if (ret == 0) + { + ESP_LOGD(TAG, "Set gain to: %d", gain); + sensor->status.agc_gain = gain; + } + return 0; +} +static int set_aec_value(sensor_t *sensor, int value) +{ + int ret = 0; + ret = write_reg(sensor->slv_addr, AETARGM, value); + if (ret == 0) + { + ESP_LOGD(TAG, "Set aec_value to: %d", value); + sensor->status.aec_value = value; + } + + return 0; +} + +static int set_ae_level(sensor_t *sensor, int level) +{ + int ret = 0; + if (level < -5 || level > 5) + { + return -1; + } + uint8_t target_level = ((level + 5) * 10) + 5; + uint8_t upper = target_level * 27 / 25; + uint8_t lower = target_level * 23 / 25; + + ret = write_reg(sensor->slv_addr, AETARGU, upper) || write_reg(sensor->slv_addr, AETARGL, lower); + if (ret == 0) + { + ESP_LOGD(TAG, "Set ae_level to: %d", level); + sensor->status.ae_level = level; + } + return 0; +} + +static int set_brightness(sensor_t *sensor, int level) +{ + int ret = 0; + uint8_t ispctrl5 = read_reg(sensor->slv_addr, ISPCTRL5); + uint8_t brightness = 0; + + switch (level) + { + case 3: + brightness = 0xFF; + break; + + case 2: + brightness = 0xBA; + break; + + case 1: + brightness = 0x96; + break; + + case 0: + brightness = 0x72; + break; + + case -1: + brightness = 0x48; + break; + + case -2: + brightness = 0x24; + break; + + case -3: + brightness = 0x00; + break; + + default: // 0 + break; + } + + ispctrl5 |= 0x40; // enable brightness + ret = write_reg(sensor->slv_addr, ISPCTRL5, ispctrl5); + if (ret == 0) + { + ret = write_reg(sensor->slv_addr, BRIGHT, brightness); + } + 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; + uint8_t ispctrl5 = read_reg(sensor->slv_addr, ISPCTRL5); + + ispctrl5 |= 0x80; // enable contrast + ret = write_reg(sensor->slv_addr, ISPCTRL5, ispctrl5); + ret = write_reg(sensor->slv_addr, ACONTQ, (level * 0x20) & 0xFF); + if (ret == 0) + { + ESP_LOGD(TAG, "Set contrast to: %d", level); + sensor->status.contrast = level; + } + return ret; +} + +static int set_saturation(sensor_t *sensor, int level) +{ + int ret = 0; + ret = write_reg(sensor->slv_addr, SAT, (level * 0x20) + 0x4A); + if (ret == 0) + { + ESP_LOGD(TAG, "Set saturation to: %d", level); + sensor->status.saturation = level; + } + return ret; +} + +static int get_sharpness(sensor_t *sensor) +{ + int ret = 0; + int level = 0; + ret = read_reg(sensor->slv_addr, EDGE); + + level = (ret - 0x60) / 0x20; + ESP_LOGD(TAG, "Get sharpness: %d", level); + + return level; +} +static int set_sharpness(sensor_t *sensor, int level) +{ + int ret = 0; + ret = write_reg(sensor->slv_addr, EDGE, (level * 0x20) + 0x60); + if (ret == 0) + { + ESP_LOGD(TAG, "Set sharpness to: %d", level); + sensor->status.sharpness = level; + } + return ret; +} + +static int get_denoise(sensor_t *sensor) +{ + int ret = 0; + int level = 0; + ret = read_reg(sensor->slv_addr, YDN); + + level = (ret - 0x07) / 2; + ESP_LOGD(TAG, "Get denoise: %d", level); + + return level; +} +static int set_denoise(sensor_t *sensor, int level) +{ + int ret = 0; + uint8_t ispctrl5 = read_reg(sensor->slv_addr, ISPCTRL5); + + ispctrl5 |= 0x20; // enable denoise + ret = write_reg(sensor->slv_addr, ISPCTRL5, ispctrl5); + ret = write_reg(sensor->slv_addr, YDN, (level * 2) + 0x07); + + if (ret == 0) + { + ESP_LOGD(TAG, "Set denoise to: %d", level); + sensor->status.denoise = level; + } + + return ret; +} + +static int get_reg(sensor_t *sensor, int reg, int mask) +{ + int ret = 0, ret2 = 0; + if (mask > 0xFF) + { + ret = read_reg16(sensor->slv_addr, reg); + if (ret >= 0 && mask > 0xFFFF) + { + ret2 = read_reg(sensor->slv_addr, reg + 2); + if (ret2 >= 0) + { + ret = (ret << 8) | ret2; + } + else + { + ret = ret2; + } + } + } + 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, ret2 = 0; + if (mask > 0xFF) + { + ret = read_reg16(sensor->slv_addr, reg); + if (ret >= 0 && mask > 0xFFFF) + { + ret2 = read_reg(sensor->slv_addr, reg + 2); + if (ret2 >= 0) + { + ret = (ret << 8) | ret2; + } + else + { + ret = ret2; + } + } + } + else + { + ret = read_reg(sensor->slv_addr, reg); + } + if (ret < 0) + { + return ret; + } + value = (ret & ~mask) | (value & mask); + if (mask > 0xFFFF) + { + ret = write_reg16(sensor->slv_addr, reg, value >> 8); + if (ret >= 0) + { + ret = write_reg(sensor->slv_addr, reg + 2, value & 0xFF); + } + } + else if (mask > 0xFF) + { + ret = write_reg16(sensor->slv_addr, reg, value); + } + else + { + ret = write_reg(sensor->slv_addr, reg, value); + } + return ret; +} + +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 0; +} + +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) +{ + int ret = 0; + uint8_t ckcfg1 = 0; + uint8_t ckcfg2 = 0; + uint8_t ckcfg3 = 0; + uint8_t pll2 = 0; + + if (sensor->xclk_freq_hz <= 6000000) + { + ckcfg2 = 0x00; + } + else if (sensor->xclk_freq_hz <= 12000000) + { + ckcfg2 = 0x20; + } + else if (sensor->xclk_freq_hz <= 18000000) + { + ckcfg2 = 0x40; + } + else if (sensor->xclk_freq_hz <= 24000000) + { + ckcfg2 = 0x60; + } + else if (sensor->xclk_freq_hz <= 30000000) + { + ckcfg2 = 0x80; + } + else if (sensor->xclk_freq_hz <= 36000000) + { + ckcfg2 = 0xA0; + } + else if (sensor->xclk_freq_hz <= 42000000) + { + ckcfg2 = 0xC0; + } + else + { // max is 48000000 + ckcfg2 = 0xE0; + } + + if (bypass == 0) + { + switch (multiplier) + { + case 204: + ckcfg2 |= 10; + break; + case 216: + ckcfg2 |= 11; + break; + case 228: + ckcfg2 |= 0x12; + break; + case 240: + ckcfg2 |= 0x13; + break; + case 288: + ckcfg2 |= 0x17; + break; + case 300: + ckcfg2 |= 0x18; + break; + case 312: + ckcfg2 |= 0x19; + break; + case 324: + ckcfg2 |= 0x1A; + break; + case 336: + ckcfg2 |= 0x1B; + break; + case 348: + ckcfg2 |= 0x1C; + break; + case 360: + ckcfg2 |= 0x1D; + break; + default: + ckcfg2 |= 0x17; + break; + } + } + + if (pclk_manual > 0) + { + if (pclk_div > 128) + { + pclk_div = 128; + } + if (pclk_div < 1) + { + pclk_div = 1; + } + ckcfg1 |= (pclk_div - 1); + } + + if (root_2x > 0) + { + ckcfg3 = 0x00; + } + else + { + ckcfg3 = 0x01; + } + + ESP_LOGD(TAG, "ckcfg1 = 0x%02x, ckcfg2 = 0x%02x, ckcfg3 = 0x%02x, pll2 = 0x%02x", ckcfg1, ckcfg2, ckcfg3, pll2); + ret = write_reg(sensor->slv_addr, CKCFG1, ckcfg1); + ret = write_reg(sensor->slv_addr, CKCFG2, ckcfg2); + ret = write_reg(sensor->slv_addr, CKCFG3, ckcfg3); + ret = write_reg(sensor->slv_addr, PLL2, pll2); + + return ret; +} + +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); + if (ret == 0) + { + ESP_LOGD(TAG, "Set xclk to %d", xclk); + } + return ret; +} + +static int init_status(sensor_t *sensor) +{ + (void) write_addr_reg; + + sensor->status.brightness = 0; + sensor->status.contrast = 0; + sensor->status.saturation = 0; + sensor->status.sharpness = get_sharpness(sensor); + sensor->status.denoise = get_denoise(sensor); + sensor->status.ae_level = 0; + sensor->status.awb = check_reg_mask(sensor->slv_addr, AEWBCFG, 0x02); + sensor->status.agc = true; + sensor->status.aec = check_reg_mask(sensor->slv_addr, AEWBCFG, 0x04); + sensor->status.hmirror = check_reg_mask(sensor->slv_addr, RDCFG, 0x02); + sensor->status.vflip = check_reg_mask(sensor->slv_addr, RDCFG, 0x01); + sensor->status.lenc = check_reg_mask(sensor->slv_addr, ISPCTRL3, 0x40); + sensor->status.awb_gain = read_reg(sensor->slv_addr, DGAIN); + sensor->status.agc_gain = read_reg(sensor->slv_addr, AGAIN); + sensor->status.aec_value = read_reg(sensor->slv_addr, AETARGM); + return 0; +} + +int esp32_camera_hm1055_detect(int slv_addr, sensor_id_t *id) +{ + if (HM1055_SCCB_ADDR == slv_addr) + { + uint8_t h = SCCB_Read16(slv_addr, IDH); + uint8_t l = SCCB_Read16(slv_addr, IDL); + uint16_t PID = (h << 8) | l; + if (HM1055_PID == PID) + { + id->PID = PID; + return PID; + } + else + { + ESP_LOGD(TAG, "Mismatch PID=0x%x", PID); + } + } + return 0; +} + +int esp32_camera_hm1055_init(sensor_t *sensor) +{ + sensor->reset = reset; + sensor->set_pixformat = set_pixformat; + sensor->set_framesize = set_framesize; + sensor->set_contrast = set_contrast; + sensor->set_brightness = set_brightness; + sensor->set_saturation = set_saturation; + sensor->set_sharpness = set_sharpness; + sensor->set_gainceiling = NULL; + sensor->set_quality = set_quality; + sensor->set_colorbar = set_colorbar; + sensor->set_gain_ctrl = set_gain_ctrl; + sensor->set_exposure_ctrl = set_exposure_ctrl; + sensor->set_whitebal = set_whitebal; + sensor->set_hmirror = set_hmirror; + sensor->set_vflip = set_vflip; + sensor->init_status = init_status; + sensor->set_aec2 = NULL; + sensor->set_aec_value = set_aec_value; + sensor->set_special_effect = NULL; + sensor->set_wb_mode = NULL; + sensor->set_ae_level = set_ae_level; + sensor->set_dcw = NULL; + sensor->set_bpc = NULL; + sensor->set_wpc = NULL; + sensor->set_agc_gain = set_agc_gain; + sensor->set_raw_gma = set_raw_gma_dsp; + sensor->set_lenc = set_lenc_dsp; + sensor->set_denoise = set_denoise; + + 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; + return 0; +} diff --git a/sensors/mega_ccm.c b/sensors/mega_ccm.c new file mode 100644 index 0000000000..bdd1903542 --- /dev/null +++ b/sensors/mega_ccm.c @@ -0,0 +1,407 @@ +// 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 +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sccb.h" +#include "mega_ccm.h" +#include "mega_ccm_regs.h" +#include "mega_ccm_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 = "mega_ccm"; +#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_Read16(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; + ret = SCCB_Write16(slv_addr, reg, value); + return ret; +} + + +static int reset(sensor_t *sensor) +{ + int ret; + ret = write_reg(sensor->slv_addr, CAMERA_RST_REG, 0x00); + ret += write_reg(sensor->slv_addr, CAMERA_RST_REG, 0x01); + vTaskDelay(1000 / portTICK_PERIOD_MS); + return ret; +} + +static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) +{ + int ret = 0; + switch (pixformat) { + case PIXFORMAT_JPEG: + ret = write_reg(sensor->slv_addr, PIXEL_FMT_REG, 0x01); + break; + case PIXFORMAT_RGB565: + ret = write_reg(sensor->slv_addr, PIXEL_FMT_REG, 0x02); + break; + case PIXFORMAT_YUV422: + ret = write_reg(sensor->slv_addr, PIXEL_FMT_REG, 0x03); + 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_5MP) { + ESP_LOGW(TAG, "Invalid framesize: %u", framesize); + framesize = FRAMESIZE_5MP; + } + sensor->status.framesize = framesize; + uint16_t w = resolution[framesize].width; + uint16_t h = resolution[framesize].height; + switch (framesize){ + case FRAMESIZE_QVGA: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x01); //320x240 + ret += write_reg(sensor->slv_addr, SYSTEM_CLK_DIV_REG, 0x02); // set system clk + ret += write_reg(sensor->slv_addr, SYSTEM_PLL_DIV_REG, 0x01); // set system pll + break; + case FRAMESIZE_VGA: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x02); //640x480 + ret += write_reg(sensor->slv_addr, SYSTEM_CLK_DIV_REG, 0x02); // set system clk + ret += write_reg(sensor->slv_addr, SYSTEM_PLL_DIV_REG, 0x01); // set system pll + break; + case FRAMESIZE_HD: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x03); //1280x720 + ret += write_reg(sensor->slv_addr, SYSTEM_CLK_DIV_REG, 0x02); // set system clk + ret += write_reg(sensor->slv_addr, SYSTEM_PLL_DIV_REG, 0x01); // set system pll + break; + case FRAMESIZE_UXGA: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x04); //1600x1200 + ret += write_reg(sensor->slv_addr, SYSTEM_CLK_DIV_REG, 0x02); // set system clk + ret += write_reg(sensor->slv_addr, SYSTEM_PLL_DIV_REG, 0x01); // set system pll + break; + case FRAMESIZE_FHD: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x05); //1920x1080 + ret += write_reg(sensor->slv_addr, SYSTEM_CLK_DIV_REG, 0x02); // set system clk + ret += write_reg(sensor->slv_addr, SYSTEM_PLL_DIV_REG, 0x01); // set system pll + break; + case FRAMESIZE_5MP: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x06); //2592x1944 + break; + case FRAMESIZE_96X96: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x07); //96x96 + break; + case FRAMESIZE_128X128: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x08); //128x128 + break; + case FRAMESIZE_320X320: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x09); //320x320 + break; + default: + ESP_LOGW(TAG, "unsupport framesize"); + ret = -1; + break; + } + 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, IMAGE_MIRROR_REG, 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, IMAGE_FLIP_REG, enable); + if (ret == 0) { + ESP_LOGD(TAG, "Set v-flip to: %d", enable); + } + return ret; +} +static int set_quality(sensor_t *sensor, int qs) +{ + int ret = 0; + ret = write_reg(sensor->slv_addr, IMAGE_QUALITY_REG, qs); + if (ret == 0) { + sensor->status.quality = qs; + ESP_LOGD(TAG, "Set quality to: %d", qs); + } + return ret; +} + + +static int set_brightness(sensor_t *sensor, int level) +{ + int ret = 0; + if(level < 0) { + level = 0; + } else if(level > 8) { + level = 8; + } + ret = write_reg(sensor->slv_addr, BRIGHTNESS_REG, 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; + if(level < 0) { + level = 0; + } else if(level > 6) { + level = 6; + } + ret = write_reg(sensor->slv_addr, CONTRAST_REG, level); + if (ret == 0) { + ESP_LOGD(TAG, "Set contrast to: %d", level); + sensor->status.contrast = level; + } + return ret; +} + +static int set_saturation (sensor_t *sensor, int level) +{ + int ret = 0; + if(level < 0) { + level = 0; + } else if(level > 6) { + level = 6; + } + ret = write_reg(sensor->slv_addr, SATURATION_REG, level); + if (ret == 0) { + ESP_LOGD(TAG, "Set saturation to: %d", level); + sensor->status.saturation = level; + } + return ret; +} +static int set_agc_mode (sensor_t *sensor, int enable) +{ + int ret = 0; + ret = write_reg(sensor->slv_addr, AGC_MODE_REG, enable); + if (ret == 0) { + ESP_LOGD(TAG, "Set agc mode to: %d", enable); + sensor->status.aec = enable; + } + return ret; +} + + +static int set_wb_mode (sensor_t *sensor, int mode) +{ + int ret = 0; + if(mode < 0) { + mode = 0; + } else if(mode > 5) { + mode = 5; + } + ret = write_reg(sensor->slv_addr, AWB_MODE_REG, mode); + if (ret == 0) { + ESP_LOGD(TAG, "Set wb_mode to: %d", mode); + sensor->status.wb_mode = mode; + } + return ret; + +} + +static int set_special_effect (sensor_t *sensor, int effect) +{ + + int ret = 0; + if(effect < 0) { + effect = 0; + } else if(effect > 6) { + effect = 6; + } + ret = write_reg(sensor->slv_addr, SPECIAL_REG, effect); + if (ret == 0) { + ESP_LOGD(TAG, "Set special_effect to: %d", effect); + sensor->status.special_effect = effect; + } + return ret; + +} + + +static int analog_gain (sensor_t *sensor, int val) +{ + + int ret = 0; + ret = write_reg(sensor->slv_addr, MANUAL_AGC_REG, val); + if (ret == 0) { + ESP_LOGD(TAG, "Set analog gain to: %d", val); + } + return ret; + +} + + +static int exposure_line (sensor_t *sensor, int val) +{ + + int ret = 0; + ret = write_reg(sensor->slv_addr, MANUAL_EXP_H_REG, val>>8); + ret += write_reg(sensor->slv_addr, MANUAL_EXP_L_REG, val>>8); + if (ret == 0) { + ESP_LOGD(TAG, "Set exposure_line to: %d", val); + } + return ret; + +} + + +static int init_status(sensor_t *sensor) +{ + 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 = 0; + sensor->status.vflip = 0; + 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 esp32_camera_mega_ccm_detect(int slv_addr, sensor_id_t *id) +{ + if (MEGA_CCM_SCCB_ADDR == slv_addr) { + uint8_t h = read_reg(slv_addr, SENSOR_ID_HIGH); + uint8_t l = read_reg(slv_addr, SENSOR_ID_LOW); + uint16_t PID = (h<<8) | l; + if (MEGA_CCM_PID == PID) { + id->PID = PID; + return PID; + } else { + ESP_LOGI(TAG, "Mismatch PID=0x%x", PID); + } + } + return 0; +} + +int esp32_camera_mega_ccm_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_brightness; + sensor->set_saturation = set_saturation; + sensor->set_sharpness = set_dummy; + sensor->set_denoise = set_dummy; + sensor->set_gainceiling = set_gainceiling_dummy; + sensor->set_quality = set_quality; + sensor->set_colorbar = set_dummy; + 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_agc_mode; + sensor->set_awb_gain = set_dummy; + sensor->set_agc_gain = analog_gain; + sensor->set_aec_value = exposure_line; + + sensor->set_special_effect = set_special_effect; + sensor->set_wb_mode = set_wb_mode; + 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 = NULL; + sensor->set_reg = NULL; + sensor->set_res_raw = NULL; + sensor->set_pll = NULL; + sensor->set_xclk = NULL; + + ESP_LOGD(TAG, "MEGA_CCM Attached"); + return 0; +} diff --git a/sensors/nt99141.c b/sensors/nt99141.c index 86a8b8a0b5..b440cb3bae 100644 --- a/sensors/nt99141.c +++ b/sensors/nt99141.c @@ -934,7 +934,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int nt99141_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_nt99141_detect(int slv_addr, sensor_id_t *id) { if (NT99141_SCCB_ADDR == slv_addr) { SCCB_Write16(slv_addr, 0x3008, 0x01);//bank sensor @@ -981,7 +981,7 @@ static int init_status(sensor_t *sensor) return 0; } -int nt99141_init(sensor_t *sensor) +int esp32_camera_nt99141_init(sensor_t *sensor) { sensor->reset = reset; sensor->set_pixformat = set_pixformat; diff --git a/sensors/ov2640.c b/sensors/ov2640.c index 7e3d77174d..d35aceee31 100755 --- a/sensors/ov2640.c +++ b/sensors/ov2640.c @@ -545,7 +545,7 @@ static int init_status(sensor_t *sensor){ return 0; } -int ov2640_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_ov2640_detect(int slv_addr, sensor_id_t *id) { if (OV2640_SCCB_ADDR == slv_addr) { SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor @@ -563,7 +563,7 @@ int ov2640_detect(int slv_addr, sensor_id_t *id) return 0; } -int ov2640_init(sensor_t *sensor) +int esp32_camera_ov2640_init(sensor_t *sensor) { sensor->reset = reset; sensor->init_status = init_status; diff --git a/sensors/ov3660.c b/sensors/ov3660.c index b9ebdba3ba..9bbcd6f8d7 100755 --- a/sensors/ov3660.c +++ b/sensors/ov3660.c @@ -996,7 +996,7 @@ static int init_status(sensor_t *sensor) return 0; } -int ov3660_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_ov3660_detect(int slv_addr, sensor_id_t *id) { if (OV3660_SCCB_ADDR == slv_addr) { uint8_t h = SCCB_Read16(slv_addr, 0x300A); @@ -1012,7 +1012,7 @@ int ov3660_detect(int slv_addr, sensor_id_t *id) return 0; } -int ov3660_init(sensor_t *sensor) +int esp32_camera_ov3660_init(sensor_t *sensor) { sensor->reset = reset; sensor->set_pixformat = set_pixformat; diff --git a/sensors/ov5640.c b/sensors/ov5640.c index a32b374f54..9186571057 100755 --- a/sensors/ov5640.c +++ b/sensors/ov5640.c @@ -384,9 +384,9 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) framesize_t old_framesize = sensor->status.framesize; sensor->status.framesize = framesize; - if(framesize > FRAMESIZE_QSXGA){ - ESP_LOGE(TAG, "Invalid framesize: %u", framesize); - return -1; + if (framesize > FRAMESIZE_5MP) { + ESP_LOGW(TAG, "Invalid framesize: %u", framesize); + framesize = FRAMESIZE_5MP; } uint16_t w = resolution[framesize].width; uint16_t h = resolution[framesize].height; @@ -1073,7 +1073,7 @@ static int init_status(sensor_t *sensor) return 0; } -int ov5640_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_ov5640_detect(int slv_addr, sensor_id_t *id) { if (OV5640_SCCB_ADDR == slv_addr) { uint8_t h = SCCB_Read16(slv_addr, 0x300A); @@ -1089,7 +1089,7 @@ int ov5640_detect(int slv_addr, sensor_id_t *id) return 0; } -int ov5640_init(sensor_t *sensor) +int esp32_camera_ov5640_init(sensor_t *sensor) { sensor->reset = reset; sensor->set_pixformat = set_pixformat; diff --git a/sensors/ov7670.c b/sensors/ov7670.c index ee64de277d..043df65cbd 100644 --- a/sensors/ov7670.c +++ b/sensors/ov7670.c @@ -173,7 +173,7 @@ int ret = 0; */ static int ov7670_frame_control(sensor_t *sensor, int hstart, int hstop, int vstart, int vstop) { -struct regval_list frame[7]; + struct regval_list frame[7]; frame[0].reg_num = HSTART; frame[0].value = (hstart >> 3); @@ -194,8 +194,8 @@ struct regval_list frame[7]; frame[5].value = (((vstop & 0x02) << 2) | (vstart & 0x02)); /* End mark */ - frame[5].reg_num = 0xFF; - frame[5].value = 0xFF; + frame[6].reg_num = 0xFF; + frame[6].value = 0xFF; return ov7670_write_array(sensor, frame); } @@ -277,7 +277,7 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) case FRAMESIZE_QQVGA: if( (ret = ov7670_write_array(sensor, ov7670_qqvga)) == 0 ) { /* These values from Omnivision */ - ret = ov7670_frame_control(sensor, 158, 14, 10, 490); + ret = ov7670_frame_control(sensor, 158, 14, 12, 490); } break; @@ -393,7 +393,7 @@ 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) +int esp32_camera_ov7670_detect(int slv_addr, sensor_id_t *id) { if (OV7670_SCCB_ADDR == slv_addr) { SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor @@ -411,7 +411,7 @@ int ov7670_detect(int slv_addr, sensor_id_t *id) return 0; } -int ov7670_init(sensor_t *sensor) +int esp32_camera_ov7670_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; diff --git a/sensors/ov7725.c b/sensors/ov7725.c index 9418a82f3f..dd0ea62fcb 100755 --- a/sensors/ov7725.c +++ b/sensors/ov7725.c @@ -502,7 +502,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int ov7725_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_ov7725_detect(int slv_addr, sensor_id_t *id) { if (OV7725_SCCB_ADDR == slv_addr) { SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor @@ -520,7 +520,7 @@ int ov7725_detect(int slv_addr, sensor_id_t *id) return 0; } -int ov7725_init(sensor_t *sensor) +int esp32_camera_ov7725_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; diff --git a/sensors/private_include/bf20a6.h b/sensors/private_include/bf20a6.h index 8c925eb5e5..743138be7b 100644 --- a/sensors/private_include/bf20a6.h +++ b/sensors/private_include/bf20a6.h @@ -13,7 +13,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int bf20a6_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_bf20a6_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -22,6 +22,6 @@ int bf20a6_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int bf20a6_init(sensor_t *sensor); +int esp32_camera_bf20a6_init(sensor_t *sensor); #endif // __BF20A6_H__ diff --git a/sensors/private_include/bf3005.h b/sensors/private_include/bf3005.h index 3d1bfdf00d..bd562a7050 100644 --- a/sensors/private_include/bf3005.h +++ b/sensors/private_include/bf3005.h @@ -19,7 +19,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int bf3005_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_bf3005_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -28,6 +28,6 @@ int bf3005_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int bf3005_init(sensor_t *sensor); +int esp32_camera_bf3005_init(sensor_t *sensor); #endif // __BF3005_H__ \ No newline at end of file diff --git a/sensors/private_include/gc0308.h b/sensors/private_include/gc0308.h index edffca1e00..eae260320a 100644 --- a/sensors/private_include/gc0308.h +++ b/sensors/private_include/gc0308.h @@ -15,7 +15,7 @@ extern "C" { * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int gc0308_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_gc0308_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -24,7 +24,7 @@ int gc0308_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int gc0308_init(sensor_t *sensor); +int esp32_camera_gc0308_init(sensor_t *sensor); #ifdef __cplusplus } diff --git a/sensors/private_include/gc032a.h b/sensors/private_include/gc032a.h index 7679f07089..8955ed4731 100644 --- a/sensors/private_include/gc032a.h +++ b/sensors/private_include/gc032a.h @@ -17,7 +17,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int gc032a_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_gc032a_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -26,6 +26,6 @@ int gc032a_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int gc032a_init(sensor_t *sensor); +int esp32_camera_gc032a_init(sensor_t *sensor); #endif // __GC032A_H__ diff --git a/sensors/private_include/gc2145.h b/sensors/private_include/gc2145.h index 6c5b60f703..db649f9198 100644 --- a/sensors/private_include/gc2145.h +++ b/sensors/private_include/gc2145.h @@ -13,7 +13,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int gc2145_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_gc2145_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -22,6 +22,6 @@ int gc2145_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int gc2145_init(sensor_t *sensor); +int esp32_camera_gc2145_init(sensor_t *sensor); #endif // __GC2145_H__ diff --git a/sensors/private_include/hm0360.h b/sensors/private_include/hm0360.h new file mode 100755 index 0000000000..2163cbe467 --- /dev/null +++ b/sensors/private_include/hm0360.h @@ -0,0 +1,27 @@ +/* + * HM0360 driver. + */ +#ifndef __HM0360_H__ +#define __HM0360_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 esp32_camera_hm0360_detect(int slv_addr, sensor_id_t *id); + +/** + * @brief initialize sensor function pointers + * + * @param sensor pointer of sensor + * @return + * Always 0 + */ +int esp32_camera_hm0360_init(sensor_t *sensor); + +#endif // __HM1055_H__ diff --git a/sensors/private_include/hm0360_regs.h b/sensors/private_include/hm0360_regs.h new file mode 100755 index 0000000000..9017484889 --- /dev/null +++ b/sensors/private_include/hm0360_regs.h @@ -0,0 +1,150 @@ +/* + * HM1055 register definitions. + */ +#ifndef __REG_REGS_H__ +#define __REG_REGS_H__ + +// Sensor ID +#define MODEL_ID_H 0x0000 +#define MODEL_ID_L 0x0001 +#define SILICON_REV 0x0002 +#define FRAME_COUNT_H 0x0005 +#define FRAME_COUNT_L 0x0006 +#define PIXEL_ODERDER 0x0007 + +// Snesor mode control +#define MODEL_SELECT 0x0100 +#define IMAGE_ORIENTATION 0x0101 +#define EMBEDDED_LINE_EN 0x0102 +#define SW_RESET 0x0103 +#define COMMAND_UPDATE 0x0104 + +// Sensor exposure gain control +#define INTEGRATION_H 0x0202 +#define INTEGRATION_L 0x0203 +#define ANALOG_GAIN 0x0205 +#define DIGITAL_GAIN_H 0x020E +#define DIGITAL_GAIN_L 0x020F + +// clock control +#define PLL1CFG 0x0300 +#define PLL2CFG 0x0301 +#define PLL3CFG 0x0302 + +// frame timming control +#define FRAME_LENGTH_LINES_H 0x0340 +#define FRAME_LENGTH_LINES_L 0x0341 +#define LINE_LENGTH_PCK_H 0x0342 +#define LINE_LENGTH_PCK_L 0x0343 + +// monochrome programming +#define MONO_MODE 0x0370 +#define MONO_MODE_ISP 0x0371 +#define MONO_MODE_SEL 0x0372 + +// sub-sampling / binning control +#define H_SUB 0x0380 +#define V_SUB 0x0381 +#define BINNING_MODE 0x0382 + +// test pattern control +#define TEST_PATTERN_MODE 0x0601 +#define TEST_DATA_BLUE_H 0x0602 +#define TEST_DATA_BLUE_L 0x0603 +#define TEST_DATA_GB_H 0x0604 +#define TEST_DATA_GB_L 0x0605 +#define TEST_DATA_GR_H 0x0605 +#define TEST_DATA_GR_L 0x0606 +#define TEST_DATA_RED_H 0x0608 +#define TEST_DATA_RED_L 0x0609 + +// black level control +#define BLC_TGT 0x1004 +#define BLC2_TGT 0x1009 + +// monochrome programming +#define MONO_CTRL 0x100A + +// VSYNC / HSYNC / pixel shift +#define OPFM_CTRL 0x1014 + +// Tone mapping registers +#define CMPRS_CTRL 0x102F +#define CMPRS_01 0x1030 +#define CMPRS_02 0x1031 +#define CMPRS_03 0x1032 +#define CMPRS_04 0x1033 +#define CMPRS_05 0x1034 +#define CMPRS_06 0x1035 +#define CMPRS_07 0x1036 +#define CMPRS_08 0x1037 +#define CMPRS_09 0x1038 +#define CMPRS_10 0x1039 +#define CMPRS_11 0x103A +#define CMPRS_12 0x103B +#define CMPRS_13 0x103C +#define CMPRS_14 0x103D +#define CMPRS_15 0x103E +#define CMPRS_16 0x103F + +// automatic exposure programming +#define AE_CTRL 0x2000 +#define AE_CTRL1 0x2001 +#define CNT_ORG_H_H 0x2002 +#define CNT_ORG_H_L 0x2003 +#define CNT_ORG_V_H 0x2004 +#define CNT_ORG_V_L 0x2005 +#define CNT_ST_H_H 0x2006 +#define CNT_ST_H_L 0x2007 +#define CNT_ST_V_H 0x2008 +#define CNT_ST_V_L 0x2009 +#define CTRL_PG_SKIPCNT 0x200A +#define BV_WIN_WEIGHT_EN 0x200D +#define MAX_INTG_H 0x2029 +#define MAX_INTG_L 0x202A +#define MAX_AGAIN 0x202B +#define MAX_DGAIN_H 0x202C +#define MAX_DGAIN_L 0x202D +#define MIN_INTG 0x202E +#define MIN_AGAIN 0x202F +#define MIN_DGAIN 0x2030 +#define T_DAMPING 0x2031 +#define N_DAMPING 0x2032 +#define ALC_TH 0x2033 +#define AE_TARGET_MEAN 0x2034 +#define AE_MIN_MEAN 0x2035 +#define AE_TARGET_ZONE 0x2036 +#define CONVERGE_IN_TH 0x2037 +#define CONVERGE_OUT_TH 0x2038 + +// Interrupt control +#define PULSE_MODE 0x2061 +#define PULSE_TH_H 0x2062 +#define PULSE_TH_L 0x2063 +#define INT_INDIC 0x2064 +#define INT_CLEAR 0x2065 + +// Motion detection control +#define MD_CTRL 0x2080 +#define ROI_START_END_V 0x2081 +#define ROI_START_END_H 0x2082 +#define MD_TH_MIN 0x2083 +#define MD_TH_STR_L 0x2084 +#define MD_TH_STR_H 0x2085 +#define MD_LIGHT_COEF 0x2099 +#define MD_BLOCK_NUM_TH 0x209B +#define MD_LATENCY 0x209C +#define MD_LATENCY_TH 0x209D +#define MD_CTRL1 0x209E + +// Context switch control registers +#define PMU_CFG_3 0x3024 +#define PMU_CFG_4 0x3025 + +// Operation mode control +#define WIN_MODE 0x3030 + +// IO and clock control +#define PAD_REGISTER_07 0x3112 + +#endif //__REG_REGS_H__ diff --git a/sensors/private_include/hm0360_settings.h b/sensors/private_include/hm0360_settings.h new file mode 100755 index 0000000000..942e9b17da --- /dev/null +++ b/sensors/private_include/hm0360_settings.h @@ -0,0 +1,549 @@ + +#include + +#define REG_DLY 0xffff +#define REGLIST_TAIL 0x0000 + +static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { + {0x0103, 0x00}, + {REG_DLY, 100}, + {0x3035, 0x01}, + {0x3126, 0x03}, + {0x3128, 0x57}, + {0x3142, 0x9F}, + {0x311C, 0x10}, + {0x3115, 0x42}, + {0x3116, 0x10}, + {0x3117, 0x0A}, + {0x310B, 0x10}, + {0x3031, 0x01}, + {0x312E, 0x00}, + {0x30D7, 0x00}, + {0x30DC, 0x00}, + {0x30E1, 0x00}, + {0x30E6, 0x00}, + {0x30EB, 0x00}, + {0x30F0, 0x00}, + {0x30F5, 0x00}, + {0x30FA, 0x00}, + {0x30FF, 0x00}, + {0x3104, 0x00}, + {0x30D8, 0xA7}, + {0x30DD, 0x27}, + {0x30E2, 0x27}, + {0x30E7, 0x27}, + {0x30EC, 0x27}, + {0x30F1, 0xA7}, + {0x30F6, 0x27}, + {0x30FB, 0x27}, + {0x3100, 0x27}, + {0x3105, 0x27}, + {0x30D9, 0x00}, // 220215 + {0x30DE, 0x05}, + {0x30E3, 0x05}, + {0x30E8, 0x05}, + {0x30ED, 0x05}, + {0x30F2, 0x00}, // 220215 + {0x30F7, 0x05}, + {0x30FC, 0x05}, + {0x3101, 0x05}, + {0x3106, 0x05}, + {0x30C4, 0x10}, + {0x30C5, 0x01}, + {0x30C6, 0x2F}, // 220215 + {0x30CB, 0xFF}, + {0x30CC, 0xFF}, + {0x30CD, 0x7F}, + {0x30CE, 0x7F}, + {0x30D3, 0x01}, + {0x30D4, 0xFF}, + {0x311F, 0x0E}, + {0x3120, 0x0D}, + {0x3121, 0x0F}, + {0x3122, 0x00}, + {0x3147, 0x18}, + {0x314B, 0x01}, + {0x3149, 0x28}, + {0x3150, 0x50}, + {0x3152, 0x00}, + {0x3156, 0x2C}, + {0x3163, 0x1F}, + {0x3165, 0x7F}, + {0x312B, 0x41}, + {0x3113, 0xA0}, + {0x3114, 0x67}, + {0x317D, 0x02}, + {0x3160, 0x1F}, + {0x318C, 0x00}, + {0x315C, 0xE0}, + {0x311E, 0x0F}, + {0x30D5, 0x00}, + {0x30DA, 0x01}, + {0x30DF, 0x07}, + {0x30E4, 0x47}, + {0x30E9, 0x87}, + {0x30FD, 0x47}, + {0x3102, 0x87}, + {0x311D, 0x06}, + {0x3141, 0x2A}, + {0x315A, 0x0A}, + {0x30D6, 0x40}, + {0x30DB, 0x40}, + {0x30E0, 0x40}, + {0x30E5, 0x30}, + {0x30EA, 0x30}, + {0x30EF, 0x40}, + {0x30F4, 0x40}, + {0x30F9, 0x40}, + {0x30FE, 0x30}, + {0x3103, 0x30}, + {0x3164, 0x7F}, + {0x282A, 0x0F}, + {0x282E, 0x2F}, + {0x282B, 0x08}, + {0x312A, 0x11}, + {0x1000, 0x43}, + {0x1001, 0x80}, + {0x0350, 0xE0}, + {0x101D, 0xCF}, + {0x1021, 0x5D}, + + // setA VGA + {0x3500, 0x74}, + {0x3501, 0x0A}, + {0x3502, 0x77}, + {0x3503, 0x04}, + {0x3504, 0x14}, + {0x3505, 0x03}, + {0x3506, 0x00}, + {0x3507, 0x00}, + {0x3508, 0x00}, + {0x3509, 0x00}, + {0x350A, 0xFF}, + {0x350B, 0x00}, + {0x350D, 0x01}, + {0x350C, 0x00}, + {0x350F, 0x00}, + {0x3510, 0x01}, + {0x3513, 0x00}, + {0x351C, 0x00}, + {0x3514, 0x00}, + {0x3515, 0x01}, + {0x3516, 0x00}, + {0x3517, 0x02}, + {0x3518, 0x00}, + {0x3519, 0x7F}, + {0x351A, 0x00}, + {0x351B, 0x5F}, + {0x351D, 0x04}, + {0x351E, 0x10}, + {0x352A, 0x01}, + {0x352B, 0x04}, + {0x352C, 0x01}, + {0x352D, 0x38}, + {0x354B, 0x21}, + {0x354C, 0x01}, + {0x354D, 0xE0}, + {0x354E, 0xF0}, + {0x354F, 0x10}, + {0x3550, 0x10}, + {0x3551, 0x10}, + {0x3552, 0x20}, + {0x3553, 0x10}, + {0x3554, 0x01}, + {0x3555, 0x06}, + {0x3556, 0x0C}, + {0x3557, 0x12}, + {0x3558, 0x1C}, + {0x3559, 0x30}, + {0x3549, 0x04}, + {0x354A, 0x35}, + + // setB QVGA + {0x355A, 0x74}, + {0x355B, 0x0A}, + {0x355C, 0x77}, + {0x355D, 0x04}, + {0x355E, 0x14}, + {0x355F, 0x03}, + {0x3560, 0x00}, + {0x3561, 0x01}, + {0x3562, 0x01}, + {0x3563, 0x00}, + {0x3564, 0xFF}, + {0x3565, 0x00}, + {0x3567, 0x01}, + {0x3566, 0x00}, + {0x3569, 0x00}, + {0x356A, 0x01}, + {0x356D, 0x00}, + {0x3576, 0x00}, + {0x356E, 0x00}, + {0x356F, 0x01}, + {0x3570, 0x00}, + {0x3571, 0x02}, + {0x3572, 0x00}, + {0x3573, 0x3F}, + {0x3574, 0x00}, + {0x3575, 0x2F}, + {0x3577, 0x04}, + {0x3578, 0x10}, + {0x3584, 0x01}, + {0x3585, 0x04}, + {0x3586, 0x01}, + {0x3587, 0x38}, + {0x3588, 0x02}, + {0x3589, 0x12}, + {0x358A, 0x04}, + {0x358B, 0x24}, + {0x358C, 0x06}, + {0x358D, 0x36}, + {0x35A5, 0x21}, + {0x35A6, 0x01}, + {0x35A7, 0xE0}, + {0x35A8, 0xF0}, + {0x35A9, 0x10}, + {0x35AA, 0x10}, + {0x35AB, 0x10}, + {0x35AC, 0x20}, + {0x35AD, 0x10}, + {0x35AE, 0x01}, + {0x35AF, 0x06}, + {0x35B0, 0x0C}, + {0x35B1, 0x12}, + {0x35B2, 0x1C}, + {0x35B3, 0x30}, + {0x35A3, 0x02}, + {0x35A4, 0x03}, + + // AE_tuning + {0x3512, 0x3F}, + {0x351F, 0x04}, + {0x3520, 0x03}, + {0x3521, 0x00}, + {0x3523, 0x60}, + {0x3524, 0x08}, + {0x3525, 0x19}, + {0x3526, 0x08}, + {0x3527, 0x10}, + {0x356C, 0x3F}, + {0x3579, 0x04}, + {0x357A, 0x03}, + {0x357B, 0x00}, + {0x357D, 0x60}, + {0x357E, 0x08}, + {0x357F, 0x19}, + {0x3580, 0x08}, + {0x3581, 0x10}, + {0x2048, 0x00}, + {0x2049, 0x10}, + {0x204A, 0x40}, + {0x204E, 0x00}, + {0x204F, 0x38}, + {0x2050, 0xE0}, + {0x204B, 0x00}, + {0x204C, 0x08}, + {0x204D, 0x20}, + {0x2051, 0x00}, + {0x2052, 0x1C}, + {0x2053, 0x70}, + {0x2054, 0x00}, + {0x2055, 0x1A}, + {0x2056, 0xC0}, + {0x2057, 0x00}, + {0x2058, 0x06}, + {0x2059, 0xB0}, + + // MD tuning + {0x2080, 0x41}, + {0x2083, 0x01}, + {0x208D, 0x02}, + {0x208E, 0x08}, + {0x208F, 0x0D}, + {0x2090, 0x14}, + {0x2091, 0x1D}, + {0x2092, 0x30}, + {0x2093, 0x08}, + {0x2094, 0x0A}, + {0x2095, 0x0F}, + {0x2096, 0x14}, + {0x2097, 0x18}, + {0x2098, 0x20}, + {0x2099, 0x10}, + {0x209A, 0x00}, + {0x209B, 0x01}, + {0x209C, 0x01}, + {0x209D, 0x11}, + {0x209E, 0x06}, + + // Tone mapping + {0x1030, 0x04}, + {0x1031, 0x08}, + {0x1032, 0x10}, + {0x1033, 0x18}, + {0x1034, 0x20}, + {0x1035, 0x28}, + {0x1036, 0x30}, + {0x1037, 0x38}, + {0x1038, 0x40}, + {0x1039, 0x50}, + {0x103A, 0x60}, + {0x103B, 0x70}, + {0x103C, 0x80}, + {0x103D, 0xA0}, + {0x103E, 0xC0}, + {0x103F, 0xE0}, + + // RESERVED + {0x35B4, 0x74}, + {0x35B5, 0x0A}, + {0x35B6, 0x77}, + {0x35B7, 0x00}, + {0x35B8, 0x94}, + {0x35B9, 0x03}, + {0x35BA, 0x00}, + {0x35BB, 0x03}, + {0x35BD, 0x00}, + {0x35BE, 0xFF}, + {0x35BF, 0x00}, + {0x35C1, 0x01}, + {0x35C0, 0x01}, + {0x35C3, 0x00}, + {0x35C4, 0x00}, + {0x35C6, 0x3F}, + {0x35C7, 0x00}, + {0x35D0, 0x00}, + {0x35D3, 0x04}, + {0x35D7, 0x18}, + {0x35D8, 0x01}, + {0x35D9, 0x20}, + {0x35DA, 0x08}, + {0x35DB, 0x14}, + {0x35DC, 0x70}, + {0x35C8, 0x00}, + {0x35C9, 0x01}, + {0x35CA, 0x00}, + {0x35CB, 0x02}, + {0x35CC, 0x00}, + {0x35CD, 0x0F}, + {0x35CE, 0x00}, + {0x35CF, 0x0B}, + {0x35DE, 0x00}, + {0x35DF, 0x01}, + {0x35FD, 0x00}, + {0x35FE, 0x5E}, + + // RESERVED + {0x3024, 0x00}, + {0x3025, 0x12}, + {0x3026, 0x03}, + {0x3027, 0x81}, + {0x3028, 0x01}, + {0x3029, 0x00}, + {0x302A, 0x30}, + + // Interrupt + {0x2061, 0x00}, + {0x2062, 0x00}, + {0x2063, 0xC8}, + + // Parallel I/F + {0x1014, 0x00}, + {0x102F, 0x08}, + {0x309E, 0x05}, + {0x309F, 0x02}, + {0x30A0, 0x02}, + {0x30A1, 0x00}, + {0x30A2, 0x08}, + {0x30A3, 0x00}, + {0x30A4, 0x20}, + {0x30A5, 0x04}, + {0x30A6, 0x02}, + {0x30A7, 0x02}, + {0x30A8, 0x01}, + {0x30B0, 0x03}, + {0x3112, 0x04}, + {0x311A, 0x30}, //31:bypass internal ldo 30:internal LDO + + // MIPI + {0x2800, 0x00}, + + // Enable BPC} + {0x0370, 0x00}, + {0x0371, 0x00}, + {0x0372, 0x01}, + {0x100A, 0x05}, + {0x2590, 0x01}, + {0x0104, 0x01}, + {0x0100, 0x01}, + + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA[][2] = { + {0x0006, 0x10}, + {0x000D, 0x00}, + {0x000E, 0x00}, + {0x0122, 0xEB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XC1}, + {0x05E1, 0x00}, + {0x05E2, 0xC1}, + {0x05E3, 0x00}, + {0x05E4, 0x03}, + {0x05E5, 0x00}, + {0x05E6, 0x82}, + {0x05E7, 0x02}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xE3}, + {0x05EB, 0x01}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA[][2] = { + {0x355A, 0x74}, // vt_sys_d + {0x355B, 0x0A}, // op_sys_d + {0x355C, 0x77}, // mclk_div_d + {0x355D, 0x01}, + {0x355E, 0x1C}, + {0x355F, 0x03}, + {0x3560, 0x00}, + {0x3561, 0x01}, + {0x3562, 0x01}, + {0x3563, 0x00}, + {0x3564, 0xFF}, + {0x3565, 0x00}, + {0x3567, 0x01}, + {0x3566, 0x00}, + {0x3569, 0x00}, + {0x356A, 0x01}, + {0x356C, 0x7F}, + {0x356D, 0x00}, + {0x3576, 0x00}, + {0x3579, 0x03}, // Max Again + {0x356E, 0x00}, // AE cnt org H HB + {0x356F, 0x01}, // AE cnt org H LB + {0x3570, 0x00}, // AE cnt org V HB + {0x3571, 0x02}, // AE cnt org V LB + {0x3572, 0x00}, // AE cnt st H HB + {0x3573, 0x3F}, // AE cnt st H LB + {0x3574, 0x00}, // AE cnt st V HB + {0x3575, 0x2F}, // AE cnt st V LB + {0x3577, 0x04}, + {0x3578, 0x24}, + {0x3584, 0x01}, + {0x3585, 0x04}, + {0x3586, 0x01}, + {0x3587, 0x38}, + {0x3588, 0x02}, // FR stage 1 H + {0x3589, 0x12}, // FR stage 1 L + {0x358A, 0x04}, // FR stage 2 H + {0x358B, 0x24}, // FR stage 2 L + {0x358C, 0x06}, // FR stage 3 H + {0x358D, 0x36}, // FR stage 3 L + {0x35A5, 0x21}, // [7:1]MD_light_coef [0] MD enable + {0x35A6, 0x01}, // MD_block_num_th + {0x35A7, 0xE0}, // [7:4]md_roi_end_v [3:0]md_roi_start_v + {0x35A8, 0xF0}, // [7:4]md_roi_end_h [3:0]md_roi_start_h + {0x35A9, 0x10}, // [6:0] md_th_str_HCG + {0x35AA, 0x10}, // [5:0] md_th_str_LCG + {0x35AB, 0x10}, // [5:0] md_th_str_HDR + {0x35AC, 0x20}, // md_flick_skip_th_adj_N + {0x35AD, 0x10}, // md_flick_skip_th_adj_P + {0x35AE, 0x01}, + {0x35AF, 0x06}, + {0x35B0, 0x0C}, + {0x35B1, 0x12}, + {0x35BC, 0x1C}, + {0x35B3, 0x30}, + {0x35A3, 0x02}, // full trigger H + {0x35A4, 0x03}, // full trigger L + + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_240X240[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XBE}, + {0x05E1, 0x00}, + {0x05E2, 0xBE}, + {0x05E3, 0x00}, + {0x05E4, 0x62}, + {0x05E5, 0x00}, + {0x05E6, 0x51}, + {0x05E7, 0x01}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xF3}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QQVGA[][2] = { + {0x355A, 0x74}, // vt_sys_d + {0x355B, 0x0A}, // op_sys_d + {0x355C, 0x77}, // mclk_div_d + {0x355D, 0x00}, + {0x355E, 0x8E}, + {0x355F, 0x03}, + {0x3560, 0x00}, + {0x3561, 0x02}, + {0x3562, 0x02}, + {0x3563, 0x00}, + {0x3564, 0xFF}, + {0x3565, 0x00}, + {0x3567, 0x01}, + {0x3566, 0x00}, + {0x3569, 0x00}, + {0x356A, 0x01}, + {0x356C, 0x7F}, + {0x356D, 0x00}, + {0x3576, 0x00}, + {0x3579, 0x03}, // Max Again + {0x356E, 0x00}, // AE cnt org H HB + {0x356F, 0x01}, // AE cnt org H LB + {0x3570, 0x00}, // AE cnt org V HB + {0x3571, 0x02}, // AE cnt org V LB + {0x3572, 0x00}, // AE cnt st H HB + {0x3573, 0x1F}, // AE cnt st H LB + {0x3574, 0x00}, // AE cnt st V HB + {0x3575, 0x17}, // AE cnt st V LB + {0x3577, 0x04}, + {0x3578, 0x24}, + {0x3584, 0x01}, + {0x3585, 0x04}, + {0x3586, 0x01}, + {0x3587, 0x38}, + {0x3588, 0x02}, // FR stage 1 H + {0x3589, 0x12}, // FR stage 1 L + {0x358A, 0x04}, // FR stage 2 H + {0x358B, 0x24}, // FR stage 2 L + {0x358C, 0x06}, // FR stage 3 H + {0x358D, 0x36}, // FR stage 3 L + {0x35A5, 0x21}, // [7:1]MD_light_coef [0] MD enable + {0x35A6, 0x01}, // MD_block_num_th + {0x35A7, 0xD0}, // [7:4]md_roi_end_v [3:0]md_roi_start_v + {0x35A8, 0xF0}, // [7:4]md_roi_end_h [3:0]md_roi_start_h + {0x35A9, 0x10}, // [6:0] md_th_str_HCG + {0x35AA, 0x10}, // [5:0] md_th_str_LCG + {0x35AB, 0x10}, // [5:0] md_th_str_HDR + {0x35AC, 0x20}, // md_flick_skip_th_adj_N + {0x35AD, 0x10}, // md_flick_skip_th_adj_P + {0x35AE, 0x01}, + {0x35AF, 0x06}, + {0x35B0, 0x0C}, + {0x35B1, 0x12}, + {0x35B2, 0x1C}, + {0x35B3, 0x30}, + {0x35A3, 0x00}, // full trigger H + {0x35A4, 0xEA}, // full trigger L + + {REGLIST_TAIL, 0x00}, +}; diff --git a/sensors/private_include/hm1055.h b/sensors/private_include/hm1055.h new file mode 100755 index 0000000000..4ddf1f8171 --- /dev/null +++ b/sensors/private_include/hm1055.h @@ -0,0 +1,27 @@ +/* + * HM1055 driver. + */ +#ifndef __HM1055_H__ +#define __HM1055_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 esp32_camera_hm1055_detect(int slv_addr, sensor_id_t *id); + +/** + * @brief initialize sensor function pointers + * + * @param sensor pointer of sensor + * @return + * Always 0 + */ +int esp32_camera_hm1055_init(sensor_t *sensor); + +#endif // __HM1055_H__ diff --git a/sensors/private_include/hm1055_regs.h b/sensors/private_include/hm1055_regs.h new file mode 100755 index 0000000000..da83db0993 --- /dev/null +++ b/sensors/private_include/hm1055_regs.h @@ -0,0 +1,120 @@ +/* + * HM1055 register definitions. + */ +#ifndef __REG_REGS_H__ +#define __REG_REGS_H__ + +// Imager Configuration +#define CMU 0x0000 +#define IDH 0x0001 +#define IDL 0x0002 +#define VID 0x0003 +#define PWDCTRL 0x0004 +#define TGRDCFG 0x0005 +#define RDCFG 0x0006 +#define VREAD 0x000D +#define HREAD 0x000E +#define IMG_CFG 0x000F + +// Imager Operation +#define BLNKRH 0x0010 +#define BLNKRL 0x0011 +#define BLNKCCLK 0x0012 +#define BLNKC 0x0013 +#define INTGH 0x0015 +#define INTGL 0x0016 +#define AGAIN 0x0018 +#define DGAIN 0x001D + +// IO and Clock Configuration Setting +#define OPRTCFG 0x0020 +#define SFTRST 0x0022 // any value to reset +#define IOCTRLH 0x0023 +#define IOCTRLL 0x0024 +#define CKCFG1 0x0025 +#define CKCFG2 0x0026 +#define PORTCTRL 0x0027 +#define TESTIMG 0x0028 +#define CCIR656 0x0029 +#define PLL1 0x002A +#define CKCFG3 0x002B +#define PLL2 0x002C + +// Black Level Target +#define BLCTGT 0x0040 +#define BLCTGT2 0x0053 + +// Vertical Arbitrary Window Configuration +#define VAWINSTARTH 0x0078 +#define VAWINSTARTL 0x0079 +#define VAWINENDH 0x007A +#define VAWINENDL 0x007B + +// Image Signal Processing Control +#define CMU_AE 0x0100 +#define CMU_AWB 0x0101 +#define ISPID 0x0105 +#define ISPCTRL1 0x0120 +#define ISPCTRL2 0x0121 +#define ISPCTRL3 0x0122 +#define ISPCTRL4 0x0124 +#define ISPCTRL5 0x0125 +#define ISPCTRL6 0x0126 + +// RAW Noise Filter Control +#define RAWNF 0x01E4 +#define ARAWNF 0x01E5 +#define ARAWNFODEL 0x01E6 + +// Automatic Exposure Control Registers +#define AEWBCFG 0x0380 +#define AETARGU 0x0381 +#define AETARGL 0x0382 +#define AETARGM 0x0383 + +// Saturation and Hue Control +#define SAT 0x0480 +#define ASAT 0x0481 +#define ASATODEL 0x0482 +#define HUESIN 0x0486 +#define HUECOS 0x0487 +#define SCENE 0x0488 + +// Contrast and Brightness Control +#define CONTM 0x04B0 +#define ACONTM 0x04B1 +#define CONTQ 0x04B3 +#define ACONTQ 0x04B4 +#define CONTN 0x04B6 +#define CONTP 0x04B9 +#define CONTGAIN 0x04BC +#define YMEAN 0x04BD +#define BRIGHT 0x04C0 + +// Y Denoise Control +#define YDN 0x0580 +#define AYDN 0x0581 +#define AYDNODEL 0x0582 + +// Sharpness Control +#define EDGE 0x05A1 + +// Fade to Black Control +#define F2BMEAN 0x05D0 +#define F2BRANGE 0x05D1 + +// Digital Window Control +#define YUVSCXL 0x05E0 +#define YUVSCXH 0x05E1 +#define YUVSCYL 0x05E2 +#define YUVSCYH 0x05E3 +#define WINXSTL 0x05E4 +#define WINXSTH 0x05E5 +#define WINXEDL 0x05E6 +#define WINXEDH 0x05E7 +#define WINYSTL 0x05E8 +#define WINYSTH 0x05E9 +#define WINYEDL 0x05EA +#define WINYEDH 0x05EB + +#endif //__REG_REGS_H__ diff --git a/sensors/private_include/hm1055_settings.h b/sensors/private_include/hm1055_settings.h new file mode 100755 index 0000000000..9d2831db18 --- /dev/null +++ b/sensors/private_include/hm1055_settings.h @@ -0,0 +1,697 @@ + +#include + +#define REG_DLY 0xffff +#define REGLIST_TAIL 0xffff + +static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { + {0x0022, 0x00}, // RESET + {0x0026, 0x77}, // 24MHz MCLK, 72MHz PCLK + {0x002A, 0x44}, // Modified by Wilson + {0x002B, 0x02}, + {0x002C, 0x00}, // Turn off DCC + {0x0025, 0x00}, // PLL on + {0x0020, 0x08}, + {0x0027, 0x40}, // FPGA 24 data format + {0x0028, 0xC0}, // + {0x0004, 0x10}, // + {0x0006, 0x00}, // Modified by Brandon. No Flip + {0x0012, 0x0F}, + {0x0044, 0x04}, + {0x004A, 0x2A}, + {0x004B, 0x72}, + {0x004E, 0x30}, + {0x0070, 0x2A}, + {0x0071, 0x57}, + {0x0072, 0x55}, + {0x0073, 0x30}, + {0x0077, 0x04}, + {0x0080, 0xC2}, + {0x0082, 0xA2}, + {0x0083, 0xF0}, + {0x0085, 0x11}, // Set ADC power to 100% and Increase Comparator power and reduce ADC swing range(4/19, K.Kim) + {0x0086, 0x22}, + {0x0087, 0x08}, + {0x0088, 0x6e}, // Temperature config 0x80[7:5]:76C, Minimize CTIA & PGA power and set VCMI to 200mV(4/19, K.Kim) + {0x0089, 0x2A}, + {0x008A, 0x2F}, // Set BLC power to 50%(4/19, K.Kim) + {0x008D, 0x20}, + {0x008f, 0x77}, + {0x0090, 0x01}, + {0x0091, 0x02}, + {0x0092, 0x03}, + {0x0093, 0x04}, + {0x0094, 0x14}, + {0x0095, 0x09}, + {0x0096, 0x0A}, + {0x0097, 0x0B}, + {0x0098, 0x0C}, + {0x0099, 0x04}, + {0x009A, 0x14}, + {0x009B, 0x34}, + {0x00A0, 0x00}, + {0x00A1, 0x00}, + {0x0B3B, 0x0B}, + {0x0040, 0x0A}, // BLC Phase1 + {0x0053, 0x0A}, // BLC Phase2 + {0x0120, 0x36}, // 36:50Hz, 37:60Hz + {0x0121, 0x80}, + {0x0122, 0xEB}, // 122[3] is temperature_control_bit2 + {0x0123, 0xCC}, // 123[3:0]maskTH + {0x0124, 0xDE}, + {0x0125, 0xDF}, + {0x0126, 0x70}, + {0x0128, 0x1F}, + {0x0129, 0x8F}, + {0x0132, 0xF8}, + {0x011F, 0x08}, // BPC COLD PIXEL ENABLE if 11F[0] = 1 then on else 0 off + {0x0144, 0x04}, // Hot pixel ratio + {0x0145, 0x00}, // Hot pixel ratio alpha + {0x0146, 0x20}, // Vertical Line TH + {0x0147, 0x20}, // Vertical Line TH alpha + {0x0148, 0x14}, // HD_2line_BPC_TH + {0x0149, 0x14}, // HD_2line_BPC_TH alpha + {0x0156, 0x0C}, // BPC RATIO FOR COLD PIXEL + {0x0157, 0x0C}, // BPC RATIO FOR COLD PIXEL + {0x0158, 0x0A}, // 2 LINE BPC RATIO + {0x0159, 0x0A}, // 2 LINE BPC RATIO + {0x015A, 0x03}, // BPC RATIO FOR COLD PIXEL + {0x015B, 0x40}, // Corner TH + {0x015C, 0x21}, // Corner TH alpha + {0x015E, 0x0F}, // Debug_mode + {0x0168, 0xC8}, // BPC T1 + {0x0169, 0xC8}, // BPC T1 ALPHA + {0x016A, 0x96}, // BPC T2 + {0x016B, 0x96}, // BPC T2 ALPHA + {0x016C, 0x64}, // BPC T3 + {0x016D, 0x64}, // BPC T3 ALPHA + {0x016E, 0x32}, // BPC T4 + {0x016F, 0x32}, // BPC T4 ALPHA + {0x01EF, 0xF1}, // BPC Control Byte + {0x0131, 0x44}, //[6] DS_EN, [5] VOFF, [4] EDGEON, [3] SBPI_MODE, [2] RAW_SAHRPNESS_EN, [1]G1G2B_EN + {0x014C, 0x60}, // VSUB + {0x014D, 0x24}, // VSUB ALPHA0 + {0x015D, 0x90}, //[7] TG_en// [6:0]:min Tgain + {0x01D8, 0x40}, // NOISE TH + {0x01D9, 0x20}, // NL_V1 + {0x01DA, 0x23}, // NL_V2 + {0x0150, 0x05}, // NL_INC1 + {0x0155, 0x07}, // NL_INC2 + {0x0178, 0x10}, // EVDIV + {0x017A, 0x10}, // EVDIVD + {0x01BA, 0x10}, // EVSUB + {0x0176, 0x00}, // EDGE2W + {0x0179, 0x10}, // EVDIV ALPHA0 + {0x017B, 0x10}, // EVDIVD ALPHA0 + {0x01BB, 0x10}, // EVSUB ALPHA0 + {0x0177, 0x00}, // EDGE2W ALPHA0 + {0x01E7, 0x20}, // TLTH1 + {0x01E8, 0x30}, // TLTH2 + {0x01E9, 0x50}, // TLTH3 + {0x01E4, 0x18}, // BD STRENGTH + {0x01E5, 0x20}, // BD STRENGTH ALPHA0 + {0x01E6, 0x04}, // BD STRENGTH OUTDOOR + {0x0210, 0x21}, // STD + {0x0211, 0x0A}, // STD ALPHA0 + {0x0212, 0x21}, // STD OUTDOOR + {0x01DB, 0x04}, // G1G2 STRENGTH + {0x01DC, 0x14}, // SBPI_OFFSET + {0x0151, 0x08}, + {0x01F2, 0x18}, + {0x01F8, 0x3C}, + {0x01FE, 0x24}, + {0x0213, 0x03}, // BPC_Control_BYTE_2 // BPC_line_edge_th 3rd bpc + {0x0214, 0x03}, // BPC_Control_BYTE_3 // 3rd bpc strength[4:0] , [7:5] is for 2line bpc temp_control_bit + {0x0215, 0x10}, // BPC_Control_BYTE_4 // 1st bpc strength[4:0] + {0x0216, 0x08}, // BPC_Control_BYTE_5 // 1st br pulse strength[4:0] + {0x0217, 0x05}, // BPC_Control_BYTE_6 // 1st gbgr pulse strengh[4:0] + {0x0218, 0xB8}, + {0x0219, 0x01}, + {0x021A, 0xB8}, + {0x021B, 0x01}, + {0x021C, 0xB8}, + {0x021D, 0x01}, + {0x021E, 0xB8}, + {0x021F, 0x01}, + {0x0220, 0xF1}, + {0x0221, 0x5D}, + {0x0222, 0x0A}, + {0x0223, 0x80}, + {0x0224, 0x50}, + {0x0225, 0x09}, + {0x0226, 0x80}, + {0x022A, 0x56}, + {0x022B, 0x13}, + {0x022C, 0x80}, + {0x022D, 0x11}, + {0x022E, 0x08}, + {0x022F, 0x11}, + {0x0230, 0x08}, + {0x0233, 0x11}, + {0x0234, 0x08}, + {0x0235, 0x88}, + {0x0236, 0x02}, + {0x0237, 0x88}, + {0x0238, 0x02}, + {0x023B, 0x88}, + {0x023C, 0x02}, + {0x023D, 0x68}, + {0x023E, 0x01}, + {0x023F, 0x68}, + {0x0240, 0x01}, + {0x0243, 0x68}, + {0x0244, 0x01}, + {0x0251, 0x0F}, + {0x0252, 0x00}, + {0x0260, 0x00}, + {0x0261, 0x4A}, + {0x0262, 0x2C}, + {0x0263, 0x68}, + {0x0264, 0x40}, + {0x0265, 0x2C}, + {0x0266, 0x6A}, + {0x026A, 0x40}, + {0x026B, 0x30}, + {0x026C, 0x66}, + {0x0278, 0x98}, + {0x0279, 0x20}, + {0x027A, 0x80}, + {0x027B, 0x73}, + {0x027C, 0x08}, + {0x027D, 0x80}, + {0x0280, 0x0D}, + {0x0282, 0x1A}, + {0x0284, 0x30}, + {0x0286, 0x53}, + {0x0288, 0x62}, + {0x028a, 0x6E}, + {0x028c, 0x7A}, + {0x028e, 0x83}, + {0x0290, 0x8B}, + {0x0292, 0x92}, + {0x0294, 0x9D}, + {0x0296, 0xA8}, + {0x0298, 0xBC}, + {0x029a, 0xCF}, + {0x029c, 0xE2}, + {0x029e, 0x2A}, + {0x02A0, 0x02}, + {0x02C0, 0x7D}, // CCM N + {0x02C1, 0x01}, + {0x02C2, 0x7C}, + {0x02C3, 0x04}, + {0x02C4, 0x01}, + {0x02C5, 0x04}, + {0x02C6, 0x3E}, + {0x02C7, 0x04}, + {0x02C8, 0x90}, + {0x02C9, 0x01}, + {0x02CA, 0x52}, + {0x02CB, 0x04}, + {0x02CC, 0x04}, + {0x02CD, 0x04}, + {0x02CE, 0xA9}, + {0x02CF, 0x04}, + {0x02D0, 0xAD}, + {0x02D1, 0x01}, + {0x0302, 0x00}, + {0x0303, 0x00}, + {0x0304, 0x00}, + {0x02e0, 0x04}, // CCM by Alpha + {0x02F0, 0x4E}, // CCM A + {0x02F1, 0x04}, + {0x02F2, 0xB1}, + {0x02F3, 0x00}, + {0x02F4, 0x63}, + {0x02F5, 0x04}, + {0x02F6, 0x28}, + {0x02F7, 0x04}, + {0x02F8, 0x29}, + {0x02F9, 0x04}, + {0x02FA, 0x51}, + {0x02FB, 0x00}, + {0x02FC, 0x64}, + {0x02FD, 0x04}, + {0x02FE, 0x6B}, + {0x02FF, 0x04}, + {0x0300, 0xCF}, + {0x0301, 0x00}, + {0x0305, 0x08}, + {0x0306, 0x40}, + {0x0307, 0x00}, + {0x032D, 0x70}, + {0x032E, 0x01}, + {0x032F, 0x00}, + {0x0330, 0x01}, + {0x0331, 0x70}, + {0x0332, 0x01}, + {0x0333, 0x82}, // AWB channel offset + {0x0334, 0x82}, + {0x0335, 0x86}, + {0x0340, 0x30}, // AWB + {0x0341, 0x44}, + {0x0342, 0x4A}, + {0x0343, 0x3C}, // CT1 + {0x0344, 0x83}, // + {0x0345, 0x4D}, // CT2 + {0x0346, 0x75}, // + {0x0347, 0x56}, // CT3 + {0x0348, 0x68}, // + {0x0349, 0x5E}, // CT4 + {0x034A, 0x5C}, // + {0x034B, 0x65}, // CT5 + {0x034C, 0x52}, // + {0x0350, 0x88}, + {0x0352, 0x18}, + {0x0354, 0x80}, + {0x0355, 0x50}, + {0x0356, 0x88}, + {0x0357, 0xE0}, + {0x0358, 0x00}, + {0x035A, 0x00}, + {0x035B, 0xAC}, + {0x0360, 0x02}, + {0x0361, 0x18}, + {0x0362, 0x50}, + {0x0363, 0x6C}, + {0x0364, 0x00}, + {0x0365, 0xF0}, + {0x0366, 0x08}, + {0x036A, 0x10}, + {0x036B, 0x18}, + {0x036E, 0x10}, + {0x0370, 0x10}, + {0x0371, 0x18}, + {0x0372, 0x0C}, + {0x0373, 0x38}, + {0x0374, 0x3A}, + {0x0375, 0x12}, + {0x0376, 0x20}, + {0x0380, 0xFF}, + {0x0381, 0x44}, + {0x0382, 0x34}, + {0x038A, 0x80}, + {0x038B, 0x0A}, + {0x038C, 0xC1}, + {0x038E, 0x3C}, + {0x038F, 0x09}, + {0x0390, 0xE0}, + {0x0391, 0x01}, + {0x0392, 0x03}, + {0x0393, 0x80}, + {0x0395, 0x22}, + {0x0398, 0x02}, // AE Frame Control + {0x0399, 0xF0}, + {0x039A, 0x03}, + {0x039B, 0xAC}, + {0x039C, 0x04}, + {0x039D, 0x68}, + {0x039E, 0x05}, + {0x039F, 0xE0}, + {0x03A0, 0x07}, + {0x03A1, 0x58}, + {0x03A2, 0x08}, + {0x03A3, 0xD0}, + {0x03A4, 0x0B}, + {0x03A5, 0xC0}, + {0x03A6, 0x18}, + {0x03A7, 0x1C}, + {0x03A8, 0x20}, + {0x03A9, 0x24}, + {0x03AA, 0x28}, + {0x03AB, 0x30}, + {0x03AC, 0x24}, + {0x03AD, 0x21}, + {0x03AE, 0x1C}, + {0x03AF, 0x18}, + {0x03B0, 0x17}, + {0x03B1, 0x13}, + {0x03B7, 0x64}, // AEAWB Windowing Step for 7x7 + {0x03B8, 0x00}, + {0x03B9, 0xB4}, + {0x03BA, 0x00}, + {0x03bb, 0xff}, // WinWeight 7x7 + {0x03bc, 0xff}, + {0x03bd, 0xff}, + {0x03be, 0xff}, + {0x03bf, 0xff}, + {0x03c0, 0xff}, + {0x03c1, 0x01}, + {0x03e0, 0x04}, + {0x03e1, 0x11}, + {0x03e2, 0x01}, + {0x03e3, 0x04}, + {0x03e4, 0x10}, + {0x03e5, 0x21}, + {0x03e6, 0x11}, + {0x03e7, 0x00}, + {0x03e8, 0x11}, + {0x03e9, 0x32}, + {0x03ea, 0x12}, + {0x03eb, 0x01}, + {0x03ec, 0x21}, + {0x03ed, 0x33}, + {0x03ee, 0x23}, + {0x03ef, 0x01}, + {0x03f0, 0x11}, + {0x03f1, 0x32}, + {0x03f2, 0x12}, + {0x03f3, 0x01}, + {0x03f4, 0x10}, + {0x03f5, 0x21}, + {0x03f6, 0x11}, + {0x03f7, 0x00}, + {0x03f8, 0x04}, + {0x03f9, 0x11}, + {0x03fa, 0x01}, + {0x03fb, 0x04}, + {0x03DC, 0x47}, // SubWinAE setting, min_dark_cnt=7, exit_dark_cnt=4 + {0x03DD, 0x5A}, // AEbl_th=70% + {0x03DE, 0x41}, // enable SubWinAE, bottom 3x5, DM_tol=12.5% + {0x03DF, 0x53}, // limit_ratio=65% + {0x0420, 0x82}, // Digital Gain offset + {0x0421, 0x00}, + {0x0422, 0x00}, + {0x0423, 0x88}, + {0x0430, 0x08}, // ABLC + {0x0431, 0x30}, + {0x0432, 0x0c}, + {0x0433, 0x04}, // 0A + {0x0435, 0x08}, // 10 + {0x0450, 0xFF}, // Alpha + {0x0451, 0xD0}, + {0x0452, 0xB8}, + {0x0453, 0x88}, + {0x0454, 0x00}, + {0x0458, 0x80}, + {0x0459, 0x03}, + {0x045A, 0x00}, + {0x045B, 0x50}, + {0x045C, 0x00}, + {0x045D, 0x90}, + {0x0465, 0x02}, + {0x0466, 0x14}, + {0x047A, 0x00}, // ELOFFNRB + {0x047B, 0x00}, // ELOFFNRY + {0x047C, 0x04}, + {0x047D, 0x50}, + {0x047E, 0x04}, + {0x047F, 0x90}, + {0x0480, 0x58}, + {0x0481, 0x06}, + {0x0482, 0x08}, // Sat outdoor + {0x04B0, 0x50}, // Contrast + {0x04B6, 0x30}, + {0x04B9, 0x10}, + {0x04B3, 0x00}, + {0x04B1, 0x85}, + {0x04B4, 0x00}, + {0x0540, 0x00}, // + {0x0541, 0xBC}, // 60Hz Flicker + {0x0542, 0x00}, // + {0x0543, 0xE1}, // 50Hz Flicker + {0x0580, 0x04}, // BLUR WEIGHT + {0x0581, 0x0F}, // BLUR WEIGHT ALPHA0 + {0x0582, 0x04}, // BLUR WEIGHT OUTDOOR + {0x05A1, 0x0A}, // SHARPNESS STRENGTH + {0x05A2, 0x21}, //[6:4] SHARP_ORE_NEG, [1:0] FIL SELECTION + {0x05A3, 0x84}, //[7]EA_EN, [6] EE_CENTER, [3:0] EDGEDIV + {0x05A4, 0x24}, // SHARP_ORE + {0x05A5, 0xFF}, // ORE_MAX + {0x05A6, 0x00}, // ORE_LOW + {0x05A7, 0x24}, // OUGB_HOT + {0x05A8, 0x24}, // OUGB_COLD + {0x05A9, 0x02}, // EDGE RANGE + {0x05B1, 0x24}, // EDGE SUB + {0x05B2, 0x0C}, //[3] Y_ADJC, [2]CMODE + {0x05B4, 0x1F}, // FIX WEIGHT + {0x05AE, 0x75}, // OAR START + {0x05AF, 0x78}, //[7:5]OAR STEP, [4:0]OAR DECSTR + {0x05B6, 0x00}, // SHARPNESS STRENGTH ALPHA0 + {0x05B7, 0x10}, // SHARP_ORE ALPHA0 + {0x05BF, 0x20}, // EDGESUB ALPHA0 + {0x05C1, 0x06}, // SHARPNESS STRENGTH OUTDOOR + {0x05C2, 0x18}, // SHARP ORE OUTDOOR + {0x05C7, 0x00}, // TFBPI + {0x05CC, 0x04}, // Raw sharpness strength + {0x05CD, 0x00}, // Raw sharpness strength ALPHA0 + {0x05CE, 0x03}, // Raw sharpness strength outdoor + {0x05E4, 0x08}, + {0x05E5, 0x00}, + {0x05E6, 0x07}, + {0x05E7, 0x05}, + {0x05E8, 0x08}, + {0x05E9, 0x00}, + {0x05EA, 0xD7}, + {0x05EB, 0x02}, + {0x0660, 0x00}, + {0x0661, 0x16}, + {0x0662, 0x07}, + {0x0663, 0xf1}, + {0x0664, 0x07}, + {0x0665, 0xde}, + {0x0666, 0x07}, + {0x0667, 0xe7}, + {0x0668, 0x00}, + {0x0669, 0x35}, + {0x066a, 0x07}, + {0x066b, 0xf9}, + {0x066c, 0x07}, + {0x066d, 0xb7}, + {0x066e, 0x00}, + {0x066f, 0x27}, + {0x0670, 0x07}, + {0x0671, 0xf3}, + {0x0672, 0x07}, + {0x0673, 0xc5}, + {0x0674, 0x07}, + {0x0675, 0xee}, + {0x0676, 0x00}, + {0x0677, 0x16}, + {0x0678, 0x01}, + {0x0679, 0x80}, + {0x067a, 0x00}, + {0x067b, 0x85}, + {0x067c, 0x07}, + {0x067d, 0xe1}, + {0x067e, 0x07}, + {0x067f, 0xf5}, + {0x0680, 0x07}, + {0x0681, 0xb9}, + {0x0682, 0x00}, + {0x0683, 0x31}, + {0x0684, 0x07}, + {0x0685, 0xe6}, + {0x0686, 0x07}, + {0x0687, 0xd3}, + {0x0688, 0x00}, + {0x0689, 0x18}, + {0x068a, 0x07}, + {0x068b, 0xfa}, + {0x068c, 0x07}, + {0x068d, 0xd2}, + {0x068e, 0x00}, + {0x068f, 0x08}, + {0x0690, 0x00}, + {0x0691, 0x02}, + {0xAFD0, 0x03}, // Auto Flicker detection off + {0xAFD3, 0x18}, + {0xAFD4, 0x04}, + {0xAFD5, 0xB8}, // auto flicker samp time 3188 + {0xAFD6, 0x02}, + {0xAFD7, 0x44}, + {0xAFD8, 0x02}, + + // QVGA + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XBE}, + {0x05E1, 0x00}, + {0x05E2, 0xBE}, + {0x05E3, 0x00}, + {0x05E4, 0x3A}, + {0x05E5, 0x00}, + {0x05E6, 0x79}, + {0x05E7, 0x01}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xF3}, + {0x05EB, 0x00}, + + {0x0000, 0x01}, + {0x0100, 0x01}, + {0x0101, 0x01}, + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_HD[][2] = { + {0x0006, 0x00}, + {0x000D, 0x00}, + {0x000E, 0x00}, + {0x0122, 0xFB}, + {0x0125, 0xDF}, + {0x0126, 0x70}, + {0x05E4, 0x08}, + {0x05E5, 0x00}, + {0x05E6, 0x07}, + {0x05E7, 0x05}, + {0x05E8, 0x08}, + {0x05E9, 0x00}, + {0x05EA, 0xD7}, + {0x05EB, 0x02}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_SVGA[][2] = { + {0x0006, 0x10}, + {0x000D, 0x00}, + {0x000E, 0x00}, + {0x0122, 0xEB}, + {0x0125, 0xDF}, + {0x0126, 0x70}, + {0x05E4, 0x58}, + {0x05E5, 0x00}, + {0x05E6, 0x77}, + {0x05E7, 0x03}, + {0x05E8, 0x42}, + {0x05E9, 0x00}, + {0x05EA, 0x99}, + {0x05EB, 0x02}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA[][2] = { + {0x0006, 0x10}, + {0x000D, 0x00}, + {0x000E, 0x00}, + {0x0122, 0xEB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XC1}, + {0x05E1, 0x00}, + {0x05E2, 0xC1}, + {0x05E3, 0x00}, + {0x05E4, 0x03}, + {0x05E5, 0x00}, + {0x05E6, 0x82}, + {0x05E7, 0x02}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xE3}, + {0x05EB, 0x01}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_CIF[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x72}, + {0x05E0, 0XC0}, + {0x05E1, 0x00}, + {0x05E2, 0x8F}, + {0x05E3, 0x00}, + {0x05E4, 0x11}, + {0x05E5, 0x00}, + {0x05E6, 0xA0}, + {0x05E7, 0x01}, + {0x05E8, 0x08}, + {0x05E9, 0x00}, + {0x05EA, 0x2F}, + {0x05EB, 0x01}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XBE}, + {0x05E1, 0x00}, + {0x05E2, 0xBE}, + {0x05E3, 0x00}, + {0x05E4, 0x3A}, + {0x05E5, 0x00}, + {0x05E6, 0x79}, + {0x05E7, 0x01}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xF3}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QCIF[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x72}, + {0x05E0, 0X52}, + {0x05E1, 0x01}, + {0x05E2, 0x38}, + {0x05E3, 0x01}, + {0x05E4, 0x22}, + {0x05E5, 0x00}, + {0x05E6, 0xD1}, + {0x05E7, 0x00}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0x93}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_240X240[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XBE}, + {0x05E1, 0x00}, + {0x05E2, 0xBE}, + {0x05E3, 0x00}, + {0x05E4, 0x62}, + {0x05E5, 0x00}, + {0x05E6, 0x51}, + {0x05E7, 0x01}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xF3}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QQVGA[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0X78}, + {0x05E1, 0x01}, + {0x05E2, 0x78}, + {0x05E3, 0x01}, + {0x05E4, 0x1E}, + {0x05E5, 0x00}, + {0x05E6, 0xBD}, + {0x05E7, 0x00}, + {0x05E8, 0x03}, + {0x05E9, 0x00}, + {0x05EA, 0x7A}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; \ No newline at end of file diff --git a/sensors/private_include/mega_ccm.h b/sensors/private_include/mega_ccm.h new file mode 100644 index 0000000000..0f5efb21ec --- /dev/null +++ b/sensors/private_include/mega_ccm.h @@ -0,0 +1,31 @@ +/* + * + * MEGA_CCM driver. + * + */ +#ifndef __MEGA_CCM_H__ +#define __MEGA_CCM_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 esp32_camera_mega_ccm_detect(int slv_addr, sensor_id_t *id); + +/** + * @brief initialize sensor function pointers + * + * @param sensor pointer of sensor + * @return + * Always 0 + */ +int esp32_camera_mega_ccm_init(sensor_t *sensor); + +#endif // __MEGA_CCM_H__ diff --git a/sensors/private_include/mega_ccm_regs.h b/sensors/private_include/mega_ccm_regs.h new file mode 100644 index 0000000000..dbd51dd274 --- /dev/null +++ b/sensors/private_include/mega_ccm_regs.h @@ -0,0 +1,43 @@ +/* + * MEGA_CCM register definitions. + */ +#ifndef __MEGA_CCM_REG_REGS_H__ +#define __MEGA_CCM_REG_REGS_H__ + +#define ID_BASE 0x0000 +#define SENSOR_BASE 0x0100 +#define SYS_CLK_BASE 0x0200 +#define BYPASS_BASE 0XFFF0 + +#define SENSOR_ID_HIGH ID_BASE | 0x00 +#define SENSOR_ID_LOW ID_BASE | 0x01 +#define FIRMWARE_VER ID_BASE | 0x02 + +#define CAMERA_RST_REG SENSOR_BASE|0x02 + + +#define PIXEL_FMT_REG SENSOR_BASE|0x20 +#define RESOLUTION_REG SENSOR_BASE|0x21 +#define BRIGHTNESS_REG SENSOR_BASE|0x22 +#define CONTRAST_REG SENSOR_BASE|0x23 +#define SATURATION_REG SENSOR_BASE|0x24 +#define EXP_COMPENSATE_REG SENSOR_BASE|0x25 +#define AWB_MODE_REG SENSOR_BASE|0x26 +#define SPECIAL_REG SENSOR_BASE|0x27 +#define SHARPNESS_REG SENSOR_BASE|0x28 +#define FOCUS_REG SENSOR_BASE|0x29 +#define IMAGE_QUALITY_REG SENSOR_BASE|0x2A +#define IMAGE_FLIP_REG SENSOR_BASE|0x2B +#define IMAGE_MIRROR_REG SENSOR_BASE|0x2C + + +#define AGC_MODE_REG SENSOR_BASE|0x30 +#define MANUAL_AGC_REG SENSOR_BASE|0x31 +#define MANUAL_EXP_H_REG SENSOR_BASE|0x33 +#define MANUAL_EXP_L_REG SENSOR_BASE|0x34 + + +#define SYSTEM_CLK_DIV_REG SYS_CLK_BASE|0x00 +#define SYSTEM_PLL_DIV_REG SYS_CLK_BASE|0x01 + +#endif //__MEGA_CCM_REG_REGS_H__ diff --git a/sensors/private_include/mega_ccm_settings.h b/sensors/private_include/mega_ccm_settings.h new file mode 100644 index 0000000000..ff8e1866d6 --- /dev/null +++ b/sensors/private_include/mega_ccm_settings.h @@ -0,0 +1,19 @@ +#ifndef _GC032A_SETTINGS_H_ +#define _GC032A_SETTINGS_H_ + +#include +#include +#include "esp_attr.h" +#include "mega_ccm_regs.h" + + +#define REG_DLY 0xffff +#define REGLIST_TAIL 0x0000 + +static const uint16_t mega_ccm_default_regs[][2] = { + {0x0120, 0x01 }, // JPEG + {0x0121, 0x01 }, // 320X240 + {REGLIST_TAIL, 0x00}, +}; + +#endif diff --git a/sensors/private_include/nt99141.h b/sensors/private_include/nt99141.h index 8b0c562b94..0e7cb52a01 100644 --- a/sensors/private_include/nt99141.h +++ b/sensors/private_include/nt99141.h @@ -20,7 +20,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int nt99141_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_nt99141_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -29,6 +29,6 @@ int nt99141_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int nt99141_init(sensor_t *sensor); +int esp32_camera_nt99141_init(sensor_t *sensor); #endif // __NT99141_H__ diff --git a/sensors/private_include/ov2640.h b/sensors/private_include/ov2640.h index 342ab21328..4622c1a004 100755 --- a/sensors/private_include/ov2640.h +++ b/sensors/private_include/ov2640.h @@ -18,7 +18,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int ov2640_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_ov2640_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -27,6 +27,6 @@ int ov2640_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int ov2640_init(sensor_t *sensor); +int esp32_camera_ov2640_init(sensor_t *sensor); #endif // __OV2640_H__ diff --git a/sensors/private_include/ov3660.h b/sensors/private_include/ov3660.h index 341d688616..1bb5beb537 100755 --- a/sensors/private_include/ov3660.h +++ b/sensors/private_include/ov3660.h @@ -20,7 +20,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int ov3660_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_ov3660_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -29,6 +29,6 @@ int ov3660_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int ov3660_init(sensor_t *sensor); +int esp32_camera_ov3660_init(sensor_t *sensor); #endif // __OV3660_H__ diff --git a/sensors/private_include/ov5640.h b/sensors/private_include/ov5640.h index 120ae72054..307f19df0c 100755 --- a/sensors/private_include/ov5640.h +++ b/sensors/private_include/ov5640.h @@ -13,7 +13,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int ov5640_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_ov5640_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -22,6 +22,6 @@ int ov5640_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int ov5640_init(sensor_t *sensor); +int esp32_camera_ov5640_init(sensor_t *sensor); #endif // __OV5640_H__ diff --git a/sensors/private_include/ov7670.h b/sensors/private_include/ov7670.h index b3a645a70d..cfd8b4792d 100644 --- a/sensors/private_include/ov7670.h +++ b/sensors/private_include/ov7670.h @@ -19,7 +19,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int ov7670_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_ov7670_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -28,6 +28,6 @@ int ov7670_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int ov7670_init(sensor_t *sensor); +int esp32_camera_ov7670_init(sensor_t *sensor); #endif // __OV7670_H__ diff --git a/sensors/private_include/ov7725.h b/sensors/private_include/ov7725.h index 291b266800..6ab41db8e5 100755 --- a/sensors/private_include/ov7725.h +++ b/sensors/private_include/ov7725.h @@ -19,7 +19,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int ov7725_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_ov7725_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -28,6 +28,6 @@ int ov7725_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int ov7725_init(sensor_t *sensor); +int esp32_camera_ov7725_init(sensor_t *sensor); #endif // __OV7725_H__ diff --git a/sensors/private_include/sc030iot.h b/sensors/private_include/sc030iot.h index 19298b76c2..824d2d6cf4 100644 --- a/sensors/private_include/sc030iot.h +++ b/sensors/private_include/sc030iot.h @@ -17,7 +17,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int sc030iot_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_sc030iot_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -26,6 +26,6 @@ int sc030iot_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int sc030iot_init(sensor_t *sensor); +int esp32_camera_sc030iot_init(sensor_t *sensor); #endif // __SC030IOT_H__ diff --git a/sensors/private_include/sc031gs.h b/sensors/private_include/sc031gs.h index bf3cdecef8..4a8c300926 100644 --- a/sensors/private_include/sc031gs.h +++ b/sensors/private_include/sc031gs.h @@ -4,7 +4,7 @@ * */ #ifndef __SC031GS_H__ -#define __SC030GS_H__ +#define __SC031GS_H__ #include "sensor.h" @@ -17,7 +17,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int sc031gs_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_sc031gs_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -26,6 +26,6 @@ int sc031gs_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int sc031gs_init(sensor_t *sensor); +int esp32_camera_sc031gs_init(sensor_t *sensor); #endif // __SC031GS_H__ diff --git a/sensors/private_include/sc031gs_settings.h b/sensors/private_include/sc031gs_settings.h index 255c1b28a6..ba5ca02d86 100644 --- a/sensors/private_include/sc031gs_settings.h +++ b/sensors/private_include/sc031gs_settings.h @@ -78,10 +78,13 @@ struct sc031gs_regval { uint8_t val; }; -// 200*200, xclk=10M, fps=120fps -static const struct sc031gs_regval sc031gs_default_init_regs[] = { - {0x0103, 0x01}, // soft reset. +static const struct sc031gs_regval sc031gs_reset_regs[] = { + {0x0103, 0x01}, // soft reset. {REG_DELAY, 10}, // delay. +}; + +// 200*200, xclk=10M, fps=120fps +static const struct sc031gs_regval sc031gs_200x200_init_regs[] = { {0x0100, 0x00}, {0x36e9, 0x80}, {0x36f9, 0x80}, @@ -200,3 +203,114 @@ static const struct sc031gs_regval sc031gs_default_init_regs[] = { {0x3317, 0x0e}, {REG_NULL, 0x00}, }; + +// 640*480, xclk=20M, fps=50fps, xclk=10M, fps=25fps +static const struct sc031gs_regval sc031gs_640x480_50fps_init_regs[] = { + {0x0100, 0x00}, + {0x36e9, 0x80}, + {0x36f9, 0x80}, + {0x300f, 0x0f}, + {0x3018, 0x1f}, + {0x3019, 0xff}, + {0x301c, 0xb4}, + {0x301f, 0x6c}, + {0x3028, 0x82}, + {0x3200, 0x00}, + {0x3201, 0x00}, + {0x3202, 0x00}, + {0x3203, 0x08}, + {0x3204, 0x02}, + {0x3205, 0x8f}, + {0x3206, 0x01}, + {0x3207, 0xf7}, + {SC031GS_OUTPUT_WINDOW_WIDTH_H_REG, 0x02}, + {SC031GS_OUTPUT_WINDOW_WIDTH_L_REG, 0x80}, + {SC031GS_OUTPUT_WINDOW_HIGH_H_REG, 0x01}, + {SC031GS_OUTPUT_WINDOW_HIGH_L_REG, 0xe0}, + {0x320c, 0x03}, + {0x320d, 0x6e}, + {0x320e, 0x04}, + {0x320f, 0x72}, + {SC031GS_OUTPUT_WINDOW_START_Y_H_REG, 0x00}, + {SC031GS_OUTPUT_WINDOW_START_Y_L_REG, 0x08}, + {SC031GS_OUTPUT_WINDOW_START_X_H_REG, 0x00}, + {SC031GS_OUTPUT_WINDOW_START_X_L_REG, 0x08}, + {0x3220, 0x10}, + {0x3223, 0x50}, + {0x3250, 0xf0}, + {0x3251, 0x02}, + {0x3252, 0x03}, + {0x3253, 0xb0}, + {0x3254, 0x02}, + {0x3255, 0x07}, + {0x3304, 0x48}, + {0x3306, 0x38}, + {0x3309, 0x68}, + {0x330b, 0xe0}, + {0x330c, 0x18}, + {0x330f, 0x20}, + {0x3310, 0x10}, + {0x3314, 0x6d}, + {0x3315, 0x38}, + {0x3316, 0x68}, + {0x3317, 0x0f}, + {0x3329, 0x5c}, + {0x332d, 0x5c}, + {0x332f, 0x60}, + {0x3335, 0x64}, + {0x3344, 0x64}, + {0x335b, 0x80}, + {0x335f, 0x80}, + {0x3366, 0x06}, + {0x3385, 0x31}, + {0x3387, 0x51}, + {0x3389, 0x01}, + {0x33b1, 0x03}, + {0x33b2, 0x06}, + {0x3621, 0xa4}, + {0x3622, 0x05}, + {0x3624, 0x47}, + {0x3631, 0x48}, + {0x3633, 0x52}, + {0x3635, 0x18}, + {0x3636, 0x25}, + {0x3637, 0x89}, + {0x3638, 0x0f}, + {0x3639, 0x08}, + {0x363a, 0x00}, + {0x363b, 0x48}, + {0x363c, 0x06}, + {0x363e, 0xf8}, + {0x3640, 0x00}, + {0x3641, 0x01}, + {0x36ea, 0x36}, + {0x36eb, 0x1a}, + {0x36ec, 0x0a}, + {0x36ed, 0x23}, + {0x36fa, 0x36}, + {0x36fb, 0x10}, + {0x36fc, 0x01}, + {0x36fd, 0x03}, + {0x3908, 0x91}, + {0x3d08, 0x01}, + {0x3e01, 0x14}, + {0x3e02, 0x80}, + {0x3e06, 0x0c}, + {0x3f04, 0x03}, + {0x3f05, 0x4e}, + {0x4500, 0x59}, + {0x4501, 0xc4}, + {0x4809, 0x01}, + {0x4837, 0x1b}, + {0x5011, 0x00}, + {0x36e9, 0x20}, + {0x36f9, 0x24}, + {0x0100, 0x01}, // must write 0x0100 with 0x01, must delay no less then 7ms + //delay 10ms + {REG_DELAY, 0X0a}, + {0x4418, 0x08}, + {0x4419, 0x80}, + {0x363d, 0x10}, + {0x3630, 0x48}, + {REG_NULL, 0x00}, +}; \ No newline at end of file diff --git a/sensors/private_include/sc101iot.h b/sensors/private_include/sc101iot.h index 85858498ec..c6c9eea609 100644 --- a/sensors/private_include/sc101iot.h +++ b/sensors/private_include/sc101iot.h @@ -17,7 +17,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int sc101iot_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_sc101iot_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -26,6 +26,6 @@ int sc101iot_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int sc101iot_init(sensor_t *sensor); +int esp32_camera_sc101iot_init(sensor_t *sensor); #endif // __SC101IOT_H__ diff --git a/sensors/sc030iot.c b/sensors/sc030iot.c index 86f525f376..e7054c1aee 100644 --- a/sensors/sc030iot.c +++ b/sensors/sc030iot.c @@ -284,7 +284,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int sc030iot_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_sc030iot_detect(int slv_addr, sensor_id_t *id) { if (SC030IOT_SCCB_ADDR == slv_addr) { uint8_t MIDL = SCCB_Read(slv_addr, SC030_SENSOR_ID_LOW_REG); @@ -300,7 +300,7 @@ int sc030iot_detect(int slv_addr, sensor_id_t *id) return 0; } -int sc030iot_init(sensor_t *sensor) +int esp32_camera_sc030iot_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; diff --git a/sensors/sc031gs.c b/sensors/sc031gs.c index dbd75ea054..307fa4232b 100644 --- a/sensors/sc031gs.c +++ b/sensors/sc031gs.c @@ -204,7 +204,7 @@ static int set_aec_value(sensor_t *sensor, int value) static int reset(sensor_t *sensor) { - int ret = write_regs(sensor->slv_addr, sc031gs_default_init_regs); + int ret = write_regs(sensor->slv_addr, sc031gs_reset_regs); if (ret) { ESP_LOGE(TAG, "reset fail"); } @@ -217,17 +217,11 @@ static int set_output_window(sensor_t *sensor, int offset_x, int offset_y, int w { int ret = 0; //sc:H_start={0x3212[1:0],0x3213},H_length={0x3208[1:0],0x3209}, - // printf("%d, %d, %d, %d\r\n", ((offset_x>>8) & 0x03), offset_x & 0xff, ((w>>8) & 0x03), w & 0xff); - WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_START_X_H_REG, 0x0); // For now, we use x_start is 0x04 - WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_START_X_L_REG, 0x04); WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_WIDTH_H_REG, ((w>>8) & 0x03)); WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_WIDTH_L_REG, w & 0xff); //sc:V_start={0x3210[1:0],0x3211},V_length={0x320a[1:0],0x320b}, - // printf("%d, %d, %d, %d\r\n", ((offset_y>>8) & 0x03), offset_y & 0xff, ((h>>8) & 0x03), h & 0xff); - WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_START_Y_H_REG, 0x0); // For now, we use y_start is 0x08 - WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_START_Y_L_REG, 0x08); WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_HIGH_H_REG, ((h>>8) & 0x03)); WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_HIGH_L_REG, h & 0xff); @@ -240,17 +234,21 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) { uint16_t w = resolution[framesize].width; uint16_t h = resolution[framesize].height; - if(w > SC031GS_MAX_FRAME_WIDTH || h > SC031GS_MAX_FRAME_HIGH) { - goto err; - } - if(w != 200 || h != 200) { - ESP_LOGE(TAG, "Only support 200*200 for now, contact us if you want to use other resolutions"); + struct sc031gs_regval const *framesize_regs = sc031gs_200x200_init_regs; + if(framesize > FRAMESIZE_VGA) { goto err; + } else if(framesize > FRAMESIZE_QVGA) { + framesize_regs = sc031gs_640x480_50fps_init_regs; } uint16_t offset_x = (640-w) /2 + 4; uint16_t offset_y = (480-h) /2 + 4; + + int ret = write_regs(sensor->slv_addr, framesize_regs); + if (ret) { + ESP_LOGE(TAG, "reset fail"); + } if(set_output_window(sensor, offset_x, offset_y, w, h)) { goto err; @@ -294,7 +292,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int sc031gs_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_sc031gs_detect(int slv_addr, sensor_id_t *id) { if (SC031GS_SCCB_ADDR == slv_addr) { uint8_t MIDL = SCCB_Read16(slv_addr, SC031GS_PID_HIGH_REG); @@ -310,7 +308,7 @@ int sc031gs_detect(int slv_addr, sensor_id_t *id) return 0; } -int sc031gs_init(sensor_t *sensor) +int esp32_camera_sc031gs_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; diff --git a/sensors/sc101iot.c b/sensors/sc101iot.c index 310a04768b..e27e4793ff 100644 --- a/sensors/sc101iot.c +++ b/sensors/sc101iot.c @@ -291,7 +291,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int sc101iot_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_sc101iot_detect(int slv_addr, sensor_id_t *id) { if (SC101IOT_SCCB_ADDR == slv_addr) { uint8_t MIDL = SCCB_Read(slv_addr, SC101_SENSOR_ID_LOW_REG); @@ -307,7 +307,7 @@ int sc101iot_detect(int slv_addr, sensor_id_t *id) return 0; } -int sc101iot_init(sensor_t *sensor) +int esp32_camera_sc101iot_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; diff --git a/target/esp32/ll_cam.c b/target/esp32/ll_cam.c index cc4065e48c..1d31dc60b9 100644 --- a/target/esp32/ll_cam.c +++ b/target/esp32/ll_cam.c @@ -39,11 +39,21 @@ static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num) #endif #if (ESP_IDF_VERSION_MAJOR >= 5) +#include "driver/gpio.h" #define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE #define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE #define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c) #endif +#if (ESP_IDF_VERSION_MAJOR > 5) +#include "soc/dport_access.h" +#include "soc/dport_reg.h" +#endif + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 2) +#define ets_delay_us esp_rom_delay_us +#endif + static const char *TAG = "esp32 ll_cam"; #define I2S_ISR_ENABLE(i) {I2S0.int_clr.i = 1;I2S0.int_ena.i = 1;} @@ -253,7 +263,6 @@ esp_err_t ll_cam_deinit(cam_obj_t *cam) esp_intr_free(cam->cam_intr_handle); cam->cam_intr_handle = NULL; } - return ESP_OK; } @@ -285,7 +294,12 @@ 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) { // Enable and configure I2S peripheral +#if ESP_IDF_VERSION_MAJOR > 5 + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST); +#else periph_module_enable(PERIPH_I2S0_MODULE); +#endif I2S0.conf.rx_reset = 1; I2S0.conf.rx_reset = 0; @@ -487,7 +501,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, 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 || sensor_pid == SC031GS_PID) { + if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID || sensor_pid == GC0308_PID) { if (xclk_freq_hz > 10000000) { sampling_mode = SM_0A00_0B00; dma_filter = ll_cam_dma_filter_yuyv_highspeed; diff --git a/target/esp32s2/ll_cam.c b/target/esp32s2/ll_cam.c index ef7d582604..c708dc3fbb 100644 --- a/target/esp32s2/ll_cam.c +++ b/target/esp32s2/ll_cam.c @@ -26,18 +26,23 @@ #endif #if (ESP_IDF_VERSION_MAJOR >= 5) +#include "driver/gpio.h" #define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE #define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE #define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c) #define ets_delay_us(a) esp_rom_delay_us(a) #endif +#if (ESP_IDF_VERSION_MAJOR > 5) +#include "soc/dport_access.h" +#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) +static void CAMERA_ISR_IRAM_ATTR ll_cam_vsync_isr(void *arg) { //DBG_PIN_SET(1); cam_obj_t *cam = (cam_obj_t *)arg; @@ -54,7 +59,7 @@ static void IRAM_ATTR ll_cam_vsync_isr(void *arg) //DBG_PIN_SET(0); } -static void IRAM_ATTR ll_cam_dma_isr(void *arg) +static void CAMERA_ISR_IRAM_ATTR ll_cam_dma_isr(void *arg) { cam_obj_t *cam = (cam_obj_t *)arg; BaseType_t HPTaskAwoken = pdFALSE; @@ -95,7 +100,6 @@ esp_err_t ll_cam_deinit(cam_obj_t *cam) esp_intr_free(cam->cam_intr_handle); cam->cam_intr_handle = NULL; } - return ESP_OK; } @@ -136,7 +140,12 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config) if(err != ESP_OK) { return err; } +#if ESP_IDF_VERSION_MAJOR > 5 + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN0_REG, DPORT_I2S0_RST); +#else periph_module_enable(PERIPH_I2S0_MODULE); +#endif // Configure the clock I2S0.clkm_conf.clkm_div_num = 2; // 160MHz / 2 = 80MHz I2S0.clkm_conf.clkm_div_b = 0; @@ -178,8 +187,6 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config) 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; @@ -217,7 +224,7 @@ esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config) 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_install_isr_service(ESP_INTR_FLAG_LOWMED | CAMERA_ISR_IRAM_FLAG); gpio_isr_handler_add(config->pin_vsync, ll_cam_vsync_isr, cam); gpio_intr_disable(config->pin_vsync); @@ -255,7 +262,7 @@ 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) { - 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); + return esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_LOWMED | CAMERA_ISR_IRAM_FLAG, ll_cam_dma_isr, cam, &cam->cam_intr_handle); } void ll_cam_do_vsync(cam_obj_t *cam) @@ -394,7 +401,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, 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 || sensor_pid == SC031GS_PID) { + if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID || sensor_pid == GC0308_PID) { cam->in_bytes_per_pixel = 1; // camera sends Y8 } else { cam->in_bytes_per_pixel = 2; // camera sends YU/YV diff --git a/target/esp32s2/tjpgd.c b/target/esp32s2/tjpgd.c deleted file mode 100755 index 5a983c4c7f..0000000000 --- a/target/esp32s2/tjpgd.c +++ /dev/null @@ -1,970 +0,0 @@ -/*----------------------------------------------------------------------------/ -/ 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 - - diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index 2c1e79225b..ee1eacde41 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -18,13 +18,15 @@ #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 "hal/clk_gate_ll.h" +#include "esp_private/gdma.h" #include "ll_cam.h" #include "cam_hal.h" #include "esp_rom_gpio.h" #if (ESP_IDF_VERSION_MAJOR >= 5) +#include "driver/gpio.h" #include "soc/gpio_sig_map.h" #include "soc/gpio_periph.h" #include "soc/io_mux_reg.h" @@ -33,9 +35,67 @@ #define ets_delay_us(a) esp_rom_delay_us(a) #endif +#if (ESP_IDF_VERSION_MAJOR > 5) +#include "soc/dport_access.h" +#else +#include "soc/gdma_periph.h" +#endif + +#if !defined(SOC_GDMA_PAIRS_PER_GROUP) && defined(SOC_GDMA_PAIRS_PER_GROUP_MAX) +#define SOC_GDMA_PAIRS_PER_GROUP SOC_GDMA_PAIRS_PER_GROUP_MAX +#endif + static const char *TAG = "s3 ll_cam"; -static void IRAM_ATTR ll_cam_vsync_isr(void *arg) +void ll_cam_dma_print_state(cam_obj_t *cam) +{ + esp_rom_printf("dma_infifo_status[%u] :\n", cam->dma_num); + esp_rom_printf(" infifo_full_l1 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_full_l1); + esp_rom_printf(" infifo_empty_l1 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_empty_l1); + esp_rom_printf(" infifo_full_l2 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_full_l2); + esp_rom_printf(" infifo_empty_l2 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_empty_l2); + esp_rom_printf(" infifo_full_l3 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_full_l3); + esp_rom_printf(" infifo_empty_l3 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_empty_l3); + esp_rom_printf(" infifo_cnt_l1 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_cnt_l1); + esp_rom_printf(" infifo_cnt_l2 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_cnt_l2); + esp_rom_printf(" infifo_cnt_l3 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_cnt_l3); + esp_rom_printf(" in_remain_under_1b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_1b_l3); + esp_rom_printf(" in_remain_under_2b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_2b_l3); + esp_rom_printf(" in_remain_under_3b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_3b_l3); + esp_rom_printf(" in_remain_under_4b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_4b_l3); + esp_rom_printf(" in_buf_hungry : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_buf_hungry); + esp_rom_printf("dma_state[%u] :\n", cam->dma_num); + esp_rom_printf(" dscr_addr : 0x%lx\n", GDMA.channel[cam->dma_num].in.state.dscr_addr); + esp_rom_printf(" in_dscr_state : %lu\n", GDMA.channel[cam->dma_num].in.state.in_dscr_state); + esp_rom_printf(" in_state : %lu\n", GDMA.channel[cam->dma_num].in.state.in_state); +} + +void ll_cam_dma_reset(cam_obj_t *cam) +{ + + 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.conf1.in_ext_mem_bk_size = 2; + + GDMA.channel[cam->dma_num].in.peri_sel.sel = 5; + //GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15 + //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 +} + +static void CAMERA_ISR_IRAM_ATTR ll_cam_vsync_isr(void *arg) { //DBG_PIN_SET(1); cam_obj_t *cam = (cam_obj_t *)arg; @@ -58,7 +118,7 @@ static void IRAM_ATTR ll_cam_vsync_isr(void *arg) //DBG_PIN_SET(0); } -static void IRAM_ATTR ll_cam_dma_isr(void *arg) +static void CAMERA_ISR_IRAM_ATTR ll_cam_dma_isr(void *arg) { cam_obj_t *cam = (cam_obj_t *)arg; BaseType_t HPTaskAwoken = pdFALSE; @@ -89,25 +149,6 @@ bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam) 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; @@ -139,48 +180,82 @@ bool ll_cam_start(cam_obj_t *cam, int frame_pos) return true; } -static esp_err_t ll_cam_dma_init(cam_obj_t *cam) +esp_err_t ll_cam_deinit(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 (cam->cam_intr_handle) { + esp_intr_free(cam->cam_intr_handle); + cam->cam_intr_handle = NULL; } - 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); + if (cam->dma_intr_handle) { + esp_intr_free(cam->dma_intr_handle); + cam->dma_intr_handle = NULL; } - - 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; + if (cam->dma_channel_handle) { + // gdma_disconnect(cam->dma_channel_handle); // not needed since code never calls gdma_connect() to connect channel to peripheral + gdma_del_channel(cam->dma_channel_handle); + cam->dma_channel_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; +} - GDMA.channel[cam->dma_num].in.conf1.in_check_owner = 0; - // GDMA.channel[cam->dma_num].in.conf1.in_ext_mem_bk_size = 2; - - GDMA.channel[cam->dma_num].in.peri_sel.sel = 5; - //GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15 - //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 +static esp_err_t ll_cam_dma_init(cam_obj_t *cam) +{ + //alloc rx gdma channel + gdma_channel_alloc_config_t rx_alloc_config = { + .direction = GDMA_CHANNEL_DIRECTION_RX, + }; +#if ((ESP_IDF_VERSION_MAJOR == 5 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 5) + esp_err_t ret = gdma_new_ahb_channel(&rx_alloc_config, &cam->dma_channel_handle); +#else + esp_err_t ret = gdma_new_channel(&rx_alloc_config, &cam->dma_channel_handle); +#endif + if (ret != ESP_OK) { + cam_deinit(); + ESP_LOGE(TAG, "Can't find available GDMA channel"); + return ESP_FAIL; + } + int chan_id = -1; + ret = gdma_get_channel_id(cam->dma_channel_handle, &chan_id); + if (ret != ESP_OK) { + cam_deinit(); + ESP_LOGE(TAG, "Can't get GDMA channel number"); + return ESP_FAIL; + } + cam->dma_num = chan_id; + ESP_LOGI(TAG, "DMA Channel=%d", cam->dma_num); + // 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 ESP_IDF_VERSION_MAJOR > 5 + if (!(DPORT_REG_GET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST) == 0 && + DPORT_REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN) != 0)) { + DPORT_CLEAR_PERI_REG_MASK(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN); + DPORT_SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST); + DPORT_SET_PERI_REG_MASK(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST); + } +#else + if (!periph_ll_periph_enabled(PERIPH_GDMA_MODULE)) { + periph_ll_disable_clk_set_rst(PERIPH_GDMA_MODULE); + periph_ll_enable_clk_clear_rst(PERIPH_GDMA_MODULE); + } +#endif + ll_cam_dma_reset(cam); return ESP_OK; } @@ -235,12 +310,16 @@ static esp_err_t ll_cam_converter_config(cam_obj_t *cam, const camera_config_t * esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config) { esp_err_t ret = ESP_OK; - if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0) { - REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN); - REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN); - REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST); - REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST); + if (!periph_ll_periph_enabled(PERIPH_LCD_CAM_MODULE)) { + periph_ll_disable_clk_set_rst(PERIPH_LCD_CAM_MODULE); + periph_ll_enable_clk_clear_rst(PERIPH_LCD_CAM_MODULE); } + // 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; @@ -336,8 +415,13 @@ 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) { 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, + ret = esp_intr_alloc_intrstatus( +#if (ESP_IDF_VERSION_MAJOR > 5) + ETS_DMA_IN_CH0_INTR_SOURCE + cam->dma_num, +#else + gdma_periph_signals.groups[0].pairs[cam->dma_num].rx_irq_id, +#endif + ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | CAMERA_ISR_IRAM_FLAG, (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) { @@ -346,7 +430,7 @@ esp_err_t ll_cam_init_isr(cam_obj_t *cam) } ret = esp_intr_alloc_intrstatus(ETS_LCD_CAM_INTR_SOURCE, - ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM, + ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | CAMERA_ISR_IRAM_FLAG, (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) { @@ -489,7 +573,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, 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 || sensor_pid == SC031GS_PID) { + if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID || sensor_pid == GC0308_PID || sensor_pid == HM0360_PID) { cam->in_bytes_per_pixel = 1; // camera sends Y8 } else { cam->in_bytes_per_pixel = 2; // camera sends YU/YV diff --git a/target/private_include/ll_cam.h b/target/private_include/ll_cam.h index 34c8da36dd..df6135f27e 100644 --- a/target/private_include/ll_cam.h +++ b/target/private_include/ll_cam.h @@ -38,6 +38,17 @@ #if __has_include("esp_private/periph_ctrl.h") # include "esp_private/periph_ctrl.h" #endif +#if __has_include("esp_private/gdma.h") +# include "esp_private/gdma.h" +#endif + +#if CONFIG_LCD_CAM_ISR_IRAM_SAFE +#define CAMERA_ISR_IRAM_FLAG ESP_INTR_FLAG_IRAM +#define CAMERA_ISR_IRAM_ATTR IRAM_ATTR +#else +#define CAMERA_ISR_IRAM_FLAG 0 +#define CAMERA_ISR_IRAM_ATTR +#endif #define CAMERA_DBG_PIN_ENABLE 0 #if CAMERA_DBG_PIN_ENABLE @@ -104,6 +115,9 @@ typedef struct { uint8_t dma_num;//ESP32-S3 intr_handle_t dma_intr_handle;//ESP32-S3 +#if SOC_GDMA_SUPPORTED + gdma_channel_handle_t dma_channel_handle;//ESP32-S3 +#endif uint8_t jpeg_mode; uint8_t vsync_pin; @@ -142,6 +156,10 @@ uint8_t ll_cam_get_dma_align(cam_obj_t *cam); bool ll_cam_dma_sizes(cam_obj_t *cam); size_t 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); +#if CONFIG_IDF_TARGET_ESP32S3 +void ll_cam_dma_print_state(cam_obj_t *cam); +void ll_cam_dma_reset(cam_obj_t *cam); +#endif // implemented in cam_hal void ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType_t * HPTaskAwoken); diff --git a/target/xclk.c b/target/xclk.c index f51daa9ce9..b8ae9af0ae 100755 --- a/target/xclk.c +++ b/target/xclk.c @@ -23,6 +23,10 @@ esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz) timer_conf.freq_hz = xclk_freq_hz; timer_conf.speed_mode = LEDC_LOW_SPEED_MODE; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) + timer_conf.deconfigure = false; +#endif + #if ESP_IDF_VERSION_MAJOR >= 4 timer_conf.clk_cfg = LEDC_AUTO_CLK; #endif @@ -34,7 +38,7 @@ esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz) return err; } -esp_err_t camera_enable_out_clock(camera_config_t* config) +esp_err_t camera_enable_out_clock(const camera_config_t* config) { esp_err_t err = xclk_timer_conf(config->ledc_timer, config->xclk_freq_hz); if (err != ESP_OK) { @@ -43,11 +47,14 @@ esp_err_t camera_enable_out_clock(camera_config_t* config) } g_ledc_channel = config->ledc_channel; - ledc_channel_config_t ch_conf; + ledc_channel_config_t ch_conf = {0}; ch_conf.gpio_num = config->pin_xclk; ch_conf.speed_mode = LEDC_LOW_SPEED_MODE; ch_conf.channel = config->ledc_channel; - ch_conf.intr_type = LEDC_INTR_DISABLE; +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6,0,0) + // no need to explicitly configure interrupt, handled in the driver (IDF v6.0 and above) + ch_conf.intr_type = LEDC_INTR_DISABLE; +#endif ch_conf.timer_sel = config->ledc_timer; ch_conf.duty = 1; ch_conf.hpoint = 0; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a8c3d16b75..1e68ddcc1e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,5 @@ idf_component_register(SRC_DIRS . PRIV_INCLUDE_DIRS . - PRIV_REQUIRES test_utils esp32-camera nvs_flash + PRIV_REQUIRES test_utils esp32-camera nvs_flash mbedtls esp_timer EMBED_TXTFILES pictures/testimg.jpeg pictures/test_outside.jpeg pictures/test_inside.jpeg) +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/test/test_camera.c b/test/test_camera.c index f03b7c6a0c..5a9db9caf7 100644 --- a/test/test_camera.c +++ b/test/test_camera.c @@ -7,6 +7,7 @@ #include #include "esp_log.h" #include "driver/i2c.h" +#include "esp_timer.h" #include "esp_camera.h" @@ -18,6 +19,8 @@ #define BOARD_CAMERA_MODEL_ESP32_S3_EYE 1 #endif +#define portTICK_RATE_MS portTICK_PERIOD_MS + // WROVER-KIT PIN Map #if BOARD_WROVER_KIT @@ -141,16 +144,15 @@ static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, fr .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 + .frame_size = frame_size, //QQVGA-UXGAQQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates. - .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 + .jpeg_quality = 12, //0-63, for OV series camera sensors, lower number means higher quality + .fb_count = fb_count, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode. .grab_mode = CAMERA_GRAB_WHEN_EMPTY }; @@ -382,7 +384,7 @@ static void print_rgb888_img(uint8_t *img, int width, int height) static void tjpgd_decode_rgb565(uint8_t *mjpegbuffer, uint32_t size, uint8_t *outbuffer) { - jpg2rgb565(mjpegbuffer, size, outbuffer, JPG_SCALE_NONE); + jpg2rgb565(mjpegbuffer, size, outbuffer, JPEG_IMAGE_SCALE_0); } static void tjpgd_decode_rgb888(uint8_t *mjpegbuffer, uint32_t size, uint8_t *outbuffer)