From 9f72bf117e17b8015813943428ae2a158934901d Mon Sep 17 00:00:00 2001
From: jomjol <30766535+jomjol@users.noreply.github.com>
Date: Sat, 16 Jul 2022 08:09:18 +0200
Subject: [PATCH 1/2] Rolling 20220716
---
README.md | 5 +
code/components/esp32-camera-master.zip | Bin 0 -> 291990 bytes
.../.github/workflows/build.yml | 29 +-
.../.github/workflows/upload_component.yml | 4 +-
.../esp32-camera-master/CMakeLists.txt | 51 +-
code/components/esp32-camera-master/Kconfig | 58 +++
code/components/esp32-camera-master/README.md | 3 +
.../conversions/esp_jpg_decode.c | 8 +-
.../esp32-camera-master/conversions/jpge.cpp | 5 +
.../esp32-camera-master/conversions/to_bmp.c | 20 +-
.../conversions/to_jpg.cpp | 20 +-
.../esp32-camera-master/driver/cam_hal.c | 17 +-
.../esp32-camera-master/driver/esp_camera.c | 41 ++
.../driver/include/esp_camera.h | 17 +
.../driver/include/sensor.h | 10 +
.../esp32-camera-master/driver/sccb.c | 5 +
.../esp32-camera-master/driver/sensor.c | 3 +
.../examples/main/take_picture.c | 5 +
.../esp32-camera-master/idf_component.yml | 5 +-
.../esp32-camera-master/sensors/bf20a6.c | 404 ++++++++++++++
.../esp32-camera-master/sensors/gc0308.c | 11 +-
.../sensors/private_include/bf20a6.h | 27 +
.../sensors/private_include/bf20a6_regs.h | 12 +
.../sensors/private_include/bf20a6_settings.h | 158 ++++++
.../sensors/private_include/gc0308_settings.h | 21 +-
.../sensors/private_include/ov5640_settings.h | 3 +-
.../sensors/private_include/sc030iot.h | 31 ++
.../private_include/sc030iot_settings.h | 491 ++++++++++++++++++
.../sensors/private_include/sc101iot.h | 31 ++
.../private_include/sc101iot_settings.h | 257 +++++++++
.../esp32-camera-master/sensors/sc030iot.c | 335 ++++++++++++
.../esp32-camera-master/sensors/sc101iot.c | 342 ++++++++++++
.../esp32-camera-master/target/esp32/ll_cam.c | 8 +-
.../target/esp32s2/ll_cam.c | 9 +-
.../target/esp32s3/ll_cam.c | 89 +++-
.../target/private_include/ll_cam.h | 6 +
code/main/version.cpp | 4 +-
code/platformio.ini | 5 +-
code/sdkconfig.esp32cam | 3 +
code/version.cpp | 4 +-
firmware/bootloader.bin | Bin 26768 -> 26864 bytes
firmware/firmware.bin | Bin 1776192 -> 1781232 bytes
42 files changed, 2457 insertions(+), 100 deletions(-)
create mode 100644 code/components/esp32-camera-master.zip
create mode 100644 code/components/esp32-camera-master/sensors/bf20a6.c
create mode 100644 code/components/esp32-camera-master/sensors/private_include/bf20a6.h
create mode 100644 code/components/esp32-camera-master/sensors/private_include/bf20a6_regs.h
create mode 100644 code/components/esp32-camera-master/sensors/private_include/bf20a6_settings.h
create mode 100644 code/components/esp32-camera-master/sensors/private_include/sc030iot.h
create mode 100644 code/components/esp32-camera-master/sensors/private_include/sc030iot_settings.h
create mode 100644 code/components/esp32-camera-master/sensors/private_include/sc101iot.h
create mode 100644 code/components/esp32-camera-master/sensors/private_include/sc101iot_settings.h
create mode 100644 code/components/esp32-camera-master/sensors/sc030iot.c
create mode 100644 code/components/esp32-camera-master/sensors/sc101iot.c
diff --git a/README.md b/README.md
index fa174600..82b14ee0 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,11 @@ In other cases you can contact the developer via email:
RAW Bayer
RGB565 | 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" |
+| SC101IOT| 1280 x 720 | color | YUV/YCbCr422
Raw RGB | 1/4.2" |
+| SC030IOT| 640 x 480 | color | YUV/YCbCr422
RAW Bayer | 1/6.5" |
## Important to Remember
diff --git a/code/components/esp32-camera-master/conversions/esp_jpg_decode.c b/code/components/esp32-camera-master/conversions/esp_jpg_decode.c
index a9615e36..52833a73 100644
--- a/code/components/esp32-camera-master/conversions/esp_jpg_decode.c
+++ b/code/components/esp32-camera-master/conversions/esp_jpg_decode.c
@@ -21,6 +21,10 @@
#include "tjpgd.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/tjpgd.h"
+#elif CONFIG_IDF_TARGET_ESP32C3
+#include "esp32c3/rom/tjpgd.h"
+#elif CONFIG_IDF_TARGET_ESP32H2
+#include "esp32h2/rom/tjpgd.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
@@ -57,7 +61,7 @@ static const char * jd_errors[] = {
"Not supported JPEG standard"
};
-static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
+static unsigned int _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
{
uint16_t x = rect->left;
uint16_t y = rect->top;
@@ -73,7 +77,7 @@ static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
return 0;
}
-static uint32_t _jpg_read(JDEC *decoder, uint8_t *buf, uint32_t len)
+static unsigned int _jpg_read(JDEC *decoder, uint8_t *buf, unsigned int len)
{
esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device;
if (jpeg->len && len > (jpeg->len - jpeg->index)) {
diff --git a/code/components/esp32-camera-master/conversions/jpge.cpp b/code/components/esp32-camera-master/conversions/jpge.cpp
index a8ab93e0..dd6790e6 100644
--- a/code/components/esp32-camera-master/conversions/jpge.cpp
+++ b/code/components/esp32-camera-master/conversions/jpge.cpp
@@ -29,7 +29,12 @@ namespace jpge {
if(b){
return b;
}
+ // check if SPIRAM is enabled and allocate on SPIRAM if allocatable
+#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
return heap_caps_malloc(nSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+#else
+ return NULL;
+#endif
}
static inline void jpge_free(void *p) { free(p); }
diff --git a/code/components/esp32-camera-master/conversions/to_bmp.c b/code/components/esp32-camera-master/conversions/to_bmp.c
index 5a54bdba..e267c78f 100644
--- a/code/components/esp32-camera-master/conversions/to_bmp.c
+++ b/code/components/esp32-camera-master/conversions/to_bmp.c
@@ -21,19 +21,6 @@
#include "esp_jpg_decode.h"
#include "esp_system.h"
-#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
-#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
-#include "esp32/spiram.h"
-#elif CONFIG_IDF_TARGET_ESP32S2
-#include "esp32s2/spiram.h"
-#elif CONFIG_IDF_TARGET_ESP32S3
-#include "esp32s3/spiram.h"
-#else
-#error Target CONFIG_IDF_TARGET is not supported
-#endif
-#else // ESP32 Before IDF 4.0
-#include "esp_spiram.h"
-#endif
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
@@ -72,7 +59,12 @@ typedef struct {
static void *_malloc(size_t size)
{
+ // check if SPIRAM is enabled and allocate on SPIRAM if allocatable
+#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+#endif
+ // try allocating in internal memory
+ return malloc(size);
}
//output buffer and image width
@@ -168,7 +160,7 @@ static bool _rgb565_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16
}
//input buffer
-static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
+static unsigned int _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
{
rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
if(buf) {
diff --git a/code/components/esp32-camera-master/conversions/to_jpg.cpp b/code/components/esp32-camera-master/conversions/to_jpg.cpp
index 9b8905a7..24cc2989 100644
--- a/code/components/esp32-camera-master/conversions/to_jpg.cpp
+++ b/code/components/esp32-camera-master/conversions/to_jpg.cpp
@@ -21,21 +21,6 @@
#include "jpge.h"
#include "yuv.h"
-#include "esp_system.h"
-#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
-#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
-#include "esp32/spiram.h"
-#elif CONFIG_IDF_TARGET_ESP32S2
-#include "esp32s2/spiram.h"
-#elif CONFIG_IDF_TARGET_ESP32S3
-#include "esp32s3/spiram.h"
-#else
-#error Target CONFIG_IDF_TARGET is not supported
-#endif
-#else // ESP32 Before IDF 4.0
-#include "esp_spiram.h"
-#endif
-
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
#define TAG ""
@@ -50,7 +35,12 @@ static void *_malloc(size_t size)
if(res) {
return res;
}
+
+ // check if SPIRAM is enabled and is allocatable
+#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+#endif
+ return NULL;
}
static IRAM_ATTR void convert_line_format(uint8_t * src, pixformat_t format, uint8_t * dst, size_t width, size_t in_channels, size_t line)
diff --git a/code/components/esp32-camera-master/driver/cam_hal.c b/code/components/esp32-camera-master/driver/cam_hal.c
index 9b7e12b5..1604f8ac 100644
--- a/code/components/esp32-camera-master/driver/cam_hal.c
+++ b/code/components/esp32-camera-master/driver/cam_hal.c
@@ -18,8 +18,21 @@
#include "ll_cam.h"
#include "cam_hal.h"
-static const char *TAG = "cam_hal";
+#if (ESP_IDF_VERSION_MAJOR == 3) && (ESP_IDF_VERSION_MINOR == 3)
+#include "rom/ets_sys.h"
+#else
+#include "esp_timer.h"
+#if CONFIG_IDF_TARGET_ESP32
+#include "esp32/rom/ets_sys.h" // will be removed in idf v5.0
+#elif CONFIG_IDF_TARGET_ESP32S2
+#include "esp32s2/rom/ets_sys.h"
+#elif CONFIG_IDF_TARGET_ESP32S3
+#include "esp32s3/rom/ets_sys.h"
+#endif
+#endif // ESP_IDF_VERSION_MAJOR
+#define ESP_CAMERA_ETS_PRINTF ets_printf
+static const char *TAG = "cam_hal";
static cam_obj_t *cam_obj = NULL;
static const uint32_t JPEG_SOI_MARKER = 0xFFD8FF; // written in little-endian for esp32
@@ -93,7 +106,7 @@ void IRAM_ATTR ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType
if (xQueueSendFromISR(cam->event_queue, (void *)&cam_event, HPTaskAwoken) != pdTRUE) {
ll_cam_stop(cam);
cam->state = CAM_STATE_IDLE;
- ESP_EARLY_LOGE(TAG, "EV-%s-OVF", cam_event==CAM_IN_SUC_EOF_EVENT ? "EOF" : "VSYNC");
+ ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: EV-%s-OVF\r\n"), cam_event==CAM_IN_SUC_EOF_EVENT ? DRAM_STR("EOF") : DRAM_STR("VSYNC"));
}
}
diff --git a/code/components/esp32-camera-master/driver/esp_camera.c b/code/components/esp32-camera-master/driver/esp_camera.c
index 5b671c0e..8327445c 100644
--- a/code/components/esp32-camera-master/driver/esp_camera.c
+++ b/code/components/esp32-camera-master/driver/esp_camera.c
@@ -57,6 +57,15 @@
#if CONFIG_BF3005_SUPPORT
#include "bf3005.h"
#endif
+#if CONFIG_BF20A6_SUPPORT
+#include "bf20a6.h"
+#endif
+#if CONFIG_SC101IOT_SUPPORT
+#include "sc101iot.h"
+#endif
+#if CONFIG_SC030IOT_SUPPORT
+#include "sc030iot.h"
+#endif
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
@@ -119,6 +128,15 @@ static const sensor_func_t g_sensors[] = {
#if CONFIG_BF3005_SUPPORT
{bf3005_detect, bf3005_init},
#endif
+#if CONFIG_BF20A6_SUPPORT
+ {bf20a6_detect, bf20a6_init},
+#endif
+#if CONFIG_SC101IOT_SUPPORT
+ {sc101iot_detect, sc101iot_init},
+#endif
+#if CONFIG_SC030IOT_SUPPORT
+ {sc030iot_detect, sc030iot_init},
+#endif
};
static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
@@ -218,6 +236,23 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
return ESP_OK;
}
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+static pixformat_t get_output_data_format(camera_conv_mode_t conv_mode)
+{
+ pixformat_t format = PIXFORMAT_RGB565;
+ switch (conv_mode) {
+ case YUV422_TO_YUV420:
+ format = PIXFORMAT_YUV420;
+ break;
+ case YUV422_TO_RGB565: // default format is RGB565
+ default:
+ break;
+ }
+ ESP_LOGD(TAG, "Convert to %d format enabled", format);
+ return format;
+}
+#endif
+
esp_err_t esp_camera_init(const camera_config_t *config)
{
esp_err_t err;
@@ -256,6 +291,7 @@ esp_err_t esp_camera_init(const camera_config_t *config)
s_state->sensor.status.framesize = frame_size;
s_state->sensor.pixformat = pix_format;
+
ESP_LOGD(TAG, "Setting frame size to %dx%d", resolution[frame_size].width, resolution[frame_size].height);
if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
ESP_LOGE(TAG, "Failed to set frame size");
@@ -263,6 +299,11 @@ esp_err_t esp_camera_init(const camera_config_t *config)
goto fail;
}
s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+ if(config->conv_mode) {
+ s_state->sensor.pixformat = get_output_data_format(config->conv_mode); // If conversion enabled, change the out data format by conversion mode
+ }
+#endif
if (s_state->sensor.id.PID == OV2640_PID) {
s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);
diff --git a/code/components/esp32-camera-master/driver/include/esp_camera.h b/code/components/esp32-camera-master/driver/include/esp_camera.h
index b6047d31..2025bb40 100644
--- a/code/components/esp32-camera-master/driver/include/esp_camera.h
+++ b/code/components/esp32-camera-master/driver/include/esp_camera.h
@@ -70,6 +70,7 @@
#include "driver/ledc.h"
#include "sensor.h"
#include "sys/time.h"
+#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
@@ -91,6 +92,19 @@ typedef enum {
CAMERA_FB_IN_DRAM /*!< Frame buffer is placed in internal DRAM */
} camera_fb_location_t;
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+/**
+ * @brief Camera RGB\YUV conversion mode
+ */
+typedef enum {
+ CONV_DISABLE,
+ RGB565_TO_YUV422,
+
+ YUV422_TO_RGB565,
+ YUV422_TO_YUV420
+} camera_conv_mode_t;
+#endif
+
/**
* @brief Configuration structure for camera initialization
*/
@@ -124,6 +138,9 @@ typedef struct {
size_t fb_count; /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) */
camera_fb_location_t fb_location; /*!< The location where the frame buffer will be allocated */
camera_grab_mode_t grab_mode; /*!< When buffers should be filled */
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+ camera_conv_mode_t conv_mode; /*!< RGB<->YUV Conversion mode */
+#endif
} camera_config_t;
/**
diff --git a/code/components/esp32-camera-master/driver/include/sensor.h b/code/components/esp32-camera-master/driver/include/sensor.h
index b2bf55f1..d5ec7463 100644
--- a/code/components/esp32-camera-master/driver/include/sensor.h
+++ b/code/components/esp32-camera-master/driver/include/sensor.h
@@ -27,6 +27,9 @@ typedef enum {
GC032A_PID = 0x232a,
GC0308_PID = 0x9b,
BF3005_PID = 0x30,
+ BF20A6_PID = 0x20a6,
+ SC101IOT_PID = 0xda4a,
+ SC030IOT_PID = 0x9a46,
} camera_pid_t;
typedef enum {
@@ -40,6 +43,9 @@ typedef enum {
CAMERA_GC032A,
CAMERA_GC0308,
CAMERA_BF3005,
+ CAMERA_BF20A6,
+ CAMERA_SC101IOT,
+ CAMERA_SC030IOT,
CAMERA_MODEL_MAX,
CAMERA_NONE,
} camera_model_t;
@@ -55,11 +61,15 @@ typedef enum {
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
BF3005_SCCB_ADDR = 0x6E,
+ BF20A6_SCCB_ADDR = 0x6E,
+ SC101IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
+ SC030IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
} camera_sccb_addr_t;
typedef enum {
PIXFORMAT_RGB565, // 2BPP/RGB565
PIXFORMAT_YUV422, // 2BPP/YUV422
+ PIXFORMAT_YUV420, // 1.5BPP/YUV420
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
PIXFORMAT_JPEG, // JPEG/COMPRESSED
PIXFORMAT_RGB888, // 3BPP/RGB888
diff --git a/code/components/esp32-camera-master/driver/sccb.c b/code/components/esp32-camera-master/driver/sccb.c
index 314dd982..edc417f8 100644
--- a/code/components/esp32-camera-master/driver/sccb.c
+++ b/code/components/esp32-camera-master/driver/sccb.c
@@ -25,6 +25,11 @@ static const char* TAG = "sccb";
#include "driver/i2c.h"
+// support IDF 5.x
+#ifndef portTICK_RATE_MS
+#define portTICK_RATE_MS portTICK_PERIOD_MS
+#endif
+
#define SCCB_FREQ CONFIG_SCCB_CLK_FREQ /*!< I2C master frequency*/
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
diff --git a/code/components/esp32-camera-master/driver/sensor.c b/code/components/esp32-camera-master/driver/sensor.c
index bf6d313f..2f4c9711 100644
--- a/code/components/esp32-camera-master/driver/sensor.c
+++ b/code/components/esp32-camera-master/driver/sensor.c
@@ -13,6 +13,9 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
{CAMERA_GC032A, "GC032A", GC032A_SCCB_ADDR, GC032A_PID, FRAMESIZE_VGA, false},
{CAMERA_GC0308, "GC0308", GC0308_SCCB_ADDR, GC0308_PID, FRAMESIZE_VGA, false},
{CAMERA_BF3005, "BF3005", BF3005_SCCB_ADDR, BF3005_PID, FRAMESIZE_VGA, false},
+ {CAMERA_BF20A6, "BF20A6", BF20A6_SCCB_ADDR, BF20A6_PID, FRAMESIZE_VGA, false},
+ {CAMERA_SC101IOT, "SC101IOT", SC101IOT_SCCB_ADDR, SC101IOT_PID, FRAMESIZE_HD, false},
+ {CAMERA_SC030IOT, "SC030IOT", SC030IOT_SCCB_ADDR, SC030IOT_PID, FRAMESIZE_VGA, false},
};
const resolution_info_t resolution[FRAMESIZE_INVALID] = {
diff --git a/code/components/esp32-camera-master/examples/main/take_picture.c b/code/components/esp32-camera-master/examples/main/take_picture.c
index 1cbad908..1fb1039d 100644
--- a/code/components/esp32-camera-master/examples/main/take_picture.c
+++ b/code/components/esp32-camera-master/examples/main/take_picture.c
@@ -38,6 +38,11 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
+// support IDF 5.x
+#ifndef portTICK_RATE_MS
+#define portTICK_RATE_MS portTICK_PERIOD_MS
+#endif
+
#include "esp_camera.h"
#define BOARD_WROVER_KIT 1
diff --git a/code/components/esp32-camera-master/idf_component.yml b/code/components/esp32-camera-master/idf_component.yml
index 848e1cd8..2b98f8d0 100644
--- a/code/components/esp32-camera-master/idf_component.yml
+++ b/code/components/esp32-camera-master/idf_component.yml
@@ -1,5 +1,2 @@
description: ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors.
-targets:
- - esp32
- - esp32s2
- - esp32s3
+url: https://github.com/espressif/esp32-camera
diff --git a/code/components/esp32-camera-master/sensors/bf20a6.c b/code/components/esp32-camera-master/sensors/bf20a6.c
new file mode 100644
index 00000000..b1179c30
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/bf20a6.c
@@ -0,0 +1,404 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "sccb.h"
+#include "bf20a6.h"
+#include "bf20a6_regs.h"
+#include "bf20a6_settings.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char *TAG = "bf20a6";
+#endif
+
+#define H8(v) ((v)>>8)
+#define L8(v) ((v)&0xff)
+
+//#define REG_DEBUG_ON
+
+static int read_reg(uint8_t slv_addr, const uint16_t reg)
+{
+ int ret = SCCB_Read(slv_addr, reg);
+ // ESP_LOGI(TAG, "READ Register 0x%02x VALUE: 0x%02x", reg, ret);
+#ifdef REG_DEBUG_ON
+ if (ret < 0) {
+ ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
+ }
+#endif
+ return ret;
+}
+
+static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
+{
+ int ret = SCCB_Write(slv_addr, reg, value);
+#ifdef REG_DEBUG_ON
+ if (ret < 0) {
+ ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
+ }
+#endif
+ return ret;
+}
+
+#ifdef DEBUG_PRINT_REG
+static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
+{
+ return (read_reg(slv_addr, reg) & mask) == mask;
+}
+
+static void print_regs(uint8_t slv_addr)
+{
+ vTaskDelay(pdMS_TO_TICKS(100));
+ ESP_LOGI(TAG, "REG list look ======================");
+ for (size_t i = 0xf0; i <= 0xfe; i++) {
+ ESP_LOGI(TAG, "reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+ }
+ ESP_LOGI(TAG, "\npage 0 ===");
+ write_reg(slv_addr, 0xfe, 0x00); // page 0
+ for (size_t i = 0x03; i <= 0x24; i++) {
+ ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+ }
+ for (size_t i = 0x40; i <= 0x95; i++) {
+ ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+ }
+ ESP_LOGI(TAG, "\npage 3 ===");
+ write_reg(slv_addr, 0xfe, 0x03); // page 3
+ for (size_t i = 0x01; i <= 0x43; i++) {
+ ESP_LOGI(TAG, "p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+ }
+}
+
+static int read_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
+{
+ int i = 0, ret = 0;
+ while (regs[i][0] != REGLIST_TAIL) {
+ if (regs[i][0] == REG_DLY) {
+ vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+ } else {
+ ret = read_reg(slv_addr, regs[i][0]);
+ }
+ i++;
+ }
+ return ret;
+}
+#endif
+
+static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
+{
+ int ret = 0;
+
+ ret = SCCB_Read(sensor->slv_addr, reg);
+ if (ret < 0) {
+ return ret;
+ }
+ uint8_t mask = ((1 << length) - 1) << offset;
+ value = (ret & ~mask) | ((value << offset) & mask);
+ ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
+ return ret;
+}
+
+static int write_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
+{
+ int i = 0, ret = 0;
+ while (!ret && regs[i][0] != REGLIST_TAIL) {
+ if (regs[i][0] == REG_DLY) {
+ vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+ } else {
+ ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
+ }
+ i++;
+ }
+ return ret;
+}
+
+static int reset(sensor_t *sensor)
+{
+ int ret;
+ // Software Reset: clear all registers and reset them to their default values
+ ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x01);
+ if (ret) {
+ ESP_LOGE(TAG, "Software Reset FAILED!");
+ return ret;
+ }
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+
+ ret = write_regs(sensor->slv_addr, bf20a6_default_init_regs);
+ if (ret == 0) {
+ ESP_LOGD(TAG, "Camera defaults loaded");
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ }
+
+ // int test_value = read_regs(sensor->slv_addr, bf20a6_default_init_regs);
+
+ return ret;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+ int ret = 0;
+ switch (pixformat) {
+ case PIXFORMAT_YUV422:
+ set_reg_bits(sensor, 0x12, 0, 1, 0);
+ break;
+ case PIXFORMAT_RAW:
+ set_reg_bits(sensor, 0x12, 0, 1, 0x1);
+ break;
+ default:
+ ESP_LOGW(TAG, "set_pix unsupport format");
+ ret = -1;
+ break;
+ }
+ if (ret == 0) {
+ sensor->pixformat = pixformat;
+ ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
+ }
+
+ return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+ int ret = 0;
+ if (framesize > FRAMESIZE_VGA) {
+ return -1;
+ }
+ uint16_t w = resolution[framesize].width;
+ uint16_t h = resolution[framesize].height;
+
+ sensor->status.framesize = framesize;
+
+ // Write MSBs
+ ret |= SCCB_Write(sensor->slv_addr, 0x17, 0);
+ ret |= SCCB_Write(sensor->slv_addr, 0x18, w >> 2);
+
+ ret |= SCCB_Write(sensor->slv_addr, 0x19, 0);
+ ret |= SCCB_Write(sensor->slv_addr, 0x1a, h >> 2);
+
+ // Write LSBs
+ ret |= SCCB_Write(sensor->slv_addr, 0x1b, 0);
+
+ if ((w <= 320) && (h <= 240)) {
+ ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 4));
+ ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 4));
+
+ ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 4));
+
+ ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 4));
+
+ } else if ((w <= 640) && (h <= 480)) {
+ ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 8));
+ ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 8));
+
+ ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 8));
+
+ ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 8));
+ }
+
+ // Delay
+ vTaskDelay(30 / portTICK_PERIOD_MS);
+
+ return ret;
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+ int ret = 0;
+ sensor->status.hmirror = enable;
+ //ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+ ret |= set_reg_bits(sensor, 0x4a, 3, 0x01, enable);
+ if (ret == 0) {
+ ESP_LOGD(TAG, "Set h-mirror to: %d", enable);
+ }
+ return ret;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+ int ret = 0;
+ sensor->status.vflip = enable;
+ //ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+ ret |= set_reg_bits(sensor, 0x4a, 2, 0x01, enable);
+ if (ret == 0) {
+ ESP_LOGD(TAG, "Set v-flip to: %d", enable);
+ }
+ return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int value)
+{
+ int ret = 0;
+ ret = write_reg(sensor->slv_addr, 0xb6, value);
+ if (ret == 0) {
+ sensor->status.colorbar = value;
+ ESP_LOGD(TAG, "Set colorbar to: %d", value);
+ }
+ return ret;
+}
+
+static int set_sharpness(sensor_t *sensor, int level)
+{
+ int ret = 0;
+ ret = SCCB_Write(sensor->slv_addr, 0x70, level);
+ if (ret == 0) {
+ ESP_LOGD(TAG, "Set sharpness to: %d", level);
+ sensor->status.sharpness = level;
+ }
+ return ret;
+}
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+ int ret = 0;
+ if (mask > 0xFF) {
+ ESP_LOGE(TAG, "mask should not more than 0xff");
+ } else {
+ ret = read_reg(sensor->slv_addr, reg);
+ }
+ if (ret > 0) {
+ ret &= mask;
+ }
+ return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+ int ret = 0;
+ if (mask > 0xFF) {
+ ESP_LOGE(TAG, "mask should not more than 0xff");
+ } else {
+ ret = read_reg(sensor->slv_addr, reg);
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ value = (ret & ~mask) | (value & mask);
+
+ if (mask > 0xFF) {
+
+ } else {
+ ret = write_reg(sensor->slv_addr, reg, value);
+ }
+ return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+ // write_reg(sensor->slv_addr, 0xfe, 0x00);
+ sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x6f);
+ sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0xd6);
+ sensor->status.saturation = 0;
+ sensor->status.sharpness = SCCB_Read(sensor->slv_addr, 0x70);
+ sensor->status.denoise = 0;
+ sensor->status.ae_level = 0;
+ sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x13);
+ sensor->status.awb = 0;
+ sensor->status.dcw = 0;
+ sensor->status.agc = 0;
+ sensor->status.aec = 0;
+ sensor->status.hmirror = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x01);
+ sensor->status.vflip = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x02);
+ sensor->status.colorbar = 0;
+ sensor->status.bpc = 0;
+ sensor->status.wpc = 0;
+ sensor->status.raw_gma = 0;
+ sensor->status.lenc = 0;
+ sensor->status.quality = 0;
+ sensor->status.special_effect = 0;
+ sensor->status.wb_mode = 0;
+ sensor->status.awb_gain = 0;
+ sensor->status.agc_gain = 0;
+ sensor->status.aec_value = 0;
+ sensor->status.aec2 = 0;
+ return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val)
+{
+ ESP_LOGW(TAG, "dummy Unsupported");
+ return -1;
+}
+static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
+{
+ ESP_LOGW(TAG, "gainceiling Unsupported");
+ return -1;
+}
+
+int bf20a6_detect(int slv_addr, sensor_id_t *id)
+{
+ if (BF20A6_SCCB_ADDR == slv_addr) {
+ uint8_t MIDL = SCCB_Read(slv_addr, SENSOR_ID_LOW);
+ uint8_t MIDH = SCCB_Read(slv_addr, SENSOR_ID_HIGH);
+ uint16_t PID = MIDH << 8 | MIDL;
+ if (BF20A6_PID == PID) {
+ id->PID = PID;
+ return PID;
+ } else {
+ ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+ }
+ }
+ return 0;
+}
+
+int bf20a6_init(sensor_t *sensor)
+{
+ sensor->init_status = init_status;
+ sensor->reset = reset;
+ sensor->set_pixformat = set_pixformat;
+ sensor->set_framesize = set_framesize;
+ sensor->set_contrast = set_dummy;
+ sensor->set_brightness = set_dummy;
+ sensor->set_saturation = set_dummy;
+ sensor->set_sharpness = set_sharpness;
+ sensor->set_denoise = set_dummy;
+ sensor->set_gainceiling = set_gainceiling_dummy;
+ sensor->set_quality = set_dummy;
+ sensor->set_colorbar = set_colorbar;
+ sensor->set_whitebal = set_dummy;
+ sensor->set_gain_ctrl = set_dummy;
+ sensor->set_exposure_ctrl = set_dummy;
+ sensor->set_hmirror = set_hmirror; // set_hmirror;
+ sensor->set_vflip = set_vflip; // set_vflip;
+
+ sensor->set_aec2 = set_dummy;
+ sensor->set_awb_gain = set_dummy;
+ sensor->set_agc_gain = set_dummy;
+ sensor->set_aec_value = set_dummy;
+
+ sensor->set_special_effect = set_dummy;
+ sensor->set_wb_mode = set_dummy;
+ sensor->set_ae_level = set_dummy;
+
+ sensor->set_dcw = set_dummy;
+ sensor->set_bpc = set_dummy;
+ sensor->set_wpc = set_dummy;
+
+ sensor->set_raw_gma = set_dummy;
+ sensor->set_lenc = set_dummy;
+
+ sensor->get_reg = get_reg;
+ sensor->set_reg = set_reg;
+ sensor->set_res_raw = NULL;
+ sensor->set_pll = NULL;
+ sensor->set_xclk = NULL;
+
+ ESP_LOGD(TAG, "BF20A6 Attached");
+ return 0;
+}
diff --git a/code/components/esp32-camera-master/sensors/gc0308.c b/code/components/esp32-camera-master/sensors/gc0308.c
index 8b106a3a..f19025eb 100644
--- a/code/components/esp32-camera-master/sensors/gc0308.c
+++ b/code/components/esp32-camera-master/sensors/gc0308.c
@@ -88,10 +88,10 @@ static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t
return ret;
}
-static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
+static int write_regs(uint8_t slv_addr, const uint8_t (*regs)[2], size_t regs_size)
{
int i = 0, ret = 0;
- while (!ret && regs[i][0] != REGLIST_TAIL) {
+ while (!ret && (i < regs_size)) {
if (regs[i][0] == REG_DLY) {
vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
} else {
@@ -132,11 +132,12 @@ static int reset(sensor_t *sensor)
ESP_LOGE(TAG, "Software Reset FAILED!");
return ret;
}
- vTaskDelay(100 / portTICK_PERIOD_MS);
- ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs);
+
+ vTaskDelay(80 / portTICK_PERIOD_MS);
+ ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs, sizeof(gc0308_sensor_default_regs)/(sizeof(uint8_t) * 2));
if (ret == 0) {
ESP_LOGD(TAG, "Camera defaults loaded");
- vTaskDelay(100 / portTICK_PERIOD_MS);
+ vTaskDelay(80 / portTICK_PERIOD_MS);
write_reg(sensor->slv_addr, 0xfe, 0x00);
#ifdef CONFIG_IDF_TARGET_ESP32
set_reg_bits(sensor->slv_addr, 0x28, 4, 0x07, 1); //frequency division for esp32, ensure pclk <= 15MHz
diff --git a/code/components/esp32-camera-master/sensors/private_include/bf20a6.h b/code/components/esp32-camera-master/sensors/private_include/bf20a6.h
new file mode 100644
index 00000000..8c925eb5
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/private_include/bf20a6.h
@@ -0,0 +1,27 @@
+
+#ifndef __BF20A6_H__
+#define __BF20A6_H__
+
+#include "sensor.h"
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ * 0: Can't detect this sensor
+ * Nonzero: This sensor has been detected
+ */
+int bf20a6_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ * Always 0
+ */
+int bf20a6_init(sensor_t *sensor);
+
+#endif // __BF20A6_H__
diff --git a/code/components/esp32-camera-master/sensors/private_include/bf20a6_regs.h b/code/components/esp32-camera-master/sensors/private_include/bf20a6_regs.h
new file mode 100644
index 00000000..ab1ff69e
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/private_include/bf20a6_regs.h
@@ -0,0 +1,12 @@
+/*
+ * BF20A6 register definitions.
+ */
+#ifndef __BF20A6_REG_REGS_H__
+#define __BF20A6_REG_REGS_H__
+
+#define SENSOR_ID_HIGH 0XFC
+#define SENSOR_ID_LOW 0XFD
+#define RESET_RELATED 0XF2
+
+
+#endif //__BF20A6_REG_REGS_H__
diff --git a/code/components/esp32-camera-master/sensors/private_include/bf20a6_settings.h b/code/components/esp32-camera-master/sensors/private_include/bf20a6_settings.h
new file mode 100644
index 00000000..0414bbac
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/private_include/bf20a6_settings.h
@@ -0,0 +1,158 @@
+
+#include
+
+#define REG_DLY 0xffff
+#define REGLIST_TAIL 0xffff /* Array end token */
+
+static const uint16_t bf20a6_default_init_regs[][2] = {
+ {0xf2,0x01},
+ {0x12,0x20},
+ {0x3a,0x00},
+ {0xe1,0x92},
+ {0xe3,0x12},// PLL Control, important for framerate(choice: 0x02\0x12\0x22\0x32\0x82)
+ {0xe0,0x00},
+ {0x2a,0x98},
+ {0xcd,0x17},
+ {0xc0,0x10},
+ {0xc6,0x1d},
+ {0x10,0x35},
+ {0xe2,0x09},
+ {0xe4,0x72},
+ {0xe5,0x22},
+ {0xe6,0x24},
+ {0xe7,0x64},
+ {0xe8,0xa2}, // DVP:a2}, SPI:f2 VDDIO=1.8V,E8[2]=1},VDDIO=2.8V,E8[2]=0},
+ {0x4a,0x00},
+ {0x00,0x03},
+ {0x1f,0x02},
+ {0x22,0x02},
+ {0x0c,0x31},
+
+ {0x00,0x00},
+ {0x60,0x81},
+ {0x61,0x81},
+
+ {0xa0,0x08},
+ {0x01,0x1a},
+ // {0x01,0x1a},
+ // {0x01,0x1a},
+ // {0x02,0x15},
+ // {0x02,0x15},
+ {0x02,0x15},
+ {0x13,0x08},
+ {0x8a,0x96},
+ {0x8b,0x06},
+ {0x87,0x18},
+
+
+ {0x34,0x48}, // lens
+ {0x35,0x40},
+ {0x36,0x40},
+
+ {0x71,0x44},
+ {0x72,0x48},
+ {0x74,0xa2},
+ {0x75,0xa9},
+ {0x78,0x12},
+ {0x79,0xa0},
+ {0x7a,0x94},
+ {0x7c,0x97},
+ {0x40,0x30},
+ {0x41,0x30},
+ {0x42,0x28},
+ {0x43,0x1f},
+ {0x44,0x1c},
+ {0x45,0x16},
+ {0x46,0x13},
+ {0x47,0x10},
+ {0x48,0x0D},
+ {0x49,0x0C},
+ {0x4B,0x0A},
+ {0x4C,0x0B},
+ {0x4E,0x09},
+ {0x4F,0x08},
+ {0x50,0x08},
+
+
+ {0x5f,0x29},
+ {0x23,0x33},
+ {0xa1,0x10}, // AWB
+ {0xa2,0x0d},
+ {0xa3,0x30},
+ {0xa4,0x06},
+ {0xa5,0x22},
+ {0xa6,0x56},
+ {0xa7,0x18},
+ {0xa8,0x1a},
+ {0xa9,0x12},
+ {0xaa,0x12},
+ {0xab,0x16},
+ {0xac,0xb1},
+ {0xba,0x12},
+ {0xbb,0x12},
+ {0xad,0x12},
+ {0xae,0x56},
+ {0xaf,0x0a},
+ {0x3b,0x30},
+ {0x3c,0x12},
+ {0x3d,0x22},
+ {0x3e,0x3f},
+ {0x3f,0x28},
+ {0xb8,0xc3},
+ {0xb9,0xa3},
+ {0x39,0x47}, // pure color threshold
+ {0x26,0x13},
+ {0x27,0x16},
+ {0x28,0x14},
+ {0x29,0x18},
+ {0xee,0x0d},
+
+
+ {0x13,0x05},
+ {0x24,0x3C},
+ {0x81,0x20},
+ {0x82,0x40},
+ {0x83,0x30},
+ {0x84,0x58},
+ {0x85,0x30},
+ {0x92,0x08},
+ {0x86,0x80},
+ {0x8a,0x96},
+ {0x91,0xff},
+ {0x94,0x62},
+ {0x9a,0x18}, // outdoor threshold
+ {0xf0,0x45}, // integral time control, important for framerate(choice: 0x46\0x45\0x44..)
+ {0x51,0x17}, // color normal
+ {0x52,0x03},
+ {0x53,0x5F},
+ {0x54,0x47},
+ {0x55,0x66},
+ {0x56,0x0F},
+ {0x7e,0x14},
+ {0x57,0x36}, // color
+ {0x58,0x2A},
+ {0x59,0xAA},
+ {0x5a,0xA8},
+ {0x5b,0x43},
+ {0x5c,0x10},
+ {0x5d,0x00},
+ {0x7d,0x36},
+ {0x5e,0x10},
+
+ {0xd6,0x88}, // contrast
+ {0xd5,0x20}, // bright
+ {0xb0,0x84}, // low light ctrl in gray section
+ {0xb5,0x08}, // the threshold of GLB_GAIN
+ {0xb1,0xc8}, // saturation
+ {0xb2,0xc0},
+ {0xb3,0xd0},
+ {0xb4,0xB0},
+
+ {0x32,0x10},
+ // {0x8a,0x00},
+ // {0x8b,0x10},
+ {0xa0,0x09},
+ {0x00,0x03},
+ {0x0b,0x02},
+ {REGLIST_TAIL, 0x00},
+};
diff --git a/code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h b/code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h
index 32ef3816..adf5f28d 100644
--- a/code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h
+++ b/code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h
@@ -3,10 +3,9 @@
#include
-#define REG_DLY 0xffff
-#define REGLIST_TAIL 0x0000 /* Array end token */
+#define REG_DLY 0xff
-static const uint16_t gc0308_sensor_default_regs[][2] = {
+static const uint8_t gc0308_sensor_default_regs[][2] = {
{0xfe, 0x00},
{0xec, 0x20},
{0x05, 0x00},
@@ -239,7 +238,21 @@ static const uint16_t gc0308_sensor_default_regs[][2] = {
{0x65, 0xd3},
{0x66, 0x60},
{0xfe, 0x00},
- {REGLIST_TAIL, 0x00},
+
+ {0x01, 0x32}, //frame setting
+ {0x02, 0x0c},
+ {0x0f, 0x01},
+ {0xe2, 0x00},
+ {0xe3, 0x78},
+ {0xe4, 0x00},
+ {0xe5, 0xfe},
+ {0xe6, 0x01},
+ {0xe7, 0xe0},
+ {0xe8, 0x01},
+ {0xe9, 0xe0},
+ {0xea, 0x01},
+ {0xeb, 0xe0},
+ {0xfe, 0x00},
};
#endif
diff --git a/code/components/esp32-camera-master/sensors/private_include/ov5640_settings.h b/code/components/esp32-camera-master/sensors/private_include/ov5640_settings.h
index fec7d679..f52572fa 100644
--- a/code/components/esp32-camera-master/sensors/private_include/ov5640_settings.h
+++ b/code/components/esp32-camera-master/sensors/private_include/ov5640_settings.h
@@ -42,7 +42,8 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
{ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
//sys reset
- {0x3000, 0x00},
+ {0x3000, 0x20}, // reset MCU
+ {REG_DLY, 10}, // delay 10ms
{0x3002, 0x1c},
//clock enable
diff --git a/code/components/esp32-camera-master/sensors/private_include/sc030iot.h b/code/components/esp32-camera-master/sensors/private_include/sc030iot.h
new file mode 100644
index 00000000..19298b76
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/private_include/sc030iot.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * SC030IOT DVP driver.
+ *
+ */
+#ifndef __SC030IOT_H__
+#define __SC030IOT_H__
+
+#include "sensor.h"
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ * 0: Can't detect this sensor
+ * Nonzero: This sensor has been detected
+ */
+int sc030iot_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ * Always 0
+ */
+int sc030iot_init(sensor_t *sensor);
+
+#endif // __SC030IOT_H__
diff --git a/code/components/esp32-camera-master/sensors/private_include/sc030iot_settings.h b/code/components/esp32-camera-master/sensors/private_include/sc030iot_settings.h
new file mode 100644
index 00000000..56f5654c
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/private_include/sc030iot_settings.h
@@ -0,0 +1,491 @@
+//version: V01P00_20220303
+//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
+//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
+//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
+//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
+//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
+//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
+//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
+//pin :BIT0 pwdn// BIT1:reset
+//avdd 0:3.3V// 1:2.5V// 2:1.8V
+//dovdd 0:2.8V// 1:2.5V// 2:1.8V
+//dvdd 0:1.8V// 1:1.5V// 2:1.2V
+
+/*
+[DataBase]
+DBName=Dothinkey
+
+[Vendor]
+VendorName=SmartSens
+
+[Sensor]
+SensorName=SC031IOT
+width=640
+height=480
+port=1
+type=2
+pin=3
+SlaveID=0xd0
+mode=0
+FlagReg=0xf7
+FlagMask=0xff
+FlagData=0xfa
+FlagReg1=0xf8
+FlagMask1=0xff
+FlagData1=0x46
+outformat=0
+mclk=20
+avdd=2.80000
+dovdd=2.800000
+dvdd=1.5
+
+Ext0=0
+Ext1=0
+Ext2=0
+AFVCC=0.0000
+VPP=0.000000
+*/
+#include
+
+static const uint8_t sc030iot_default_init_regs[][2] = {
+ {0xf0, 0x30},
+ {0x01, 0xff},
+ {0x02, 0xff},
+ {0x22, 0x07},
+ {0x19, 0xff},
+ {0x3f, 0x82},
+ {0x30, 0x02},
+ {0xf0, 0x01},
+ {0x70, 0x00},
+ {0x71, 0x80},
+ {0x72, 0x20},
+ {0x73, 0x00},
+ {0x74, 0xe0},
+ {0x75, 0x10},
+ {0x76, 0x81},
+ {0x77, 0x88},
+ {0x78, 0xe1},
+ {0x79, 0x01},
+ {0xf5, 0x01},
+ {0xf4, 0x0a},
+ {0xf0, 0x36},
+ {0x37, 0x79},
+ {0x31, 0x82},
+ {0x3e, 0x60},
+ {0x30, 0xf0},
+ {0x33, 0x33},
+ {0xf0, 0x32},
+ {0x48, 0x02},
+ {0xf0, 0x33},
+ {0x02, 0x12},
+ {0x7c, 0x02},
+ {0x7d, 0x0e},
+ {0xa2, 0x04},
+ {0x5e, 0x06},
+ {0x5f, 0x0a},
+ {0x0b, 0x58},
+ {0x06, 0x38},
+ {0xf0, 0x32},
+ {0x48, 0x02},
+ {0xf0, 0x39},
+ {0x02, 0x70},
+ {0xf0, 0x45},
+ {0x09, 0x1c},
+ {0xf0, 0x37},
+ {0x22, 0x0d},
+ {0xf0, 0x33},
+ {0x33, 0x10},
+ {0xb1, 0x80},
+ {0x34, 0x40},
+ {0x0b, 0x54},
+ {0xb2, 0x78},
+ {0xf0, 0x36},
+ {0x11, 0x80},
+ {0xf0, 0x30},
+ {0x38, 0x44},
+ {0xf0, 0x33},
+ {0xb3, 0x51},
+ {0x01, 0x10},
+ {0x0b, 0x6c},
+ {0x06, 0x24},
+ {0xf0, 0x36},
+ {0x31, 0x82},
+ {0x3e, 0x60},
+ {0x30, 0xf0},
+ {0x33, 0x33},
+ {0xf0, 0x34},
+ {0x9f, 0x02},
+ {0xa6, 0x40},
+ {0xa7, 0x47},
+ {0xe8, 0x5f},
+ {0xa8, 0x51},
+ {0xa9, 0x44},
+ {0xe9, 0x36},
+ {0xf0, 0x33},
+ {0xb3, 0x51},
+ {0x64, 0x17},
+ {0x90, 0x01},
+ {0x91, 0x03},
+ {0x92, 0x07},
+ {0x01, 0x10},
+ {0x93, 0x10},
+ {0x94, 0x10},
+ {0x95, 0x10},
+ {0x96, 0x01},
+ {0x97, 0x07},
+ {0x98, 0x1f},
+ {0x99, 0x10},
+ {0x9a, 0x20},
+ {0x9b, 0x28},
+ {0x9c, 0x28},
+ {0xf0, 0x36},
+ {0x70, 0x54},
+ {0xb6, 0x40},
+ {0xb7, 0x41},
+ {0xb8, 0x43},
+ {0xb9, 0x47},
+ {0xba, 0x4f},
+ {0xb0, 0x8b},
+ {0xb1, 0x8b},
+ {0xb2, 0x8b},
+ {0xb3, 0x9b},
+ {0xb4, 0xb8},
+ {0xb5, 0xf0},
+ {0x7e, 0x41},
+ {0x7f, 0x47},
+ {0x77, 0x80},
+ {0x78, 0x84},
+ {0x79, 0x8a},
+ {0xa0, 0x47},
+ {0xa1, 0x5f},
+ {0x96, 0x43},
+ {0x97, 0x44},
+ {0x98, 0x54},
+ {0xf0, 0x00},
+ {0xf0, 0x01},
+ {0x73, 0x00},
+ {0x74, 0xe0},
+ {0x70, 0x00},
+ {0x71, 0x80},
+ {0xf0, 0x36},
+ {0x37, 0x74},
+ {0xf0, 0x3f},
+ {0x03, 0xa1},
+ {0xf0, 0x36},//cvbs_off
+ {0x11, 0x80},
+ {0xf0, 0x01},
+ {0x79, 0xc1},
+ {0xf0, 0x37},
+ {0x24, 0x21},
+ {0xf0, 0x36},
+ {0x41, 0x00},
+ {0xea, 0x09},
+ {0xeb, 0x03},
+ {0xec, 0x19},
+ {0xed, 0x38},
+ {0xe9, 0x30},
+ {0xf0, 0x33},
+ {0x33, 0x00},
+ {0x34, 0x00},
+ {0xb1, 0x00},
+ {0xf0, 0x00},
+ {0xe0, 0x04},
+ {0xf0, 0x01},
+ {0x73, 0x00},
+ {0x74, 0xe0},
+ {0x70, 0x00},
+ {0x71, 0x80},
+ {0xf0, 0x36},
+ {0x32, 0x44},
+ {0xf0, 0x36},
+ {0x3e, 0xe0},
+ {0x70, 0x56},
+ {0x7c, 0x43},
+ {0x7d, 0x47},
+ {0x74, 0x00},
+ {0x75, 0x00},
+ {0x76, 0x00},
+ {0xa0, 0x47},
+ {0xa1, 0x5f},
+ {0x96, 0x22},
+ {0x97, 0x22},
+ {0x98, 0x22},
+ {0xf0, 0x00},
+ {0x72, 0x38},
+ {0x7a, 0x80},
+ {0x85, 0x18},
+ {0x9b, 0x35},
+ {0x9e, 0x20},
+ {0xd0, 0x66},
+ {0xd1, 0x34},
+ {0Xd3, 0x44},
+ {0xd6, 0x44},
+ {0xb0, 0x41},
+ {0xb2, 0x48},
+ {0xb3, 0xf4},
+ {0xb4, 0x0b},
+ {0xb5, 0x78},
+ {0xba, 0xff},
+ {0xbb, 0xc0},
+ {0xbc, 0x90},
+ {0xbd, 0x3a},
+ {0xc1, 0x67},
+ {0xf0, 0x01},
+ {0x20, 0x11},
+ {0x23, 0x90},
+ {0x24, 0x15},
+ {0x25, 0x87},
+ {0xbc, 0x9f},
+ {0xbd, 0x3a},
+ {0x48, 0xe6},
+ {0x49, 0xc0},
+ {0x4a, 0xd0},
+ {0x4b, 0x48},
+
+ // [cvbs_on]
+ {0xf0, 0x36},
+ {0x11, 0x00},
+ {0xf0, 0x01},
+ {0x79, 0xf1},
+
+ // [cvbs_off]
+ {0xf0, 0x36},
+ {0x11, 0x80},
+ {0xf0, 0x01},
+ {0x79, 0xc1},
+};
+
+/*
+[Sensor]
+SensorName=SC031IOT
+width=640
+height=480
+port=1
+type=2
+pin=3
+SlaveID=0xd0
+mode=0
+FlagReg=0xf7
+FlagMask=0xff
+FlagData=0xfa
+FlagReg1=0xf8
+FlagMask1=0xff
+FlagData1=0x46
+outformat=0
+mclk=27
+avdd=2.80000
+dovdd=2.800000
+dvdd=1.5
+
+Ext0=0
+Ext1=0
+Ext2=0
+AFVCC=0.0000
+VPP=0.000000
+*/
+/* 27M MCLK, 30fps
+static const uint8_t sc030iot_default_init_regs[][2] = {
+ {0xf0, 0x30},
+ {0x01, 0xff},
+ {0x02, 0xff},
+ {0x22, 0x07},
+ {0x19, 0xff},
+ {0x3f, 0x82},
+ {0x30, 0x02},
+ {0xf0, 0x01},
+ {0x70, 0x00},
+ {0x71, 0x80},
+ {0x72, 0x20},
+ {0x73, 0x00},
+ {0x74, 0xe0},
+ {0x75, 0x10},
+ {0x76, 0x81},
+ {0x77, 0x88},
+ {0x78, 0xe1},
+ {0x79, 0x01},
+ {0xf5, 0x01},
+ {0xf4, 0x0a},
+ {0xf0, 0x36},
+ {0x37, 0x79},
+ {0x31, 0x82},
+ {0x3e, 0x60},
+ {0x30, 0xf0},
+ {0x33, 0x33},
+ {0xf0, 0x32},
+ {0x48, 0x02},
+ {0xf0, 0x33},
+ {0x02, 0x12},
+ {0x7c, 0x02},
+ {0x7d, 0x0e},
+ {0xa2, 0x04},
+ {0x5e, 0x06},
+ {0x5f, 0x0a},
+ {0x0b, 0x58},
+ {0x06, 0x38},
+ {0xf0, 0x32},
+ {0x48, 0x02},
+ {0xf0, 0x39},
+ {0x02, 0x70},
+ {0xf0, 0x45},
+ {0x09, 0x1c},
+ {0xf0, 0x37},
+ {0x22, 0x0d},
+ {0xf0, 0x33},
+ {0x33, 0x10},
+ {0xb1, 0x80},
+ {0x34, 0x40},
+ {0x0b, 0x54},
+ {0xb2, 0x78},
+ {0xf0, 0x36},
+ {0x11, 0x80},
+ {0xf0, 0x30},
+ {0x38, 0x44},
+ {0xf0, 0x33},
+ {0xb3, 0x51},
+ {0x01, 0x10},
+ {0x0b, 0x6c},
+ {0x06, 0x24},
+ {0xf0, 0x36},
+ {0x31, 0x82},
+ {0x3e, 0x60},
+ {0x30, 0xf0},
+ {0x33, 0x33},
+ {0xf0, 0x34},
+ {0x9f, 0x02},
+ {0xa6, 0x40},
+ {0xa7, 0x47},
+ {0xe8, 0x5f},
+ {0xa8, 0x51},
+ {0xa9, 0x44},
+ {0xe9, 0x36},
+ {0xf0, 0x33},
+ {0xb3, 0x51},
+ {0x64, 0x17},
+ {0x90, 0x01},
+ {0x91, 0x03},
+ {0x92, 0x07},
+ {0x01, 0x10},
+ {0x93, 0x10},
+ {0x94, 0x10},
+ {0x95, 0x10},
+ {0x96, 0x01},
+ {0x97, 0x07},
+ {0x98, 0x1f},
+ {0x99, 0x10},
+ {0x9a, 0x20},
+ {0x9b, 0x28},
+ {0x9c, 0x28},
+ {0xf0, 0x36},
+ {0x70, 0x54},
+ {0xb6, 0x40},
+ {0xb7, 0x41},
+ {0xb8, 0x43},
+ {0xb9, 0x47},
+ {0xba, 0x4f},
+ {0xb0, 0x8b},
+ {0xb1, 0x8b},
+ {0xb2, 0x8b},
+ {0xb3, 0x9b},
+ {0xb4, 0xb8},
+ {0xb5, 0xf0},
+ {0x7e, 0x41},
+ {0x7f, 0x47},
+ {0x77, 0x80},
+ {0x78, 0x84},
+ {0x79, 0x8a},
+ {0xa0, 0x47},
+ {0xa1, 0x5f},
+ {0x96, 0x43},
+ {0x97, 0x44},
+ {0x98, 0x54},
+ {0xf0, 0x00},
+ {0xf0, 0x01},
+ {0x73, 0x00},
+ {0x74, 0xe0},
+ {0x70, 0x00},
+ {0x71, 0x80},
+ {0xf0, 0x36},
+ {0x37, 0x74},
+ {0xf0, 0x3f},
+ {0x03, 0x93},
+ {0xf0, 0x36},//cvbs_off
+ {0x11, 0x80},
+ {0xf0, 0x01},
+ {0x79, 0xc1},
+ {0xf0, 0x37},
+ {0x24, 0x21},
+ {0xf0, 0x36},
+ {0x41, 0x00},
+ {0xe9, 0x2c},
+ {0xf0, 0x33},
+ {0x33, 0x00},
+ {0x34, 0x00},
+ {0xb1, 0x00},
+ {0xf0, 0x00},
+ {0xe0, 0x04},
+ {0xf0, 0x01},
+ {0x73, 0x00},
+ {0x74, 0xe0},
+ {0x70, 0x00},
+ {0x71, 0x80},
+ {0xf0, 0x36},
+ {0x32, 0x44},
+ {0xf0, 0x36},
+ {0x3e, 0xe0},
+ {0x70, 0x56},
+ {0x7c, 0x43},
+ {0x7d, 0x47},
+ {0x74, 0x00},
+ {0x75, 0x00},
+ {0x76, 0x00},
+ {0xa0, 0x47},
+ {0xa1, 0x5f},
+ {0x96, 0x22},
+ {0x97, 0x22},
+ {0x98, 0x22},
+ {0xf0, 0x00},
+ {0x72, 0x38},
+ {0x7a, 0x80},
+ {0x85, 0x18},
+ {0x9b, 0x35},
+ {0x9e, 0x20},
+ {0xd0, 0x66},
+ {0xd1, 0x34},
+ {0Xd3, 0x44},
+ {0xd6, 0x44},
+ {0xb0, 0x41},
+ {0xb2, 0x48},
+ {0xb3, 0xf4},
+ {0xb4, 0x0b},
+ {0xb5, 0x78},
+ {0xba, 0xff},
+ {0xbb, 0xc0},
+ {0xbc, 0x90},
+ {0xbd, 0x3a},
+ {0xc1, 0x67},
+ {0xf0, 0x01},
+ {0x20, 0x11},
+ {0x23, 0x90},
+ {0x24, 0x15},
+ {0x25, 0x87},
+ {0xbc, 0x9f},
+ {0xbd, 0x3a},
+ {0x48, 0xe6},
+ {0x49, 0xc0},
+ {0x4a, 0xd0},
+ {0x4b, 0x48},
+
+ // [cvbs_on]
+ {0xf0, 0x36},
+ {0x11, 0x00},
+ {0xf0, 0x01},
+ {0x79, 0xf1},
+
+ // [cvbs_off]
+ {0xf0, 0x36},
+ {0x11, 0x80},
+ {0xf0, 0x01},
+ {0x79, 0xc1},
+};
+
+*/
\ No newline at end of file
diff --git a/code/components/esp32-camera-master/sensors/private_include/sc101iot.h b/code/components/esp32-camera-master/sensors/private_include/sc101iot.h
new file mode 100644
index 00000000..85858498
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/private_include/sc101iot.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * SC101IOT DVP driver.
+ *
+ */
+#ifndef __SC101IOT_H__
+#define __SC101IOT_H__
+
+#include "sensor.h"
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ * 0: Can't detect this sensor
+ * Nonzero: This sensor has been detected
+ */
+int sc101iot_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ * Always 0
+ */
+int sc101iot_init(sensor_t *sensor);
+
+#endif // __SC101IOT_H__
diff --git a/code/components/esp32-camera-master/sensors/private_include/sc101iot_settings.h b/code/components/esp32-camera-master/sensors/private_include/sc101iot_settings.h
new file mode 100644
index 00000000..2eb14398
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/private_include/sc101iot_settings.h
@@ -0,0 +1,257 @@
+//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
+//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
+//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
+//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
+//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
+//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
+//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
+//pin :BIT0 pwdn// BIT1:reset
+//avdd 0:2.8V// 1:2.5V// 2:1.8V
+//dovdd 0:2.8V// 1:2.5V// 2:1.8V
+//dvdd 0:1.8V// 1:1.5V// 2:1.2V
+/*
+[DataBase]
+DBName=DemoSens
+
+[Vendor]
+VendorName=SmartSens
+I2C_CRC=0
+
+[Sensor]
+SensorName=SC101AP_raw
+width=1280
+height=720
+port=1
+type=2
+pin=3
+SlaveID=0xd0
+mode=0
+FlagReg=0xf7
+FlagMask=0xff
+FlagData=0xda
+FlagReg1=0xf8
+FlagMask1=0xff
+FlagData1=0x4a
+outformat=0
+mclk=20
+avdd=2.800000
+dovdd=2.800000
+dvdd=1.200000
+
+Ext0=0
+Ext1=0
+Ext2=0
+AFVCC=0.00
+VPP=0.000000
+*/
+#include
+
+static const uint8_t sc101iot_default_init_regs[][2] = {
+#if CONFIG_SC101IOT_720P_15FPS_ENABLED // 720P+YUV422+15FPS sensor default regs
+/* Here are some test results:
+# size xclk fps pic pclk
+# ------- ------- ------ --------- ------- --- --- --- --- ---
+# 720p 4 3 err
+# 720p 8 5 normal 15
+# 720p 10 7.8 normal 19
+# 720p 20 15 warning 37.5
+# VGA 8 6 normal
+# VGA 20 16 normal
+
+*/
+ {0xf0, 0x30},
+ {0x01, 0xff},
+ {0x02, 0xe0},
+ {0x30, 0x10},
+ {0x3f, 0x81},
+ {0xf0, 0x00},
+ {0x70, 0x6b},
+ {0x72, 0x30},
+ {0x84, 0xb4},
+ {0x8b, 0x00},
+ {0x8c, 0x20},
+ {0x8d, 0x02},
+ {0x8e, 0xec},
+ {0x9e, 0x10},
+ {0xb0, 0xc1},
+ {0xc8, 0x10},
+ {0xc9, 0x10},
+ {0xc6, 0x00},
+ {0xe0, 0x0f},
+ {0xb5, 0xf0},
+ {0xde, 0x80},
+ {0xb5, 0xf0},
+ {0xde, 0x80},
+ {0xb2, 0x50},
+ {0xb3, 0xfc},
+ {0xb4, 0x40},
+ {0xb5, 0xc0},
+ {0xb6, 0x50},
+ {0xb7, 0xfc},
+ {0xb8, 0x40},
+ {0xb9, 0xc0},
+ {0xba, 0xff},
+ {0xbb, 0xcc},
+ {0xbc, 0xa9},
+ {0xbd, 0x7d},
+ {0xc1, 0x77},
+ {0xf0, 0x01},
+ {0x70, 0x02},
+ {0x71, 0x02},
+ {0x72, 0x50},
+ {0x73, 0x02},
+ {0x74, 0xd2},
+ {0x75, 0x20},
+ {0x76, 0x81},
+ {0x77, 0x8c},
+ {0x78, 0x81},
+ {0xf4, 0x01},
+ {0xf5, 0x00},
+ {0xf6, 0x00},
+ {0xf0, 0x36},
+ {0x40, 0x03},
+ {0x41, 0x01},
+ {0xf0, 0x39},
+ {0x02, 0x70},
+ {0xf0, 0x32},
+ {0x41, 0x00},
+ {0x43, 0x01},
+ {0x48, 0x02},
+ {0xf0, 0x45},
+ {0x09, 0x20},
+ {0xf0, 0x33},
+ {0x33, 0x10},
+ {0xf0, 0x30},
+ {0x38, 0x44},
+ {0xf0, 0x39},
+ {0x07, 0x00},
+ {0x08, 0x19},
+ {0x47, 0x00},
+ {0x48, 0x00},
+ {0xf0, 0x37},
+ {0x24, 0x31},
+ {0xf0, 0x34},
+ {0x9f, 0x02},
+ {0xa6, 0x51},
+ {0xa7, 0x57},
+ {0xe8, 0x5f},
+ {0xa8, 0x50},
+ {0xa9, 0x50},
+ {0xe9, 0x50},
+ {0xf0, 0x33},
+ {0xb3, 0x58},
+ {0xb2, 0x78},
+ {0xf0, 0x34},
+ {0x9f, 0x03},
+ {0xa6, 0x51},
+ {0xa7, 0x57},
+ {0xaa, 0x01},
+ {0xab, 0x28},
+ {0xac, 0x01},
+ {0xad, 0x38},
+ {0xf0, 0x33},
+ {0x0a, 0x01},
+ {0x0b, 0x28},
+ {0xf0, 0x33},
+ {0x64, 0x0f},
+ {0xec, 0x51},
+ {0xed, 0x57},
+ {0x06, 0x58},
+ {0xe9, 0x58},
+ {0xeb, 0x68},
+ {0xf0, 0x33},
+ {0x64, 0x0f},
+ {0xf0, 0x36},
+ {0x70, 0xdf},
+ {0xb6, 0x40},
+ {0xb7, 0x51},
+ {0xb8, 0x53},
+ {0xb9, 0x57},
+ {0xba, 0x5f},
+ {0xb0, 0x84},
+ {0xb1, 0x82},
+ {0xb2, 0x84},
+ {0xb3, 0x88},
+ {0xb4, 0x90},
+ {0xb5, 0x90},
+ {0xf0, 0x36},
+ {0x7e, 0x50},
+ {0x7f, 0x51},
+ {0x77, 0x81},
+ {0x78, 0x86},
+ {0x79, 0x89},
+ {0xf0, 0x36},
+ {0x70, 0xdf},
+ {0x9c, 0x51},
+ {0x9d, 0x57},
+ {0x90, 0x54},
+ {0x91, 0x54},
+ {0x92, 0x56},
+ {0xf0, 0x36},
+ {0xa0, 0x51},
+ {0xa1, 0x57},
+ {0x96, 0x33},
+ {0x97, 0x43},
+ {0x98, 0x43},
+ {0xf0, 0x36},
+ {0x70, 0xdf},
+ {0x7c, 0x40},
+ {0x7d, 0x53},
+ {0x74, 0xd0},
+ {0x75, 0xf0},
+ {0x76, 0xf0},
+ {0xf0, 0x37},
+ {0x0f, 0xd5},
+ {0x7a, 0x40},
+ {0x7b, 0x57},
+ {0x71, 0x09},
+ {0x72, 0x09},
+ {0x73, 0x05},
+ {0xf0, 0x33},
+ {0x01, 0x44},
+ {0xf0, 0x36},
+ {0x37, 0xfb},
+ {0xf0, 0x36},
+ {0x3c, 0x0d},
+ {0xf0, 0x33},
+ {0x14, 0x95},
+ {0xf0, 0x33},
+ {0x8f, 0x80},
+ {0xf0, 0x37},
+ {0x27, 0x14},
+ {0x28, 0x03},
+ {0xf0, 0x36},
+ {0x37, 0xf4},
+ {0xf0, 0x33},
+ {0x01, 0x44},
+ {0xf0, 0x36},
+ {0x79, 0x89},
+ {0xf0, 0x34},
+ {0xac, 0x01},
+ {0xad, 0x40},
+ {0xf0, 0x33},
+ {0xeb, 0x70},
+ {0xf0, 0x34},
+ {0xa8, 0x50},
+ {0xa9, 0x50},
+ {0xf0, 0x33},
+ {0xb3, 0x58},
+ {0xf0, 0x36},
+ {0x11, 0x80},
+ {0xf0, 0x36},
+ {0x41, 0x51},
+ {0xf0, 0x3f},
+ {0x03, 0x09},
+ {0xf0, 0x32},
+ {0x0c, 0x06},
+ {0x0d, 0x82},
+ {0x0e, 0x02},
+ {0x0f, 0xee},
+ {0xf0, 0x36},
+ {0xea, 0x09},
+ {0xeb, 0xf5},
+ {0xec, 0x11},
+ {0xed, 0x27},
+ {0xe9, 0x20},
+#endif
+};
diff --git a/code/components/esp32-camera-master/sensors/sc030iot.c b/code/components/esp32-camera-master/sensors/sc030iot.c
new file mode 100644
index 00000000..86f525f3
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/sc030iot.c
@@ -0,0 +1,335 @@
+/*
+ * SC030IOT driver.
+ *
+ * Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include
+#include
+#include
+#include
+#include "sccb.h"
+#include "xclk.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#include "sc030iot.h"
+#include "sc030iot_settings.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char* TAG = "sc030";
+#endif
+
+#define SC030_SENSOR_ID_HIGH_REG 0XF7
+#define SC030_SENSOR_ID_LOW_REG 0XF8
+#define SC030_MAX_FRAME_WIDTH (640)
+#define SC030_MAX_FRAME_HIGH (480)
+
+// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
+// For more information please refer to the Technical Reference Manual.
+static int get_reg(sensor_t *sensor, int reg, int reg_value_mask)
+{
+ int ret = 0;
+ uint8_t reg_high = (reg>>8) & 0xFF;
+ uint8_t reg_low = reg & 0xFF;
+
+ if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
+ return -1;
+ }
+
+ ret = SCCB_Read(sensor->slv_addr, reg_low);
+ if(ret > 0){
+ ret &= reg_value_mask;
+ }
+ return ret;
+}
+
+// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
+// For more information please refer to the Technical Reference Manual.
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+ int ret = 0;
+ uint8_t reg_high = (reg>>8) & 0xFF;
+ uint8_t reg_low = reg & 0xFF;
+
+ if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
+ return -1;
+ }
+
+ ret = SCCB_Write(sensor->slv_addr, reg_low, value & 0xFF);
+ return ret;
+}
+
+static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
+{
+ int i=0, res = 0;
+ while (islv_addr, regs[i][0], regs[i][1]);
+ if (res) {
+ return res;
+ }
+ i++;
+ }
+ return res;
+}
+
+static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
+{
+ int ret = 0;
+ ret = get_reg(sensor, reg, 0xff);
+ if(ret < 0){
+ return ret;
+ }
+ uint8_t mask = ((1 << length) - 1) << offset;
+ value = (ret & ~mask) | ((value << offset) & mask);
+ ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
+ return ret;
+}
+
+#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
+#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
+#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+ int ret = 0;
+ if(enable) {
+ SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // mirror on
+ } else {
+ SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // mirror off
+ }
+
+ return ret;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+ int ret = 0;
+ if(enable) {
+ SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
+ } else {
+ SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
+ }
+
+ return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable test pattern mode
+
+ return ret;
+}
+
+static int set_sharpness(sensor_t *sensor, int level)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
+ WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
+ WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
+
+ return ret;
+}
+
+static int set_agc_gain(sensor_t *sensor, int gain)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
+ WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
+ WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
+ WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
+ WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
+
+ return ret;
+}
+
+static int set_aec_value(sensor_t *sensor, int value)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
+ WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
+
+ return ret;
+}
+
+static int set_awb_gain(sensor_t *sensor, int value)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
+ WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
+ WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
+ return ret;
+}
+
+static int set_saturation(sensor_t *sensor, int level)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
+ WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
+ WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
+ return ret;
+}
+
+static int set_contrast(sensor_t *sensor, int level)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
+ WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
+ return ret;
+}
+
+static int reset(sensor_t *sensor)
+{
+ int ret = set_regs(sensor, sc030iot_default_init_regs, sizeof(sc030iot_default_init_regs)/(sizeof(uint8_t) * 2));
+
+ // Delay
+ vTaskDelay(50 / portTICK_PERIOD_MS);
+
+ // ESP_LOGI(TAG, "set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
+ // ESP_LOGI(TAG, "0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
+ if (ret) {
+ ESP_LOGE(TAG, "reset fail");
+ }
+ return ret;
+}
+
+static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
+{
+ int ret = 0;
+ //sc:H_start={0x0172[1:0],0x0170},H_end={0x0172[5:4],0x0171},
+ WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
+ WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
+ WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x03) | (((offset_x+w)>>4)&0x30));
+
+ //sc:V_start={0x0175[1:0],0x0173},H_end={0x0175[5:4],0x0174},
+ WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
+ WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
+ WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x03) | (((offset_y+h)>>4)&0x30));
+
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+
+ return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+ uint16_t w = resolution[framesize].width;
+ uint16_t h = resolution[framesize].height;
+ if(w>SC030_MAX_FRAME_WIDTH || h > SC030_MAX_FRAME_HIGH) {
+ goto err;
+ }
+
+ uint16_t offset_x = (640-w) /2;
+ uint16_t offset_y = (480-h) /2;
+
+ if(set_window(sensor, offset_x, offset_y, w, h)) {
+ goto err;
+ }
+
+ sensor->status.framesize = framesize;
+ return 0;
+err:
+ ESP_LOGE(TAG, "frame size err");
+ return -1;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+ int ret=0;
+ sensor->pixformat = pixformat;
+
+ switch (pixformat) {
+ case PIXFORMAT_RGB565:
+ case PIXFORMAT_RAW:
+ case PIXFORMAT_GRAYSCALE:
+ ESP_LOGE(TAG, "Not support");
+ break;
+ case PIXFORMAT_YUV422: // For now, sc030/sc031 sensor only support YUV422.
+ break;
+ default:
+ return -1;
+ }
+
+ return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+ return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val){ return -1; }
+
+static int set_xclk(sensor_t *sensor, int timer, int xclk)
+{
+ int ret = 0;
+ sensor->xclk_freq_hz = xclk * 1000000U;
+ ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
+ return ret;
+}
+
+int sc030iot_detect(int slv_addr, sensor_id_t *id)
+{
+ if (SC030IOT_SCCB_ADDR == slv_addr) {
+ uint8_t MIDL = SCCB_Read(slv_addr, SC030_SENSOR_ID_LOW_REG);
+ uint8_t MIDH = SCCB_Read(slv_addr, SC030_SENSOR_ID_HIGH_REG);
+ uint16_t PID = MIDH << 8 | MIDL;
+ if (SC030IOT_PID == PID) {
+ id->PID = PID;
+ return PID;
+ } else {
+ ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+ }
+ }
+ return 0;
+}
+
+int sc030iot_init(sensor_t *sensor)
+{
+ // Set function pointers
+ sensor->reset = reset;
+ sensor->init_status = init_status;
+ sensor->set_pixformat = set_pixformat;
+ sensor->set_framesize = set_framesize;
+
+ sensor->set_saturation= set_saturation;
+ sensor->set_colorbar = set_colorbar;
+ sensor->set_hmirror = set_hmirror;
+ sensor->set_vflip = set_vflip;
+ sensor->set_sharpness = set_sharpness;
+ sensor->set_agc_gain = set_agc_gain;
+ sensor->set_aec_value = set_aec_value;
+ sensor->set_awb_gain = set_awb_gain;
+ sensor->set_contrast = set_contrast;
+ //not supported
+ sensor->set_denoise = set_dummy;
+ sensor->set_quality = set_dummy;
+ sensor->set_special_effect = set_dummy;
+ sensor->set_wb_mode = set_dummy;
+ sensor->set_ae_level = set_dummy;
+
+
+ sensor->get_reg = get_reg;
+ sensor->set_reg = set_reg;
+ sensor->set_xclk = set_xclk;
+
+ ESP_LOGD(TAG, "sc030iot Attached");
+
+ return 0;
+}
\ No newline at end of file
diff --git a/code/components/esp32-camera-master/sensors/sc101iot.c b/code/components/esp32-camera-master/sensors/sc101iot.c
new file mode 100644
index 00000000..310a0476
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/sc101iot.c
@@ -0,0 +1,342 @@
+/*
+ * SC101IOT driver.
+ *
+ * Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include
+#include
+#include
+#include
+#include "sccb.h"
+#include "xclk.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#include "sc101iot.h"
+#include "sc101iot_settings.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char* TAG = "sc101";
+#endif
+
+#define SC101_SENSOR_ID_HIGH_REG 0XF7
+#define SC101_SENSOR_ID_LOW_REG 0XF8
+#define SC101_MAX_FRAME_WIDTH (1280)
+#define SC101_MAX_FRAME_HIGH (720)
+
+// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
+// For more information please refer to the Technical Reference Manual.
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+ int ret = 0;
+ uint8_t reg_high = (reg>>8) & 0xFF;
+ uint8_t reg_low = reg & 0xFF;
+
+ if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
+ return -1;
+ }
+
+ ret = SCCB_Read(sensor->slv_addr, reg_low);
+ if(ret > 0){
+ ret &= mask;
+ }
+ return ret;
+}
+
+// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
+// For more information please refer to the Technical Reference Manual.
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+ int ret = 0;
+ uint8_t reg_high = (reg>>8) & 0xFF;
+ uint8_t reg_low = reg & 0xFF;
+
+ if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
+ return -1;
+ }
+
+ ret = SCCB_Write(sensor->slv_addr, reg_low, value & 0xFF);
+ return ret;
+}
+
+static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
+{
+ int i=0, res = 0;
+ while (islv_addr, regs[i][0], regs[i][1]);
+ if (res) {
+ return res;
+ }
+ i++;
+ }
+ return res;
+}
+
+static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
+{
+ int ret = 0;
+ ret = get_reg(sensor, reg, 0xff);
+ if(ret < 0){
+ return ret;
+ }
+ uint8_t mask = ((1 << length) - 1) << offset;
+ value = (ret & ~mask) | ((value << offset) & mask);
+ ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
+ return ret;
+}
+
+#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
+#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
+#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+ int ret = 0;
+ if(enable) {
+ SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // enable mirror
+ } else {
+ SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // disable mirror
+ }
+
+ return ret;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+ int ret = 0;
+ if(enable) {
+ SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
+ } else {
+ SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
+ }
+
+ return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable colorbar mode
+ return ret;
+}
+
+static int set_raw_gma(sensor_t *sensor, int enable)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x00f5, 1, 1, enable & 0xff); // enable gamma compensation
+
+ return ret;
+}
+
+static int set_sharpness(sensor_t *sensor, int level)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
+ WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
+ WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
+
+ return ret;
+}
+
+static int set_agc_gain(sensor_t *sensor, int gain)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
+ WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
+ WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
+ WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
+ WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
+
+ return ret;
+}
+
+static int set_aec_value(sensor_t *sensor, int value)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
+ WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
+
+ return ret;
+}
+
+static int set_awb_gain(sensor_t *sensor, int value)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
+ WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
+ WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
+ return ret;
+}
+
+static int set_saturation(sensor_t *sensor, int level)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
+ WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
+ WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
+ return ret;
+}
+
+static int set_contrast(sensor_t *sensor, int level)
+{
+ int ret = 0;
+ SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
+ WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
+ return ret;
+}
+
+static int reset(sensor_t *sensor)
+{
+ int ret = set_regs(sensor, sc101iot_default_init_regs, sizeof(sc101iot_default_init_regs)/(sizeof(uint8_t) * 2));
+
+ // Delay
+ vTaskDelay(50 / portTICK_PERIOD_MS);
+
+ // ESP_LOGI(TAG, "set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
+ // ESP_LOGI(TAG, "0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
+ if (ret) {
+ ESP_LOGE(TAG, "reset fail");
+ }
+ return ret;
+}
+
+static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
+{
+ int ret = 0;
+ //sc:H_start={0x0172[3:0],0x0170},H_end={0x0172[7:4],0x0171},
+ WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
+ WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
+ WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x0f) | (((offset_x+w)>>4)&0xf0));
+
+ //sc:V_start={0x0175[3:0],0x0173},H_end={0x0175[7:4],0x0174},
+ WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
+ WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
+ WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x0f) | (((offset_y+h)>>4)&0xf0));
+
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+
+ return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+ uint16_t w = resolution[framesize].width;
+ uint16_t h = resolution[framesize].height;
+ if(w>SC101_MAX_FRAME_WIDTH || h > SC101_MAX_FRAME_HIGH) {
+ goto err;
+ }
+
+ uint16_t offset_x = (SC101_MAX_FRAME_WIDTH-w) /2;
+ uint16_t offset_y = (SC101_MAX_FRAME_HIGH-h) /2;
+
+ if(set_window(sensor, offset_x, offset_y, w, h)) {
+ goto err;
+ }
+
+ sensor->status.framesize = framesize;
+ return 0;
+err:
+ ESP_LOGE(TAG, "frame size err");
+ return -1;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+ int ret=0;
+ sensor->pixformat = pixformat;
+
+ switch (pixformat) {
+ case PIXFORMAT_RGB565:
+ case PIXFORMAT_RAW:
+ case PIXFORMAT_GRAYSCALE:
+ ESP_LOGE(TAG, "Not support");
+ break;
+ case PIXFORMAT_YUV422: // For now, sc101 sensor only support YUV422.
+ break;
+ default:
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+ return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val){ return -1; }
+
+static int set_xclk(sensor_t *sensor, int timer, int xclk)
+{
+ int ret = 0;
+ sensor->xclk_freq_hz = xclk * 1000000U;
+ ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
+ return ret;
+}
+
+int sc101iot_detect(int slv_addr, sensor_id_t *id)
+{
+ if (SC101IOT_SCCB_ADDR == slv_addr) {
+ uint8_t MIDL = SCCB_Read(slv_addr, SC101_SENSOR_ID_LOW_REG);
+ uint8_t MIDH = SCCB_Read(slv_addr, SC101_SENSOR_ID_HIGH_REG);
+ uint16_t PID = MIDH << 8 | MIDL;
+ if (SC101IOT_PID == PID) {
+ id->PID = PID;
+ return PID;
+ } else {
+ ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+ }
+ }
+ return 0;
+}
+
+int sc101iot_init(sensor_t *sensor)
+{
+ // Set function pointers
+ sensor->reset = reset;
+ sensor->init_status = init_status;
+ sensor->set_pixformat = set_pixformat;
+ sensor->set_framesize = set_framesize;
+ sensor->set_hmirror = set_hmirror;
+ sensor->set_vflip = set_vflip;
+ sensor->set_colorbar = set_colorbar;
+ sensor->set_raw_gma = set_raw_gma;
+ sensor->set_sharpness = set_sharpness;
+ sensor->set_agc_gain = set_agc_gain;
+ sensor->set_aec_value = set_aec_value;
+ sensor->set_awb_gain = set_awb_gain;
+ sensor->set_saturation= set_saturation;
+ sensor->set_contrast = set_contrast;
+
+ sensor->set_denoise = set_dummy;
+ sensor->set_quality = set_dummy;
+ sensor->set_special_effect = set_dummy;
+ sensor->set_wb_mode = set_dummy;
+ sensor->set_ae_level = set_dummy;
+
+
+ sensor->get_reg = get_reg;
+ sensor->set_reg = set_reg;
+ sensor->set_xclk = set_xclk;
+
+ ESP_LOGD(TAG, "sc101iot Attached");
+
+ return 0;
+}
\ No newline at end of file
diff --git a/code/components/esp32-camera-master/target/esp32/ll_cam.c b/code/components/esp32-camera-master/target/esp32/ll_cam.c
index d0f0c862..1e3def87 100644
--- a/code/components/esp32-camera-master/target/esp32/ll_cam.c
+++ b/code/components/esp32-camera-master/target/esp32/ll_cam.c
@@ -34,10 +34,14 @@ static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
#include "xclk.h"
#include "cam_hal.h"
+#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR >= 3)
+#include "esp_rom_gpio.h"
+#endif
+
#if (ESP_IDF_VERSION_MAJOR >= 5)
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
-#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
+#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
#endif
static const char *TAG = "esp32 ll_cam";
@@ -233,7 +237,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
//DBG_PIN_SET(0);
}
-bool ll_cam_stop(cam_obj_t *cam)
+bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
{
I2S0.conf.rx_start = 0;
I2S_ISR_DISABLE(in_suc_eof);
diff --git a/code/components/esp32-camera-master/target/esp32s2/ll_cam.c b/code/components/esp32-camera-master/target/esp32s2/ll_cam.c
index e54b81f0..54764329 100644
--- a/code/components/esp32-camera-master/target/esp32s2/ll_cam.c
+++ b/code/components/esp32-camera-master/target/esp32s2/ll_cam.c
@@ -21,10 +21,15 @@
#include "xclk.h"
#include "cam_hal.h"
+#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR >= 3)
+#include "esp_rom_gpio.h"
+#endif
+
#if (ESP_IDF_VERSION_MAJOR >= 5)
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
-#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
+#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
+#define ets_delay_us(a) esp_rom_delay_us(a)
#endif
static const char *TAG = "s2 ll_cam";
@@ -70,7 +75,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
}
}
-bool ll_cam_stop(cam_obj_t *cam)
+bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
{
I2S0.conf.rx_start = 0;
diff --git a/code/components/esp32-camera-master/target/esp32s3/ll_cam.c b/code/components/esp32-camera-master/target/esp32s3/ll_cam.c
index ce405d16..2211a0ed 100644
--- a/code/components/esp32-camera-master/target/esp32s3/ll_cam.c
+++ b/code/components/esp32-camera-master/target/esp32s3/ll_cam.c
@@ -22,10 +22,15 @@
#include "soc/gdma_reg.h"
#include "ll_cam.h"
#include "cam_hal.h"
+#include "esp_rom_gpio.h"
#if (ESP_IDF_VERSION_MAJOR >= 5)
-#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
-#define gpio_matrix_out(a,b,c,d) gpio_iomux_out(a,b,c)
+#include "soc/gpio_sig_map.h"
+#include "soc/gpio_periph.h"
+#include "soc/io_mux_reg.h"
+#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
+#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
+#define ets_delay_us(a) esp_rom_delay_us(a)
#endif
static const char *TAG = "s3 ll_cam";
@@ -74,7 +79,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
}
}
-bool ll_cam_stop(cam_obj_t *cam)
+bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
{
if (cam->jpeg_mode || !cam->psram_mode) {
GDMA.channel[cam->dma_num].in.int_ena.in_suc_eof = 0;
@@ -170,6 +175,7 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
}
GDMA.channel[cam->dma_num].in.conf1.in_check_owner = 0;
+ // GDMA.channel[cam->dma_num].in.conf1.in_ext_mem_bk_size = 2;
GDMA.channel[cam->dma_num].in.peri_sel.sel = 5;
//GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15
@@ -178,8 +184,52 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
return ESP_OK;
}
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+static esp_err_t ll_cam_converter_config(cam_obj_t *cam, const camera_config_t *config)
+{
+ esp_err_t ret = ESP_OK;
+
+ switch (config->conv_mode) {
+ case YUV422_TO_YUV420:
+ if (config->pixel_format != PIXFORMAT_YUV422) {
+ ret = ESP_FAIL;
+ } else {
+ ESP_LOGI(TAG, "YUV422 to YUV420 mode");
+ LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 1;
+ LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
+ LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 1;
+ }
+ break;
+ case YUV422_TO_RGB565:
+ if (config->pixel_format != PIXFORMAT_YUV422) {
+ ret = ESP_FAIL;
+ } else {
+ ESP_LOGI(TAG, "YUV422 to RGB565 mode");
+ LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3;
+ LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
+ LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 0;
+ }
+ break;
+ default:
+ break;
+ }
+#if CONFIG_LCD_CAM_CONV_BT709_ENABLED
+ LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 1;
+#else
+ LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 0;
+#endif
+ LCD_CAM.cam_rgb_yuv.cam_conv_data_out_mode = 0;
+ LCD_CAM.cam_rgb_yuv.cam_conv_data_in_mode = 0;
+ LCD_CAM.cam_rgb_yuv.cam_conv_mode_8bits_on = 1;
+ LCD_CAM.cam_rgb_yuv.cam_conv_bypass = 1;
+ cam->conv_mode = config->conv_mode;
+ return ret;
+}
+#endif
+
esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
{
+ esp_err_t ret = ESP_OK;
if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0) {
REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
@@ -215,15 +265,21 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
LCD_CAM.cam_rgb_yuv.val = 0;
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+ if (config->conv_mode) {
+ ret = ll_cam_converter_config(cam, config);
+ if(ret != ESP_OK) {
+ return ret;
+ }
+ }
+#endif
+
LCD_CAM.cam_ctrl.cam_update = 1;
LCD_CAM.cam_ctrl1.cam_start = 1;
- esp_err_t err = ll_cam_dma_init(cam);
- if(err != ESP_OK) {
- return err;
- }
+ ret = ll_cam_dma_init(cam);
- return ESP_OK;
+ return ret;
}
void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
@@ -417,6 +473,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in,
}
return len / 2;
}
+
// just memcpy
memcpy(out, in, len);
@@ -433,8 +490,22 @@ esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_
}
cam->fb_bytes_per_pixel = 1; // frame buffer stores Y8
} else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
- cam->in_bytes_per_pixel = 2; // camera sends YU/YV
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+ switch (cam->conv_mode) {
+ case YUV422_TO_YUV420:
+ cam->in_bytes_per_pixel = 1.5; // for DMA receive
+ cam->fb_bytes_per_pixel = 1.5; // frame buffer stores YUV420
+ break;
+ case YUV422_TO_RGB565:
+ default:
+ cam->in_bytes_per_pixel = 2; // for DMA receive
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
+ break;
+ }
+#else
+ cam->in_bytes_per_pixel = 2; // for DMA receive
+ cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
+#endif
} else if (pix_format == PIXFORMAT_JPEG) {
cam->in_bytes_per_pixel = 1;
cam->fb_bytes_per_pixel = 1;
diff --git a/code/components/esp32-camera-master/target/private_include/ll_cam.h b/code/components/esp32-camera-master/target/private_include/ll_cam.h
index 7d30c370..c27db0c4 100644
--- a/code/components/esp32-camera-master/target/private_include/ll_cam.h
+++ b/code/components/esp32-camera-master/target/private_include/ll_cam.h
@@ -116,8 +116,14 @@ typedef struct {
//for RGB/YUV modes
uint16_t width;
uint16_t height;
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+ float in_bytes_per_pixel;
+ float fb_bytes_per_pixel;
+ camera_conv_mode_t conv_mode;
+#else
uint8_t in_bytes_per_pixel;
uint8_t fb_bytes_per_pixel;
+#endif
uint32_t fb_size;
cam_state_t state;
diff --git a/code/main/version.cpp b/code/main/version.cpp
index e996364f..a6add0f6 100644
--- a/code/main/version.cpp
+++ b/code/main/version.cpp
@@ -1,4 +1,4 @@
-const char* GIT_REV="60cdee2";
+const char* GIT_REV="058e943";
const char* GIT_TAG="";
const char* GIT_BRANCH="rolling";
-const char* BUILD_TIME="2022-06-26 21:24";
\ No newline at end of file
+const char* BUILD_TIME="2022-07-16 07:55";
\ No newline at end of file
diff --git a/code/platformio.ini b/code/platformio.ini
index 11935158..abf5731f 100644
--- a/code/platformio.ini
+++ b/code/platformio.ini
@@ -14,9 +14,8 @@ src_dir = main
[env:esp32cam]
-;platform = espressif32@2.1.0
-;platform = espressif32@3.5.0
-platform = espressif32
+platform = espressif32@4.4.0
+;platform = espressif32
board = esp32cam
framework = espidf
diff --git a/code/sdkconfig.esp32cam b/code/sdkconfig.esp32cam
index 5437b6a8..28ab25b6 100644
--- a/code/sdkconfig.esp32cam
+++ b/code/sdkconfig.esp32cam
@@ -1233,6 +1233,9 @@ CONFIG_OV2640_SUPPORT=y
# CONFIG_GC032A_SUPPORT is not set
# CONFIG_GC0308_SUPPORT is not set
# CONFIG_BF3005_SUPPORT is not set
+CONFIG_BF20A6_SUPPORT=y
+# CONFIG_SC101IOT_SUPPORT is not set
+CONFIG_SC030IOT_SUPPORT=y
# CONFIG_SCCB_HARDWARE_I2C_PORT0 is not set
CONFIG_SCCB_HARDWARE_I2C_PORT1=y
CONFIG_SCCB_CLK_FREQ=100000
diff --git a/code/version.cpp b/code/version.cpp
index f4238673..0b90033d 100644
--- a/code/version.cpp
+++ b/code/version.cpp
@@ -1,4 +1,4 @@
-const char* GIT_REV="60cdee2";
+const char* GIT_REV="058e943";
const char* GIT_TAG="";
const char* GIT_BRANCH="rolling";
-const char* BUILD_TIME="2022-06-26 21:23";
\ No newline at end of file
+const char* BUILD_TIME="2022-07-16 07:54";
\ No newline at end of file
diff --git a/firmware/bootloader.bin b/firmware/bootloader.bin
index 326b0f43d0c4f43474b3d602c1267dbeee060f38..d13bbe16bf3fb2114c8a8c07791a34d7aaa8c444 100644
GIT binary patch
delta 1273
zcmbW$YiyHM9LMqV)UNAFFY8u2)~#FX=D-&&Z8rxj%6Zs`F+pI7FBFP|gi8W38i^({
zG#iOAU{D9gOGIX-GBHX9*0ip;lF?D&!Pm)e728)vL^A3+d8Nwh
z4VWW`N<=TmPN;!@^dR*j9-L5iqlX>fF+7bucoBW*$NLz<#~$g?;{?^K)b~_RDU)Iw
z8oi=FrCx#5D|Ihzi$%Un9m6$rl!^XkT-mC3Rv#u`K+4BfF^9eI^USlOC3d<(^!7^8
zE2y90a4N4=%Dj*W2D<1j$9Ht+%{z(8N2kLqs=w3@(c?-e6qVZzBuSE9}|CD&|S8WjD@9%Tplqo>Hb@EnFQ
z1051wcr$Kxv7gL$wmzp?OccgyY~l@k*rn6~az8Fz5}Bj6v575s*GwkQc|tMKRWb9$
z9m_Mb#3}rU5e%Up`>`8GRw(-fuXL_(t~OoOcFZ(4WPHiE=o?rTH#-_)B_9w^I{O+n
zw}pFodCy}n49=K;*RAUDbMeBcK|LC=7=;VTY1b*Vky|;|UDQIm6{pA-a218)ruYDI
zPNZ=Bytnf;5noo`p}JNxsWC%z3j
bKa8F}Gv0mBz4`L8Yr}WC2B(_bxv%~MB4Pp4
delta 1249
zcmbW$|7+BB9Ki9{=eoOnyKU|~-QDfJ&2x^R)$O(hjqv@MNk5o5mqOx=GBpe$X2mLH
zvdwTwjB>uCW-S;pup-3z6b+1atpyT=1x6{2K`qRVYT_Cu$KE|ZgZ_cw9=u-f*S$aQ
z&-?TKvhbaCf?v!U8;PIMJ;Ah|3RiqkTwCIVN=H5a6`c9FQl;)beVh4iS%F5JMQoYyZQWPJQju7UQR#sc_aR%{I5Uw6tei^goen
z&V2NcM<0lv(#!EgS)Ox{uv-r%UTyZ)8dZ%tG+;lj
zfUfEky+pEdk!W~W#8y7tk*vG@UXM{P;}S%kQGUix+-6h{&La`#Y22xgB_HlTM<#Mz
z?PAQ%m6_!8hSD(k$V&UhlrdTPi+r;zsBSEh`+P>V;t9z3-|8*m9JbDg9rg1C*o!Ce
zJO(fXjWJB%eZT2flO!@AGEFR^wZy1ygiDP&OS}e?6v@!GnB-C7$0(q!%%}^~V#~my
zIzfIJ>&uOL3csNb0iNm0`|OgSQFpWDwZvl>%Pc6#=qstJ{ut$-D7}GCG3x&HZFN3H
z+;vjyFKU?L+a>m|L{EyPu3-PL6R+cUGzQooyo=^lM%7drl}6E2*hN!j-$i~ua_Ge?
z=%U|5rCF^c<;$C-PmoWkW%YGjAFki_cvTg`gXxXgccLr=hR}=s
z*nv%G#zcqM=UDG$!|V1esilZMn$DJgALFmMQGdkrXX&W#A<~>Po8H$}vz67|j-7ZE
zr*+rv2l{^`o5fWWuz*Flx6(b|8F)g}xQ#KQ+it}$`58>YJ&VGAm^owIv-uUZjcbew
z(eERkdy%{}v#8Dz$7x?C9wc&Kf+DM!zgTkc=Ud84v0XRi&ig;%IxL0tpSkEgN!BEX
z$1sYUeH^rDOExpQd+1WmAeU}a^=KV$V_`l=kbIx+=x(eml69~qbdb|dZ}+x9{UdG9
kMI$p!$B%62yl_|-<~_g6PTm?{cYVuCq57K*>sxO83voUGX8-^I
diff --git a/firmware/firmware.bin b/firmware/firmware.bin
index 32c9de1fa1c91358f1339e0f776d379140a7740f..149d64f47548d71df7ef0c20ad284ac7f37db11f 100644
GIT binary patch
delta 422931
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≫H%8pWh$x3)$xt=XxqQ8X+IQvY~{0}c%J32lWw=0
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