diff --git a/CMakeLists.txt b/CMakeLists.txt
index 105e419..7c07b0b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,6 +39,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
sensors/gc032a.c
sensors/bf3005.c
sensors/bf20a6.c
+ sensors/sc101iot.c
sensors/sc030iot.c
)
diff --git a/Kconfig b/Kconfig
index 6e2b668..7103358 100755
--- a/Kconfig
+++ b/Kconfig
@@ -77,6 +77,31 @@ menu "Camera configuration"
Enable this option if you want to use the BF20A6.
Disable this option to save memory.
+ config SC101IOT_SUPPORT
+ bool "Support SC101IOT HD"
+ default n
+ help
+ Enable this option if you want to use the SC101IOT.
+ Disable this option to save memory.
+
+ choice SC101_REGS_SELECT
+ prompt "SC101iot default regs"
+ default SC101IOT_720P_15FPS_ENABLED
+ depends on SC101IOT_SUPPORT
+ help
+ Currently SC010iot has several register sets available.
+ Select the one that matches your needs.
+
+ config SC101IOT_720P_15FPS_ENABLED
+ bool "xclk20M_720p_15fps"
+ help
+ Select this option means that when xclk is 20M, the frame rate is 15fps at 720p resolution.
+ config SC101IOT_VGA_25FPS_ENABLED
+ bool "xclk20M_VGA_25fps"
+ help
+ Select this option means that when xclk is 20M, the frame rate is 25fps at VGA resolution.
+ endchoice
+
config SC030IOT_SUPPORT
bool "Support SC030IOT VGA"
default y
diff --git a/README.md b/README.md
index 4deef25..9b5282e 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi
| 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/driver/esp_camera.c b/driver/esp_camera.c
index 738d083..5d3630b 100644
--- a/driver/esp_camera.c
+++ b/driver/esp_camera.c
@@ -60,6 +60,9 @@
#if CONFIG_BF20A6_SUPPORT
#include "bf20a6.h"
#endif
+#if CONFIG_SC101IOT_SUPPORT
+#include "sc101iot.h"
+#endif
#if CONFIG_SC030IOT_SUPPORT
#include "sc030iot.h"
#endif
@@ -128,6 +131,9 @@ static const sensor_func_t g_sensors[] = {
#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
diff --git a/driver/include/sensor.h b/driver/include/sensor.h
index e4a1cce..c1b882a 100755
--- a/driver/include/sensor.h
+++ b/driver/include/sensor.h
@@ -28,6 +28,7 @@ typedef enum {
GC0308_PID = 0x9b,
BF3005_PID = 0x30,
BF20A6_PID = 0x20a6,
+ SC101IOT_PID = 0xda4a,
SC030IOT_PID = 0x9a46,
} camera_pid_t;
@@ -43,6 +44,7 @@ typedef enum {
CAMERA_GC0308,
CAMERA_BF3005,
CAMERA_BF20A6,
+ CAMERA_SC101IOT,
CAMERA_SC030IOT,
CAMERA_MODEL_MAX,
CAMERA_NONE,
@@ -60,6 +62,7 @@ typedef enum {
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;
diff --git a/driver/sensor.c b/driver/sensor.c
index 73cf08e..2f4c971 100644
--- a/driver/sensor.c
+++ b/driver/sensor.c
@@ -14,6 +14,7 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
{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},
};
diff --git a/sensors/private_include/sc101iot.h b/sensors/private_include/sc101iot.h
new file mode 100644
index 0000000..8585849
--- /dev/null
+++ b/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/sensors/private_include/sc101iot_settings.h b/sensors/private_include/sc101iot_settings.h
new file mode 100644
index 0000000..2eb1439
--- /dev/null
+++ b/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/sensors/sc030iot.c b/sensors/sc030iot.c
index ef570ed..86f525f 100644
--- a/sensors/sc030iot.c
+++ b/sensors/sc030iot.c
@@ -42,7 +42,7 @@ static const char* TAG = "sc030";
// 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.
-int get_reg(sensor_t *sensor, int reg, int reg_value_mask)
+static int get_reg(sensor_t *sensor, int reg, int reg_value_mask)
{
int ret = 0;
uint8_t reg_high = (reg>>8) & 0xFF;
@@ -61,7 +61,7 @@ int get_reg(sensor_t *sensor, int reg, int reg_value_mask)
// 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.
-int set_reg(sensor_t *sensor, int reg, int mask, int value)
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
uint8_t reg_high = (reg>>8) & 0xFF;
diff --git a/sensors/sc101iot.c b/sensors/sc101iot.c
new file mode 100644
index 0000000..310a047
--- /dev/null
+++ b/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