porkling 2 years ago
parent
commit
36e63e50f8
70 changed files with 17785 additions and 936 deletions
  1. 137 0
      std/camera/camera.c
  2. 20 0
      std/camera/camera.h
  3. 132 0
      std/camera/conversions/esp_jpg_decode.c
  4. 43 0
      std/camera/conversions/include/esp_jpg_decode.h
  5. 130 0
      std/camera/conversions/include/img_converters.h
  6. 723 0
      std/camera/conversions/jpge.cpp
  7. 142 0
      std/camera/conversions/private_include/jpge.h
  8. 29 0
      std/camera/conversions/private_include/yuv.h
  9. 393 0
      std/camera/conversions/to_bmp.c
  10. 245 0
      std/camera/conversions/to_jpg.cpp
  11. 298 0
      std/camera/conversions/yuv.c
  12. 495 0
      std/camera/driver/cam_hal.c
  13. 418 0
      std/camera/driver/esp_camera.c
  14. 219 0
      std/camera/driver/include/esp_camera.h
  15. 251 0
      std/camera/driver/include/sensor.h
  16. 60 0
      std/camera/driver/private_include/cam_hal.h
  17. 19 0
      std/camera/driver/private_include/sccb.h
  18. 9 0
      std/camera/driver/private_include/xclk.h
  19. 191 0
      std/camera/driver/sccb.c
  20. 54 0
      std/camera/driver/sensor.c
  21. 404 0
      std/camera/sensors/bf20a6.c
  22. 541 0
      std/camera/sensors/bf3005.c
  23. 467 0
      std/camera/sensors/gc0308.c
  24. 391 0
      std/camera/sensors/gc032a.c
  25. 477 0
      std/camera/sensors/gc2145.c
  26. 1022 0
      std/camera/sensors/nt99141.c
  27. 612 0
      std/camera/sensors/ov2640.c
  28. 1053 0
      std/camera/sensors/ov3660.c
  29. 1130 0
      std/camera/sensors/ov5640.c
  30. 457 0
      std/camera/sensors/ov7670.c
  31. 575 0
      std/camera/sensors/ov7725.c
  32. 27 0
      std/camera/sensors/private_include/bf20a6.h
  33. 12 0
      std/camera/sensors/private_include/bf20a6_regs.h
  34. 158 0
      std/camera/sensors/private_include/bf20a6_settings.h
  35. 33 0
      std/camera/sensors/private_include/bf3005.h
  36. 337 0
      std/camera/sensors/private_include/bf3005_regs.h
  37. 31 0
      std/camera/sensors/private_include/gc0308.h
  38. 25 0
      std/camera/sensors/private_include/gc0308_regs.h
  39. 245 0
      std/camera/sensors/private_include/gc0308_settings.h
  40. 31 0
      std/camera/sensors/private_include/gc032a.h
  41. 82 0
      std/camera/sensors/private_include/gc032a_regs.h
  42. 401 0
      std/camera/sensors/private_include/gc032a_settings.h
  43. 27 0
      std/camera/sensors/private_include/gc2145.h
  44. 85 0
      std/camera/sensors/private_include/gc2145_regs.h
  45. 719 0
      std/camera/sensors/private_include/gc2145_settings.h
  46. 34 0
      std/camera/sensors/private_include/nt99141.h
  47. 211 0
      std/camera/sensors/private_include/nt99141_regs.h
  48. 825 0
      std/camera/sensors/private_include/nt99141_settings.h
  49. 32 0
      std/camera/sensors/private_include/ov2640.h
  50. 216 0
      std/camera/sensors/private_include/ov2640_regs.h
  51. 485 0
      std/camera/sensors/private_include/ov2640_settings.h
  52. 34 0
      std/camera/sensors/private_include/ov3660.h
  53. 211 0
      std/camera/sensors/private_include/ov3660_regs.h
  54. 318 0
      std/camera/sensors/private_include/ov3660_settings.h
  55. 27 0
      std/camera/sensors/private_include/ov5640.h
  56. 213 0
      std/camera/sensors/private_include/ov5640_regs.h
  57. 334 0
      std/camera/sensors/private_include/ov5640_settings.h
  58. 33 0
      std/camera/sensors/private_include/ov7670.h
  59. 354 0
      std/camera/sensors/private_include/ov7670_regs.h
  60. 33 0
      std/camera/sensors/private_include/ov7725.h
  61. 335 0
      std/camera/sensors/private_include/ov7725_regs.h
  62. 528 0
      std/camera/target/ll_cam.c
  63. 141 0
      std/camera/target/private_include/ll_cam.h
  64. 64 0
      std/camera/target/xclk.c
  65. 0 227
      std/inc/bc26_work.h~RF21af2a2.TMP
  66. 0 224
      std/inc/bc26_work.h~RFb31113.TMP
  67. 0 235
      std/inc/bc95.h~RF188fa9f5.TMP
  68. 0 241
      std/inc/bc95.h~RF18eae9e4.TMP
  69. 5 7
      std/inc/nrf_usr_uart.h
  70. 2 2
      std/src/nrf_usr_uart.c

+ 137 - 0
std/camera/camera.c

@@ -0,0 +1,137 @@
+#include "camera.h"
+
+static void ov_camera_pw_gpio_init(void);
+static void ov_camera_pw_on(void);
+static void ov_camera_pw_off(void);
+
+static const char *TAG = "camera";
+camera_fb_t *pic;
+
+static camera_config_t camera_config = {
+	.pin_pwdn = -1,
+	.pin_reset = CONFIG_CAM_RST_GPIO,
+	.pin_xclk = CONFIG_CAM_XCLK_GPIO,
+	.pin_sscb_sda = CONFIG_CAM_SDA_GPIO,
+	.pin_sscb_scl = CONFIG_CAM_SCL_GPIO,
+
+	.pin_d7 = CONFIG_CAM_D7_GPIO,
+	.pin_d6 = CONFIG_CAM_D6_GPIO,
+	.pin_d5 = CONFIG_CAM_D5_GPIO,
+	.pin_d4 = CONFIG_CAM_D4_GPIO,
+	.pin_d3 = CONFIG_CAM_D3_GPIO,
+	.pin_d2 = CONFIG_CAM_D2_GPIO,
+	.pin_d1 = CONFIG_CAM_D1_GPIO,
+	.pin_d0 = CONFIG_CAM_D0_GPIO,
+	.pin_vsync = CONFIG_CAM_VSYNC_GPIO,
+	.pin_href = CONFIG_CAM_HREF_GPIO,
+	.pin_pclk = CONFIG_CAM_PCLK_GPIO,
+
+	//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
+	.xclk_freq_hz = CONFIG_CAM_XCLK_FREQ,
+#if CONFIG_CAM_XCLK_GPIO > 0
+    // .ledc_timer = LEDC_TIMER(CONFIG_CAM_XCLK_LEDC_TIMER),
+    // .ledc_channel = LEDC_CHANNEL(CONFIG_CAM_XCLK_LEDC_CHANNEL),
+#endif
+
+	.pixel_format = PIXFORMAT_JPEG,    //YUV422,GRAYSCALE,RGB565,JPEG
+	.frame_size = FRAMESIZE_SVGA,      //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
+
+	.jpeg_quality = 12, //0-63 lower number means higher quality
+	.fb_count = 1,       //if more than one, i2s runs in continuous mode. Use only with JPEG
+	.grab_mode = CAMERA_GRAB_WHEN_EMPTY,
+};
+
+static void camera_task(void)
+{
+	while (1)
+	{
+		ESP_LOGI(TAG, "Taking picture...");
+		camera_fb_t *pic = esp_camera_fb_get();
+
+		// use pic->buf to access the image
+		ESP_LOGI(TAG, "Picture taken! Its size was: %zu bytes", pic->len);
+		esp_camera_fb_return(pic);
+
+		vTaskDelay(5000 / portTICK_RATE_MS);
+	}
+}
+
+esp_err_t camera_run(void)
+{
+    ESP_LOGD(TAG, "Taking picture...");
+    pic = esp_camera_fb_get();
+
+    if(pic == NULL)
+    {
+        return ESP_FAIL;
+    }
+    // use pic->buf to access the image
+    ESP_LOGD(TAG, "Picture taken! Its size was: %zu bytes", pic->len);
+    return ESP_OK;
+}
+
+void camera_stop(void)
+{
+    esp_camera_fb_return(pic);
+}
+
+uint8_t * camera_get_fb(void)
+{
+    return pic->buf;
+}
+
+size_t camera_get_data_size(void)
+{
+    return pic->len;
+}
+
+esp_err_t init_camera(void)
+{
+    ov_camera_pw_gpio_init();
+    ov_camera_pw_on();
+    //initialize the camera
+    esp_err_t err = esp_camera_init(&camera_config);
+    if (err != ESP_OK)
+    {
+        ESP_LOGE(TAG, "Camera Init Failed");
+        return err;
+    }
+
+    return ESP_OK;
+}
+
+static void ov_camera_pw_gpio_init(void)
+{
+#ifdef CONFIG_OV2640_SUPPORT
+    uint64_t ov2640_output_gpio = ((1ULL<<CONFIG_CAM_PW_GPIO));
+	//zero-initialize the config structure.
+    gpio_config_t io_conf;
+    //disable interrupt
+    io_conf.intr_type = GPIO_INTR_DISABLE;
+    //set as output mode
+    io_conf.mode = GPIO_MODE_OUTPUT;
+    //bit mask of the pins that you want to set
+    io_conf.pin_bit_mask = ov2640_output_gpio;
+    //disable pull-down mode
+    io_conf.pull_down_en = 0;
+    //disable pull-up mode
+    io_conf.pull_up_en = 0;
+    //configure GPIO with the given settings
+    gpio_config(&io_conf);
+#endif
+}
+
+static void ov_camera_pw_on(void)
+{
+#ifdef CONFIG_OV2640_SUPPORT
+    gpio_set_level(CONFIG_CAM_PW_GPIO, CONFIG_CAM_PW_ON_LEVEL?1:0);
+#endif
+    vTaskDelay(100 / portTICK_PERIOD_MS);
+}
+
+static void ov_camera_pw_off(void)
+{
+#ifdef CONFIG_OV2640_SUPPORT
+    gpio_set_level(CONFIG_CAM_PW_GPIO, CONFIG_CAM_PW_ON_LEVEL?0:1);
+#endif
+}

+ 20 - 0
std/camera/camera.h

@@ -0,0 +1,20 @@
+#ifndef __CAMERA_H__
+#define __CAMERA_H__
+
+#include <esp_log.h>
+#include <esp_system.h>
+#include <nvs_flash.h>
+#include <sys/param.h>
+#include <string.h>
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_camera.h"
+
+esp_err_t init_camera(void);
+esp_err_t camera_run(void);
+void camera_stop(void);
+uint8_t * camera_get_fb(void);
+size_t camera_get_data_size(void);
+
+#endif//__CAMERA_H__

+ 132 - 0
std/camera/conversions/esp_jpg_decode.c

@@ -0,0 +1,132 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "esp_jpg_decode.h"
+
+#include "esp_system.h"
+#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
+#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
+#include "esp32/rom/tjpgd.h"
+#elif CONFIG_IDF_TARGET_ESP32S2
+#include "tjpgd.h"
+#elif CONFIG_IDF_TARGET_ESP32S3
+#include "esp32s3/rom/tjpgd.h"
+#else
+#error Target CONFIG_IDF_TARGET is not supported
+#endif
+#else // ESP32 Before IDF 4.0
+#include "rom/tjpgd.h"
+#endif
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#define TAG ""
+#else
+#include "esp_log.h"
+static const char* TAG = "esp_jpg_decode";
+#endif
+
+typedef struct {
+        jpg_scale_t scale;
+        jpg_reader_cb reader;
+        jpg_writer_cb writer;
+        void * arg;
+        size_t len;
+        size_t index;
+} esp_jpg_decoder_t;
+
+static const char * jd_errors[] = {
+    "Succeeded",
+    "Interrupted by output function",
+    "Device error or wrong termination of input stream",
+    "Insufficient memory pool for the image",
+    "Insufficient stream input buffer",
+    "Parameter error",
+    "Data format error",
+    "Right format but not supported",
+    "Not supported JPEG standard"
+};
+
+static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
+{
+    uint16_t x = rect->left;
+    uint16_t y = rect->top;
+    uint16_t w = rect->right + 1 - x;
+    uint16_t h = rect->bottom + 1 - y;
+    uint8_t *data = (uint8_t *)bitmap;
+
+    esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device;
+
+    if (jpeg->writer) {
+        return jpeg->writer(jpeg->arg, x, y, w, h, data);
+    }
+    return 0;
+}
+
+static uint32_t _jpg_read(JDEC *decoder, uint8_t *buf, uint32_t len)
+{
+    esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device;
+    if (jpeg->len && len > (jpeg->len - jpeg->index)) {
+        len = jpeg->len - jpeg->index;
+    }
+    if (len) {
+        len = jpeg->reader(jpeg->arg, jpeg->index, buf, len);
+        if (!len) {
+            ESP_LOGE(TAG, "Read Fail at %u/%u", jpeg->index, jpeg->len);
+        }
+        jpeg->index += len;
+    }
+    return len;
+}
+
+esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg)
+{
+    static uint8_t work[3100];
+    JDEC decoder;
+    esp_jpg_decoder_t jpeg;
+
+    jpeg.len = len;
+    jpeg.reader = reader;
+    jpeg.writer = writer;
+    jpeg.arg = arg;
+    jpeg.scale = scale;
+    jpeg.index = 0;
+
+    JRESULT jres = jd_prepare(&decoder, _jpg_read, work, 3100, &jpeg);
+    if(jres != JDR_OK){
+        ESP_LOGE(TAG, "JPG Header Parse Failed! %s", jd_errors[jres]);
+        return ESP_FAIL;
+    }
+
+    uint16_t output_width = decoder.width / (1 << (uint8_t)(jpeg.scale));
+    uint16_t output_height = decoder.height / (1 << (uint8_t)(jpeg.scale));
+
+    //output start
+    writer(arg, 0, 0, output_width, output_height, NULL);
+    //output write
+    jres = jd_decomp(&decoder, _jpg_write, (uint8_t)jpeg.scale);
+    //output end
+    writer(arg, output_width, output_height, output_width, output_height, NULL);
+
+    if (jres != JDR_OK) {
+        ESP_LOGE(TAG, "JPG Decompression Failed! %s", jd_errors[jres]);
+        return ESP_FAIL;
+    }
+    //check if all data has been consumed.
+    if (len && jpeg.index < len) {
+        _jpg_read(&decoder, NULL, len - jpeg.index);
+    }
+
+    return ESP_OK;
+}
+

+ 43 - 0
std/camera/conversions/include/esp_jpg_decode.h

@@ -0,0 +1,43 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef _ESP_JPG_DECODE_H_
+#define _ESP_JPG_DECODE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp_err.h"
+
+typedef enum {
+    JPG_SCALE_NONE,
+    JPG_SCALE_2X,
+    JPG_SCALE_4X,
+    JPG_SCALE_8X,
+    JPG_SCALE_MAX = JPG_SCALE_8X
+} jpg_scale_t;
+
+typedef size_t (* jpg_reader_cb)(void * arg, size_t index, uint8_t *buf, size_t len);
+typedef bool (* jpg_writer_cb)(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data);
+
+esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ESP_JPG_DECODE_H_ */

+ 130 - 0
std/camera/conversions/include/img_converters.h

@@ -0,0 +1,130 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef _IMG_CONVERTERS_H_
+#define _IMG_CONVERTERS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp_camera.h"
+#include "esp_jpg_decode.h"
+
+typedef size_t (* jpg_out_cb)(void * arg, size_t index, const void* data, size_t len);
+
+/**
+ * @brief Convert image buffer to JPEG
+ *
+ * @param src       Source buffer in RGB565, RGB888, YUYV or GRAYSCALE format
+ * @param src_len   Length in bytes of the source buffer
+ * @param width     Width in pixels of the source image
+ * @param height    Height in pixels of the source image
+ * @param format    Format of the source image
+ * @param quality   JPEG quality of the resulting image
+ * @param cp        Callback to be called to write the bytes of the output JPEG
+ * @param arg       Pointer to be passed to the callback
+ *
+ * @return true on success
+ */
+bool fmt2jpg_cb(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, jpg_out_cb cb, void * arg);
+
+/**
+ * @brief Convert camera frame buffer to JPEG
+ *
+ * @param fb        Source camera frame buffer
+ * @param quality   JPEG quality of the resulting image
+ * @param cp        Callback to be called to write the bytes of the output JPEG
+ * @param arg       Pointer to be passed to the callback
+ *
+ * @return true on success
+ */
+bool frame2jpg_cb(camera_fb_t * fb, uint8_t quality, jpg_out_cb cb, void * arg);
+
+/**
+ * @brief Convert image buffer to JPEG buffer
+ *
+ * @param src       Source buffer in RGB565, RGB888, YUYV or GRAYSCALE format
+ * @param src_len   Length in bytes of the source buffer
+ * @param width     Width in pixels of the source image
+ * @param height    Height in pixels of the source image
+ * @param format    Format of the source image
+ * @param quality   JPEG quality of the resulting image
+ * @param out       Pointer to be populated with the address of the resulting buffer.
+ *                  You MUST free the pointer once you are done with it.
+ * @param out_len   Pointer to be populated with the length of the output buffer
+ *
+ * @return true on success
+ */
+bool fmt2jpg(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, uint8_t ** out, size_t * out_len);
+
+/**
+ * @brief Convert camera frame buffer to JPEG buffer
+ *
+ * @param fb        Source camera frame buffer
+ * @param quality   JPEG quality of the resulting image
+ * @param out       Pointer to be populated with the address of the resulting buffer
+ * @param out_len   Pointer to be populated with the length of the output buffer
+ *
+ * @return true on success
+ */
+bool frame2jpg(camera_fb_t * fb, uint8_t quality, uint8_t ** out, size_t * out_len);
+
+/**
+ * @brief Convert image buffer to BMP buffer
+ *
+ * @param src       Source buffer in JPEG, RGB565, RGB888, YUYV or GRAYSCALE format
+ * @param src_len   Length in bytes of the source buffer
+ * @param width     Width in pixels of the source image
+ * @param height    Height in pixels of the source image
+ * @param format    Format of the source image
+ * @param out       Pointer to be populated with the address of the resulting buffer
+ * @param out_len   Pointer to be populated with the length of the output buffer
+ *
+ * @return true on success
+ */
+bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t ** out, size_t * out_len);
+
+/**
+ * @brief Convert camera frame buffer to BMP buffer
+ *
+ * @param fb        Source camera frame buffer
+ * @param out       Pointer to be populated with the address of the resulting buffer
+ * @param out_len   Pointer to be populated with the length of the output buffer
+ *
+ * @return true on success
+ */
+bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len);
+
+/**
+ * @brief Convert image buffer to RGB888 buffer (used for face detection)
+ *
+ * @param src       Source buffer in JPEG, RGB565, RGB888, YUYV or GRAYSCALE format
+ * @param src_len   Length in bytes of the source buffer
+ * @param format    Format of the source image
+ * @param rgb_buf   Pointer to the output buffer (width * height * 3)
+ *
+ * @return true on success
+ */
+bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf);
+
+bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IMG_CONVERTERS_H_ */

+ 723 - 0
std/camera/conversions/jpge.cpp

@@ -0,0 +1,723 @@
+// jpge.cpp - C++ class for JPEG compression.
+// Public domain, Rich Geldreich <richgel99@gmail.com>
+// v1.01, Dec. 18, 2010 - Initial release
+// v1.02, Apr. 6, 2011 - Removed 2x2 ordered dither in H2V1 chroma subsampling method load_block_16_8_8(). (The rounding factor was 2, when it should have been 1. Either way, it wasn't helping.)
+// v1.03, Apr. 16, 2011 - Added support for optimized Huffman code tables, optimized dynamic memory allocation down to only 1 alloc.
+//                        Also from Alex Evans: Added RGBA support, linear memory allocator (no longer needed in v1.03).
+// v1.04, May. 19, 2012: Forgot to set m_pFile ptr to NULL in cfile_stream::close(). Thanks to Owen Kaluza for reporting this bug.
+//                       Code tweaks to fix VS2008 static code analysis warnings (all looked harmless).
+//                       Code review revealed method load_block_16_8_8() (used for the non-default H2V1 sampling mode to downsample chroma) somehow didn't get the rounding factor fix from v1.02.
+
+#include "jpge.h"
+
+#include <stdint.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include "esp_heap_caps.h"
+
+#define JPGE_MAX(a,b) (((a)>(b))?(a):(b))
+#define JPGE_MIN(a,b) (((a)<(b))?(a):(b))
+
+namespace jpge {
+
+    static inline void *jpge_malloc(size_t nSize) {
+        void * b = malloc(nSize);
+        if(b){
+            return b;
+        }
+        return heap_caps_malloc(nSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+    }
+    static inline void jpge_free(void *p) { free(p); }
+
+    // Various JPEG enums and tables.
+    enum { M_SOF0 = 0xC0, M_DHT = 0xC4, M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_APP0 = 0xE0 };
+    enum { DC_LUM_CODES = 12, AC_LUM_CODES = 256, DC_CHROMA_CODES = 12, AC_CHROMA_CODES = 256, MAX_HUFF_SYMBOLS = 257, MAX_HUFF_CODESIZE = 32 };
+
+    static const uint8 s_zag[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
+    static const int16 s_std_lum_quant[64] = { 16,11,12,14,12,10,16,14,13,14,18,17,16,19,24,40,26,24,22,22,24,49,35,37,29,40,58,51,61,60,57,51,56,55,64,72,92,78,64,68,87,69,55,56,80,109,81,87,95,98,103,104,103,62,77,113,121,112,100,120,92,101,103,99 };
+    static const int16 s_std_croma_quant[64] = { 17,18,18,24,21,24,47,26,26,47,99,66,56,66,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 };
+    static const uint8 s_dc_lum_bits[17] = { 0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0 };
+    static const uint8 s_dc_lum_val[DC_LUM_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
+    static const uint8 s_ac_lum_bits[17] = { 0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d };
+    static const uint8 s_ac_lum_val[AC_LUM_CODES]  = {
+        0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
+        0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
+        0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
+        0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
+        0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
+        0xf9,0xfa
+    };
+    static const uint8 s_dc_chroma_bits[17] = { 0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 };
+    static const uint8 s_dc_chroma_val[DC_CHROMA_CODES]  = { 0,1,2,3,4,5,6,7,8,9,10,11 };
+    static const uint8 s_ac_chroma_bits[17] = { 0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 };
+    static const uint8 s_ac_chroma_val[AC_CHROMA_CODES] = {
+        0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
+        0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
+        0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
+        0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
+        0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
+        0xf9,0xfa
+    };
+
+    const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329;
+
+    static int32 m_last_quality = 0;
+    static int32 m_quantization_tables[2][64];
+
+    static bool m_huff_initialized = false;
+    static uint m_huff_codes[4][256];
+    static uint8 m_huff_code_sizes[4][256];
+    static uint8 m_huff_bits[4][17];
+    static uint8 m_huff_val[4][256];
+
+    static inline uint8 clamp(int i) {
+        if (i < 0) {
+            i = 0;
+        } else if (i > 255){
+            i = 255;
+        }
+        return static_cast<uint8>(i);
+    }
+
+    static void RGB_to_YCC(uint8* pDst, const uint8 *pSrc, int num_pixels) {
+        for ( ; num_pixels; pDst += 3, pSrc += 3, num_pixels--) {
+            const int r = pSrc[0], g = pSrc[1], b = pSrc[2];
+            pDst[0] = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
+            pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16));
+            pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16));
+        }
+    }
+
+    static void RGB_to_Y(uint8* pDst, const uint8 *pSrc, int num_pixels) {
+        for ( ; num_pixels; pDst++, pSrc += 3, num_pixels--) {
+            pDst[0] = static_cast<uint8>((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16);
+        }
+    }
+
+    static void Y_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels) {
+        for( ; num_pixels; pDst += 3, pSrc++, num_pixels--) {
+            pDst[0] = pSrc[0];
+            pDst[1] = 128;
+            pDst[2] = 128;
+        }
+    }
+
+    // Forward DCT - DCT derived from jfdctint.
+    enum { CONST_BITS = 13, ROW_BITS = 2 };
+#define DCT_DESCALE(x, n) (((x) + (((int32)1) << ((n) - 1))) >> (n))
+#define DCT_MUL(var, c) (static_cast<int16>(var) * static_cast<int32>(c))
+#define DCT1D(s0, s1, s2, s3, s4, s5, s6, s7) \
+    int32 t0 = s0 + s7, t7 = s0 - s7, t1 = s1 + s6, t6 = s1 - s6, t2 = s2 + s5, t5 = s2 - s5, t3 = s3 + s4, t4 = s3 - s4; \
+    int32 t10 = t0 + t3, t13 = t0 - t3, t11 = t1 + t2, t12 = t1 - t2; \
+    int32 u1 = DCT_MUL(t12 + t13, 4433); \
+    s2 = u1 + DCT_MUL(t13, 6270); \
+    s6 = u1 + DCT_MUL(t12, -15137); \
+    u1 = t4 + t7; \
+    int32 u2 = t5 + t6, u3 = t4 + t6, u4 = t5 + t7; \
+    int32 z5 = DCT_MUL(u3 + u4, 9633); \
+    t4 = DCT_MUL(t4, 2446); t5 = DCT_MUL(t5, 16819); \
+    t6 = DCT_MUL(t6, 25172); t7 = DCT_MUL(t7, 12299); \
+    u1 = DCT_MUL(u1, -7373); u2 = DCT_MUL(u2, -20995); \
+    u3 = DCT_MUL(u3, -16069); u4 = DCT_MUL(u4, -3196); \
+    u3 += z5; u4 += z5; \
+    s0 = t10 + t11; s1 = t7 + u1 + u4; s3 = t6 + u2 + u3; s4 = t10 - t11; s5 = t5 + u2 + u4; s7 = t4 + u1 + u3;
+
+    static void DCT2D(int32 *p) {
+        int32 c, *q = p;
+        for (c = 7; c >= 0; c--, q += 8) {
+            int32 s0 = q[0], s1 = q[1], s2 = q[2], s3 = q[3], s4 = q[4], s5 = q[5], s6 = q[6], s7 = q[7];
+            DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
+            q[0] = s0 << ROW_BITS; q[1] = DCT_DESCALE(s1, CONST_BITS-ROW_BITS); q[2] = DCT_DESCALE(s2, CONST_BITS-ROW_BITS); q[3] = DCT_DESCALE(s3, CONST_BITS-ROW_BITS);
+            q[4] = s4 << ROW_BITS; q[5] = DCT_DESCALE(s5, CONST_BITS-ROW_BITS); q[6] = DCT_DESCALE(s6, CONST_BITS-ROW_BITS); q[7] = DCT_DESCALE(s7, CONST_BITS-ROW_BITS);
+        }
+        for (q = p, c = 7; c >= 0; c--, q++) {
+            int32 s0 = q[0*8], s1 = q[1*8], s2 = q[2*8], s3 = q[3*8], s4 = q[4*8], s5 = q[5*8], s6 = q[6*8], s7 = q[7*8];
+            DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
+            q[0*8] = DCT_DESCALE(s0, ROW_BITS+3); q[1*8] = DCT_DESCALE(s1, CONST_BITS+ROW_BITS+3); q[2*8] = DCT_DESCALE(s2, CONST_BITS+ROW_BITS+3); q[3*8] = DCT_DESCALE(s3, CONST_BITS+ROW_BITS+3);
+            q[4*8] = DCT_DESCALE(s4, ROW_BITS+3); q[5*8] = DCT_DESCALE(s5, CONST_BITS+ROW_BITS+3); q[6*8] = DCT_DESCALE(s6, CONST_BITS+ROW_BITS+3); q[7*8] = DCT_DESCALE(s7, CONST_BITS+ROW_BITS+3);
+        }
+    }
+
+    // Compute the actual canonical Huffman codes/code sizes given the JPEG huff bits and val arrays.
+    static void compute_huffman_table(uint *codes, uint8 *code_sizes, uint8 *bits, uint8 *val)
+    {
+        int i, l, last_p, si;
+        static uint8 huff_size[257];
+        static uint huff_code[257];
+        uint code;
+
+        int p = 0;
+        for (l = 1; l <= 16; l++) {
+            for (i = 1; i <= bits[l]; i++) {
+                huff_size[p++] = (char)l;
+            }
+        }
+
+        huff_size[p] = 0;
+        last_p = p; // write sentinel
+
+        code = 0; si = huff_size[0]; p = 0;
+
+        while (huff_size[p]) {
+            while (huff_size[p] == si) {
+                huff_code[p++] = code++;
+            }
+            code <<= 1;
+            si++;
+        }
+
+        memset(codes, 0, sizeof(codes[0])*256);
+        memset(code_sizes, 0, sizeof(code_sizes[0])*256);
+        for (p = 0; p < last_p; p++) {
+            codes[val[p]]      = huff_code[p];
+            code_sizes[val[p]] = huff_size[p];
+        }
+    }
+
+    void jpeg_encoder::flush_output_buffer()
+    {
+        if (m_out_buf_left != JPGE_OUT_BUF_SIZE) {
+            m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_buf(m_out_buf, JPGE_OUT_BUF_SIZE - m_out_buf_left);
+        }
+        m_pOut_buf = m_out_buf;
+        m_out_buf_left = JPGE_OUT_BUF_SIZE;
+    }
+
+    void jpeg_encoder::emit_byte(uint8 i)
+    {
+        *m_pOut_buf++ = i;
+        if (--m_out_buf_left == 0) {
+            flush_output_buffer();
+        }
+    }
+
+    void jpeg_encoder::put_bits(uint bits, uint len)
+    {
+        uint8 c = 0;
+        m_bit_buffer |= ((uint32)bits << (24 - (m_bits_in += len)));
+        while (m_bits_in >= 8) {
+            c = (uint8)((m_bit_buffer >> 16) & 0xFF);
+            emit_byte(c);
+            if (c == 0xFF) {
+                emit_byte(0);
+            }
+            m_bit_buffer <<= 8;
+            m_bits_in -= 8;
+        }
+    }
+
+    void jpeg_encoder::emit_word(uint i)
+    {
+        emit_byte(uint8(i >> 8)); emit_byte(uint8(i & 0xFF));
+    }
+
+    // JPEG marker generation.
+    void jpeg_encoder::emit_marker(int marker)
+    {
+        emit_byte(uint8(0xFF)); emit_byte(uint8(marker));
+    }
+
+    // Emit JFIF marker
+    void jpeg_encoder::emit_jfif_app0()
+    {
+        emit_marker(M_APP0);
+        emit_word(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1);
+        emit_byte(0x4A); emit_byte(0x46); emit_byte(0x49); emit_byte(0x46); /* Identifier: ASCII "JFIF" */
+        emit_byte(0);
+        emit_byte(1);      /* Major version */
+        emit_byte(1);      /* Minor version */
+        emit_byte(0);      /* Density unit */
+        emit_word(1);
+        emit_word(1);
+        emit_byte(0);      /* No thumbnail image */
+        emit_byte(0);
+    }
+
+    // Emit quantization tables
+    void jpeg_encoder::emit_dqt()
+    {
+        for (int i = 0; i < ((m_num_components == 3) ? 2 : 1); i++)
+        {
+            emit_marker(M_DQT);
+            emit_word(64 + 1 + 2);
+            emit_byte(static_cast<uint8>(i));
+            for (int j = 0; j < 64; j++)
+                emit_byte(static_cast<uint8>(m_quantization_tables[i][j]));
+        }
+    }
+
+    // Emit start of frame marker
+    void jpeg_encoder::emit_sof()
+    {
+        emit_marker(M_SOF0);                           /* baseline */
+        emit_word(3 * m_num_components + 2 + 5 + 1);
+        emit_byte(8);                                  /* precision */
+        emit_word(m_image_y);
+        emit_word(m_image_x);
+        emit_byte(m_num_components);
+        for (int i = 0; i < m_num_components; i++)
+        {
+            emit_byte(static_cast<uint8>(i + 1));                                   /* component ID     */
+            emit_byte((m_comp_h_samp[i] << 4) + m_comp_v_samp[i]);  /* h and v sampling */
+            emit_byte(i > 0);                                   /* quant. table num */
+        }
+    }
+
+    // Emit Huffman table.
+    void jpeg_encoder::emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag)
+    {
+        emit_marker(M_DHT);
+
+        int length = 0;
+        for (int i = 1; i <= 16; i++)
+            length += bits[i];
+
+        emit_word(length + 2 + 1 + 16);
+        emit_byte(static_cast<uint8>(index + (ac_flag << 4)));
+
+        for (int i = 1; i <= 16; i++)
+            emit_byte(bits[i]);
+
+        for (int i = 0; i < length; i++)
+            emit_byte(val[i]);
+    }
+
+    // Emit all Huffman tables.
+    void jpeg_encoder::emit_dhts()
+    {
+        emit_dht(m_huff_bits[0+0], m_huff_val[0+0], 0, false);
+        emit_dht(m_huff_bits[2+0], m_huff_val[2+0], 0, true);
+        if (m_num_components == 3) {
+            emit_dht(m_huff_bits[0+1], m_huff_val[0+1], 1, false);
+            emit_dht(m_huff_bits[2+1], m_huff_val[2+1], 1, true);
+        }
+    }
+
+    // emit start of scan
+    void jpeg_encoder::emit_sos()
+    {
+        emit_marker(M_SOS);
+        emit_word(2 * m_num_components + 2 + 1 + 3);
+        emit_byte(m_num_components);
+        for (int i = 0; i < m_num_components; i++)
+        {
+            emit_byte(static_cast<uint8>(i + 1));
+            if (i == 0)
+                emit_byte((0 << 4) + 0);
+            else
+                emit_byte((1 << 4) + 1);
+        }
+        emit_byte(0);     /* spectral selection */
+        emit_byte(63);
+        emit_byte(0);
+    }
+
+    void jpeg_encoder::load_block_8_8_grey(int x)
+    {
+        uint8 *pSrc;
+        sample_array_t *pDst = m_sample_array;
+        x <<= 3;
+        for (int i = 0; i < 8; i++, pDst += 8)
+        {
+            pSrc = m_mcu_lines[i] + x;
+            pDst[0] = pSrc[0] - 128; pDst[1] = pSrc[1] - 128; pDst[2] = pSrc[2] - 128; pDst[3] = pSrc[3] - 128;
+            pDst[4] = pSrc[4] - 128; pDst[5] = pSrc[5] - 128; pDst[6] = pSrc[6] - 128; pDst[7] = pSrc[7] - 128;
+        }
+    }
+
+    void jpeg_encoder::load_block_8_8(int x, int y, int c)
+    {
+        uint8 *pSrc;
+        sample_array_t *pDst = m_sample_array;
+        x = (x * (8 * 3)) + c;
+        y <<= 3;
+        for (int i = 0; i < 8; i++, pDst += 8)
+        {
+            pSrc = m_mcu_lines[y + i] + x;
+            pDst[0] = pSrc[0 * 3] - 128; pDst[1] = pSrc[1 * 3] - 128; pDst[2] = pSrc[2 * 3] - 128; pDst[3] = pSrc[3 * 3] - 128;
+            pDst[4] = pSrc[4 * 3] - 128; pDst[5] = pSrc[5 * 3] - 128; pDst[6] = pSrc[6 * 3] - 128; pDst[7] = pSrc[7 * 3] - 128;
+        }
+    }
+
+    void jpeg_encoder::load_block_16_8(int x, int c)
+    {
+        uint8 *pSrc1, *pSrc2;
+        sample_array_t *pDst = m_sample_array;
+        x = (x * (16 * 3)) + c;
+        int a = 0, b = 2;
+        for (int i = 0; i < 16; i += 2, pDst += 8)
+        {
+            pSrc1 = m_mcu_lines[i + 0] + x;
+            pSrc2 = m_mcu_lines[i + 1] + x;
+            pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3] + pSrc2[ 0 * 3] + pSrc2[ 1 * 3] + a) >> 2) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3] + pSrc2[ 2 * 3] + pSrc2[ 3 * 3] + b) >> 2) - 128;
+            pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3] + pSrc2[ 4 * 3] + pSrc2[ 5 * 3] + a) >> 2) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3] + pSrc2[ 6 * 3] + pSrc2[ 7 * 3] + b) >> 2) - 128;
+            pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3] + pSrc2[ 8 * 3] + pSrc2[ 9 * 3] + a) >> 2) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3] + pSrc2[10 * 3] + pSrc2[11 * 3] + b) >> 2) - 128;
+            pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3] + pSrc2[12 * 3] + pSrc2[13 * 3] + a) >> 2) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3] + pSrc2[14 * 3] + pSrc2[15 * 3] + b) >> 2) - 128;
+            int temp = a; a = b; b = temp;
+        }
+    }
+
+    void jpeg_encoder::load_block_16_8_8(int x, int c)
+    {
+        uint8 *pSrc1;
+        sample_array_t *pDst = m_sample_array;
+        x = (x * (16 * 3)) + c;
+        for (int i = 0; i < 8; i++, pDst += 8)
+        {
+            pSrc1 = m_mcu_lines[i + 0] + x;
+            pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3]) >> 1) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3]) >> 1) - 128;
+            pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3]) >> 1) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3]) >> 1) - 128;
+            pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3]) >> 1) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3]) >> 1) - 128;
+            pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3]) >> 1) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3]) >> 1) - 128;
+        }
+    }
+
+    void jpeg_encoder::load_quantized_coefficients(int component_num)
+    {
+        int32 *q = m_quantization_tables[component_num > 0];
+        int16 *pDst = m_coefficient_array;
+        for (int i = 0; i < 64; i++)
+        {
+            sample_array_t j = m_sample_array[s_zag[i]];
+            if (j < 0)
+            {
+                if ((j = -j + (*q >> 1)) < *q)
+                    *pDst++ = 0;
+                else
+                    *pDst++ = static_cast<int16>(-(j / *q));
+            }
+            else
+            {
+                if ((j = j + (*q >> 1)) < *q)
+                    *pDst++ = 0;
+                else
+                    *pDst++ = static_cast<int16>((j / *q));
+            }
+            q++;
+        }
+    }
+
+    void jpeg_encoder::code_coefficients_pass_two(int component_num)
+    {
+        int i, j, run_len, nbits, temp1, temp2;
+        int16 *pSrc = m_coefficient_array;
+        uint *codes[2];
+        uint8 *code_sizes[2];
+
+        if (component_num == 0)
+        {
+            codes[0] = m_huff_codes[0 + 0]; codes[1] = m_huff_codes[2 + 0];
+            code_sizes[0] = m_huff_code_sizes[0 + 0]; code_sizes[1] = m_huff_code_sizes[2 + 0];
+        }
+        else
+        {
+            codes[0] = m_huff_codes[0 + 1]; codes[1] = m_huff_codes[2 + 1];
+            code_sizes[0] = m_huff_code_sizes[0 + 1]; code_sizes[1] = m_huff_code_sizes[2 + 1];
+        }
+
+        temp1 = temp2 = pSrc[0] - m_last_dc_val[component_num];
+        m_last_dc_val[component_num] = pSrc[0];
+
+        if (temp1 < 0)
+        {
+            temp1 = -temp1; temp2--;
+        }
+
+        nbits = 0;
+        while (temp1)
+        {
+            nbits++; temp1 >>= 1;
+        }
+
+        put_bits(codes[0][nbits], code_sizes[0][nbits]);
+        if (nbits) put_bits(temp2 & ((1 << nbits) - 1), nbits);
+
+        for (run_len = 0, i = 1; i < 64; i++)
+        {
+            if ((temp1 = m_coefficient_array[i]) == 0)
+                run_len++;
+            else
+            {
+                while (run_len >= 16)
+                {
+                    put_bits(codes[1][0xF0], code_sizes[1][0xF0]);
+                    run_len -= 16;
+                }
+                if ((temp2 = temp1) < 0)
+                {
+                    temp1 = -temp1;
+                    temp2--;
+                }
+                nbits = 1;
+                while (temp1 >>= 1)
+                    nbits++;
+                j = (run_len << 4) + nbits;
+                put_bits(codes[1][j], code_sizes[1][j]);
+                put_bits(temp2 & ((1 << nbits) - 1), nbits);
+                run_len = 0;
+            }
+        }
+        if (run_len)
+            put_bits(codes[1][0], code_sizes[1][0]);
+    }
+
+    void jpeg_encoder::code_block(int component_num)
+    {
+        DCT2D(m_sample_array);
+        load_quantized_coefficients(component_num);
+        code_coefficients_pass_two(component_num);
+    }
+
+    void jpeg_encoder::process_mcu_row()
+    {
+        if (m_num_components == 1)
+        {
+            for (int i = 0; i < m_mcus_per_row; i++)
+            {
+                load_block_8_8_grey(i); code_block(0);
+            }
+        }
+        else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1))
+        {
+            for (int i = 0; i < m_mcus_per_row; i++)
+            {
+                load_block_8_8(i, 0, 0); code_block(0); load_block_8_8(i, 0, 1); code_block(1); load_block_8_8(i, 0, 2); code_block(2);
+            }
+        }
+        else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1))
+        {
+            for (int i = 0; i < m_mcus_per_row; i++)
+            {
+                load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
+                load_block_16_8_8(i, 1); code_block(1); load_block_16_8_8(i, 2); code_block(2);
+            }
+        }
+        else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2))
+        {
+            for (int i = 0; i < m_mcus_per_row; i++)
+            {
+                load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
+                load_block_8_8(i * 2 + 0, 1, 0); code_block(0); load_block_8_8(i * 2 + 1, 1, 0); code_block(0);
+                load_block_16_8(i, 1); code_block(1); load_block_16_8(i, 2); code_block(2);
+            }
+        }
+    }
+
+    void jpeg_encoder::load_mcu(const void *pSrc)
+    {
+        const uint8* Psrc = reinterpret_cast<const uint8*>(pSrc);
+
+        uint8* pDst = m_mcu_lines[m_mcu_y_ofs]; // OK to write up to m_image_bpl_xlt bytes to pDst
+
+        if (m_num_components == 1) {
+            if (m_image_bpp == 3)
+                RGB_to_Y(pDst, Psrc, m_image_x);
+            else
+                memcpy(pDst, Psrc, m_image_x);
+        } else {
+            if (m_image_bpp == 3)
+                RGB_to_YCC(pDst, Psrc, m_image_x);
+            else
+                Y_to_YCC(pDst, Psrc, m_image_x);
+        }
+
+        // Possibly duplicate pixels at end of scanline if not a multiple of 8 or 16
+        if (m_num_components == 1)
+            memset(m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt, pDst[m_image_bpl_xlt - 1], m_image_x_mcu - m_image_x);
+        else
+        {
+            const uint8 y = pDst[m_image_bpl_xlt - 3 + 0], cb = pDst[m_image_bpl_xlt - 3 + 1], cr = pDst[m_image_bpl_xlt - 3 + 2];
+            uint8 *q = m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt;
+            for (int i = m_image_x; i < m_image_x_mcu; i++)
+            {
+                *q++ = y; *q++ = cb; *q++ = cr;
+            }
+        }
+
+        if (++m_mcu_y_ofs == m_mcu_y)
+        {
+            process_mcu_row();
+            m_mcu_y_ofs = 0;
+        }
+    }
+
+    // Quantization table generation.
+    void jpeg_encoder::compute_quant_table(int32 *pDst, const int16 *pSrc)
+    {
+        int32 q;
+        if (m_params.m_quality < 50)
+            q = 5000 / m_params.m_quality;
+        else
+            q = 200 - m_params.m_quality * 2;
+        for (int i = 0; i < 64; i++)
+        {
+            int32 j = *pSrc++; j = (j * q + 50L) / 100L;
+            *pDst++ = JPGE_MIN(JPGE_MAX(j, 1), 255);
+        }
+    }
+
+    // Higher-level methods.
+    bool jpeg_encoder::jpg_open(int p_x_res, int p_y_res, int src_channels)
+    {
+        m_num_components = 3;
+        switch (m_params.m_subsampling)
+        {
+            case Y_ONLY:
+            {
+                m_num_components = 1;
+                m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
+                m_mcu_x          = 8; m_mcu_y          = 8;
+                break;
+            }
+            case H1V1:
+            {
+                m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
+                m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
+                m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
+                m_mcu_x          = 8; m_mcu_y          = 8;
+                break;
+            }
+            case H2V1:
+            {
+                m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 1;
+                m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
+                m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
+                m_mcu_x          = 16; m_mcu_y         = 8;
+                break;
+            }
+            case H2V2:
+            {
+                m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 2;
+                m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
+                m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
+                m_mcu_x          = 16; m_mcu_y         = 16;
+            }
+        }
+
+        m_image_x        = p_x_res; m_image_y = p_y_res;
+        m_image_bpp      = src_channels;
+        m_image_bpl      = m_image_x * src_channels;
+        m_image_x_mcu    = (m_image_x + m_mcu_x - 1) & (~(m_mcu_x - 1));
+        m_image_y_mcu    = (m_image_y + m_mcu_y - 1) & (~(m_mcu_y - 1));
+        m_image_bpl_xlt  = m_image_x * m_num_components;
+        m_image_bpl_mcu  = m_image_x_mcu * m_num_components;
+        m_mcus_per_row   = m_image_x_mcu / m_mcu_x;
+
+        if ((m_mcu_lines[0] = static_cast<uint8*>(jpge_malloc(m_image_bpl_mcu * m_mcu_y))) == NULL) {
+            return false;
+        }
+        for (int i = 1; i < m_mcu_y; i++)
+            m_mcu_lines[i] = m_mcu_lines[i-1] + m_image_bpl_mcu;
+
+        if(m_last_quality != m_params.m_quality){
+            m_last_quality = m_params.m_quality;
+            compute_quant_table(m_quantization_tables[0], s_std_lum_quant);
+            compute_quant_table(m_quantization_tables[1], s_std_croma_quant);
+        }
+
+        if(!m_huff_initialized){
+            m_huff_initialized = true;
+
+            memcpy(m_huff_bits[0+0], s_dc_lum_bits, 17);    memcpy(m_huff_val[0+0], s_dc_lum_val, DC_LUM_CODES);
+            memcpy(m_huff_bits[2+0], s_ac_lum_bits, 17);    memcpy(m_huff_val[2+0], s_ac_lum_val, AC_LUM_CODES);
+            memcpy(m_huff_bits[0+1], s_dc_chroma_bits, 17); memcpy(m_huff_val[0+1], s_dc_chroma_val, DC_CHROMA_CODES);
+            memcpy(m_huff_bits[2+1], s_ac_chroma_bits, 17); memcpy(m_huff_val[2+1], s_ac_chroma_val, AC_CHROMA_CODES);
+
+            compute_huffman_table(&m_huff_codes[0+0][0], &m_huff_code_sizes[0+0][0], m_huff_bits[0+0], m_huff_val[0+0]);
+            compute_huffman_table(&m_huff_codes[2+0][0], &m_huff_code_sizes[2+0][0], m_huff_bits[2+0], m_huff_val[2+0]);
+            compute_huffman_table(&m_huff_codes[0+1][0], &m_huff_code_sizes[0+1][0], m_huff_bits[0+1], m_huff_val[0+1]);
+            compute_huffman_table(&m_huff_codes[2+1][0], &m_huff_code_sizes[2+1][0], m_huff_bits[2+1], m_huff_val[2+1]);
+        }
+
+        m_out_buf_left = JPGE_OUT_BUF_SIZE;
+        m_pOut_buf = m_out_buf;
+        m_bit_buffer = 0;
+        m_bits_in = 0;
+        m_mcu_y_ofs = 0;
+        m_pass_num = 2;
+        memset(m_last_dc_val, 0, 3 * sizeof(m_last_dc_val[0]));
+
+        // Emit all markers at beginning of image file.
+        emit_marker(M_SOI);
+        emit_jfif_app0();
+        emit_dqt();
+        emit_sof();
+        emit_dhts();
+        emit_sos();
+
+        return m_all_stream_writes_succeeded;
+    }
+
+    bool jpeg_encoder::process_end_of_image()
+    {
+        if (m_mcu_y_ofs) {
+            if (m_mcu_y_ofs < 16) { // check here just to shut up static analysis
+                for (int i = m_mcu_y_ofs; i < m_mcu_y; i++) {
+                    memcpy(m_mcu_lines[i], m_mcu_lines[m_mcu_y_ofs - 1], m_image_bpl_mcu);
+                }
+            }
+            process_mcu_row();
+        }
+
+        put_bits(0x7F, 7);
+        emit_marker(M_EOI);
+        flush_output_buffer();
+        m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_buf(NULL, 0);
+        m_pass_num++; // purposely bump up m_pass_num, for debugging
+        return true;
+    }
+
+    void jpeg_encoder::clear()
+    {
+        m_mcu_lines[0] = NULL;
+        m_pass_num = 0;
+        m_all_stream_writes_succeeded = true;
+    }
+
+    jpeg_encoder::jpeg_encoder()
+    {
+        clear();
+    }
+
+    jpeg_encoder::~jpeg_encoder()
+    {
+        deinit();
+    }
+
+    bool jpeg_encoder::init(output_stream *pStream, int width, int height, int src_channels, const params &comp_params)
+    {
+        deinit();
+        if (((!pStream) || (width < 1) || (height < 1)) || ((src_channels != 1) && (src_channels != 3) && (src_channels != 4)) || (!comp_params.check())) return false;
+        m_pStream = pStream;
+        m_params = comp_params;
+        return jpg_open(width, height, src_channels);
+    }
+
+    void jpeg_encoder::deinit()
+    {
+        jpge_free(m_mcu_lines[0]);
+        clear();
+    }
+
+    bool jpeg_encoder::process_scanline(const void* pScanline)
+    {
+        if ((m_pass_num < 1) || (m_pass_num > 2)) {
+            return false;
+        }
+        if (m_all_stream_writes_succeeded) {
+            if (!pScanline) {
+                if (!process_end_of_image()) {
+                    return false;
+                }
+            } else {
+                load_mcu(pScanline);
+            }
+        }
+        return m_all_stream_writes_succeeded;
+    }
+
+} // namespace jpge

+ 142 - 0
std/camera/conversions/private_include/jpge.h

@@ -0,0 +1,142 @@
+// jpge.h - C++ class for JPEG compression.
+// Public domain, Rich Geldreich <richgel99@gmail.com>
+// Alex Evans: Added RGBA support, linear memory allocator.
+#ifndef JPEG_ENCODER_H
+#define JPEG_ENCODER_H
+
+namespace jpge
+{
+    typedef unsigned char  uint8;
+    typedef signed short   int16;
+    typedef signed int     int32;
+    typedef unsigned short uint16;
+    typedef unsigned int   uint32;
+    typedef unsigned int   uint;
+
+    // JPEG chroma subsampling factors. Y_ONLY (grayscale images) and H2V2 (color images) are the most common.
+    enum subsampling_t { Y_ONLY = 0, H1V1 = 1, H2V1 = 2, H2V2 = 3 };
+
+    // JPEG compression parameters structure.
+    struct params {
+            inline params() : m_quality(85), m_subsampling(H2V2) { }
+
+            inline bool check() const {
+                if ((m_quality < 1) || (m_quality > 100)) {
+                    return false;
+                }
+                if ((uint)m_subsampling > (uint)H2V2) {
+                    return false;
+                }
+                return true;
+            }
+
+            // Quality: 1-100, higher is better. Typical values are around 50-95.
+            int m_quality;
+
+            // m_subsampling:
+            // 0 = Y (grayscale) only
+            // 1 = H1V1 subsampling (YCbCr 1x1x1, 3 blocks per MCU)
+            // 2 = H2V1 subsampling (YCbCr 2x1x1, 4 blocks per MCU)
+            // 3 = H2V2 subsampling (YCbCr 4x1x1, 6 blocks per MCU-- very common)
+            subsampling_t m_subsampling;
+    };
+    
+    // Output stream abstract class - used by the jpeg_encoder class to write to the output stream.
+    // put_buf() is generally called with len==JPGE_OUT_BUF_SIZE bytes, but for headers it'll be called with smaller amounts.
+    class output_stream {
+        public:
+            virtual ~output_stream() { };
+            virtual bool put_buf(const void* Pbuf, int len) = 0;
+            virtual uint get_size() const = 0;
+    };
+    
+    // Lower level jpeg_encoder class - useful if more control is needed than the above helper functions.
+    class jpeg_encoder {
+        public:
+            jpeg_encoder();
+            ~jpeg_encoder();
+
+            // Initializes the compressor.
+            // pStream: The stream object to use for writing compressed data.
+            // params - Compression parameters structure, defined above.
+            // width, height  - Image dimensions.
+            // channels - May be 1, or 3. 1 indicates grayscale, 3 indicates RGB source data.
+            // Returns false on out of memory or if a stream write fails.
+            bool init(output_stream *pStream, int width, int height, int src_channels, const params &comp_params = params());
+
+            // Call this method with each source scanline.
+            // width * src_channels bytes per scanline is expected (RGB or Y format).
+            // You must call with NULL after all scanlines are processed to finish compression.
+            // Returns false on out of memory or if a stream write fails.
+            bool process_scanline(const void* pScanline);
+
+            // Deinitializes the compressor, freeing any allocated memory. May be called at any time.
+            void deinit();
+
+        private:
+            jpeg_encoder(const jpeg_encoder &);
+            jpeg_encoder &operator =(const jpeg_encoder &);
+
+            typedef int32 sample_array_t;
+            enum { JPGE_OUT_BUF_SIZE = 512 };
+
+            output_stream *m_pStream;
+            params m_params;
+            uint8 m_num_components;
+            uint8 m_comp_h_samp[3], m_comp_v_samp[3];
+            int m_image_x, m_image_y, m_image_bpp, m_image_bpl;
+            int m_image_x_mcu, m_image_y_mcu;
+            int m_image_bpl_xlt, m_image_bpl_mcu;
+            int m_mcus_per_row;
+            int m_mcu_x, m_mcu_y;
+            uint8 *m_mcu_lines[16];
+            uint8 m_mcu_y_ofs;
+            sample_array_t m_sample_array[64];
+            int16 m_coefficient_array[64];
+
+            int m_last_dc_val[3];
+            uint8 m_out_buf[JPGE_OUT_BUF_SIZE];
+            uint8 *m_pOut_buf;
+            uint m_out_buf_left;
+            uint32 m_bit_buffer;
+            uint m_bits_in;
+            uint8 m_pass_num;
+            bool m_all_stream_writes_succeeded;
+
+            bool jpg_open(int p_x_res, int p_y_res, int src_channels);
+
+            void flush_output_buffer();
+            void put_bits(uint bits, uint len);
+
+            void emit_byte(uint8 i);
+            void emit_word(uint i);
+            void emit_marker(int marker);
+
+            void emit_jfif_app0();
+            void emit_dqt();
+            void emit_sof();
+            void emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag);
+            void emit_dhts();
+            void emit_sos();
+
+            void compute_quant_table(int32 *dst, const int16 *src);
+            void load_quantized_coefficients(int component_num);
+
+            void load_block_8_8_grey(int x);
+            void load_block_8_8(int x, int y, int c);
+            void load_block_16_8(int x, int c);
+            void load_block_16_8_8(int x, int c);
+
+            void code_coefficients_pass_two(int component_num);
+            void code_block(int component_num);
+
+            void process_mcu_row();
+            bool process_end_of_image();
+            void load_mcu(const void* src);
+            void clear();
+            void init();
+    };
+    
+} // namespace jpge
+
+#endif // JPEG_ENCODER

+ 29 - 0
std/camera/conversions/private_include/yuv.h

@@ -0,0 +1,29 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef _CONVERSIONS_YUV_H_
+#define _CONVERSIONS_YUV_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CONVERSIONS_YUV_H_ */

+ 393 - 0
std/camera/conversions/to_bmp.c

@@ -0,0 +1,393 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include <stddef.h>
+#include <string.h>
+#include "img_converters.h"
+#include "soc/efuse_reg.h"
+#include "esp_heap_caps.h"
+#include "yuv.h"
+#include "sdkconfig.h"
+#include "esp_jpg_decode.h"
+
+#include "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 ""
+#else
+#include "esp_log.h"
+static const char* TAG = "to_bmp";
+#endif
+
+static const int BMP_HEADER_LEN = 54;
+
+typedef struct {
+    uint32_t filesize;
+    uint32_t reserved;
+    uint32_t fileoffset_to_pixelarray;
+    uint32_t dibheadersize;
+    int32_t width;
+    int32_t height;
+    uint16_t planes;
+    uint16_t bitsperpixel;
+    uint32_t compression;
+    uint32_t imagesize;
+    uint32_t ypixelpermeter;
+    uint32_t xpixelpermeter;
+    uint32_t numcolorspallette;
+    uint32_t mostimpcolor;
+} bmp_header_t;
+
+typedef struct {
+        uint16_t width;
+        uint16_t height;
+        uint16_t data_offset;
+        const uint8_t *input;
+        uint8_t *output;
+} rgb_jpg_decoder;
+
+static void *_malloc(size_t size)
+{
+    return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+}
+
+//output buffer and image width
+static bool _rgb_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data)
+{
+    rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
+    if(!data){
+        if(x == 0 && y == 0){
+            //write start
+            jpeg->width = w;
+            jpeg->height = h;
+            //if output is null, this is BMP
+            if(!jpeg->output){
+                jpeg->output = (uint8_t *)_malloc((w*h*3)+jpeg->data_offset);
+                if(!jpeg->output){
+                    return false;
+                }
+            }
+        } else {
+            //write end
+        }
+        return true;
+    }
+
+    size_t jw = jpeg->width*3;
+    size_t t = y * jw;
+    size_t b = t + (h * jw);
+    size_t l = x * 3;
+    uint8_t *out = jpeg->output+jpeg->data_offset;
+    uint8_t *o = out;
+    size_t iy, ix;
+
+    w = w * 3;
+
+    for(iy=t; iy<b; iy+=jw) {
+        o = out+iy+l;
+        for(ix=0; ix<w; ix+= 3) {
+            o[ix] = data[ix+2];
+            o[ix+1] = data[ix+1];
+            o[ix+2] = data[ix];
+        }
+        data+=w;
+    }
+    return true;
+}
+
+static bool _rgb565_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data)
+{
+    rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
+    if(!data){
+        if(x == 0 && y == 0){
+            //write start
+            jpeg->width = w;
+            jpeg->height = h;
+            //if output is null, this is BMP
+            if(!jpeg->output){
+                jpeg->output = (uint8_t *)_malloc((w*h*3)+jpeg->data_offset);
+                if(!jpeg->output){
+                    return false;
+                }
+            }
+        } else {
+            //write end
+        }
+        return true;
+    }
+
+    size_t jw = jpeg->width*3;
+    size_t jw2 = jpeg->width*2;
+    size_t t = y * jw;
+    size_t t2 = y * jw2;
+    size_t b = t + (h * jw);
+    size_t l = x * 2;
+    uint8_t *out = jpeg->output+jpeg->data_offset;
+    uint8_t *o = out;
+    size_t iy, iy2, ix, ix2;
+
+    w = w * 3;
+
+    for(iy=t, iy2=t2; iy<b; iy+=jw, iy2+=jw2) {
+        o = out+iy2+l;
+        for(ix2=ix=0; ix<w; ix+= 3, ix2 +=2) {
+            uint16_t r = data[ix];
+            uint16_t g = data[ix+1];
+            uint16_t b = data[ix+2];
+            uint16_t c = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
+            o[ix2+1] = c>>8;
+            o[ix2] = c&0xff;
+        }
+        data+=w;
+    }
+    return true;
+}
+
+//input buffer
+static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
+{
+    rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
+    if(buf) {
+        memcpy(buf, jpeg->input + index, len);
+    }
+    return len;
+}
+
+static bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale)
+{
+    rgb_jpg_decoder jpeg;
+    jpeg.width = 0;
+    jpeg.height = 0;
+    jpeg.input = src;
+    jpeg.output = out;
+    jpeg.data_offset = 0;
+
+    if(esp_jpg_decode(src_len, scale, _jpg_read, _rgb_write, (void*)&jpeg) != ESP_OK){
+        return false;
+    }
+    return true;
+}
+
+bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale)
+{
+    rgb_jpg_decoder jpeg;
+    jpeg.width = 0;
+    jpeg.height = 0;
+    jpeg.input = src;
+    jpeg.output = out;
+    jpeg.data_offset = 0;
+
+    if(esp_jpg_decode(src_len, scale, _jpg_read, _rgb565_write, (void*)&jpeg) != ESP_OK){
+        return false;
+    }
+    return true;
+}
+
+bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_len)
+{
+
+    rgb_jpg_decoder jpeg;
+    jpeg.width = 0;
+    jpeg.height = 0;
+    jpeg.input = src;
+    jpeg.output = NULL;
+    jpeg.data_offset = BMP_HEADER_LEN;
+
+    if(esp_jpg_decode(src_len, JPG_SCALE_NONE, _jpg_read, _rgb_write, (void*)&jpeg) != ESP_OK){
+        return false;
+    }
+
+    size_t output_size = jpeg.width*jpeg.height*3;
+
+    jpeg.output[0] = 'B';
+    jpeg.output[1] = 'M';
+    bmp_header_t * bitmap  = (bmp_header_t*)&jpeg.output[2];
+    bitmap->reserved = 0;
+    bitmap->filesize = output_size+BMP_HEADER_LEN;
+    bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN;
+    bitmap->dibheadersize = 40;
+    bitmap->width = jpeg.width;
+    bitmap->height = -jpeg.height;//set negative for top to bottom
+    bitmap->planes = 1;
+    bitmap->bitsperpixel = 24;
+    bitmap->compression = 0;
+    bitmap->imagesize = output_size;
+    bitmap->ypixelpermeter = 0x0B13 ; //2835 , 72 DPI
+    bitmap->xpixelpermeter = 0x0B13 ; //2835 , 72 DPI
+    bitmap->numcolorspallette = 0;
+    bitmap->mostimpcolor = 0;
+
+    *out = jpeg.output;
+    *out_len = output_size+BMP_HEADER_LEN;
+
+    return true;
+}
+
+bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf)
+{
+    int pix_count = 0;
+    if(format == PIXFORMAT_JPEG) {
+        return jpg2rgb888(src_buf, src_len, rgb_buf, JPG_SCALE_NONE);
+    } else if(format == PIXFORMAT_RGB888) {
+        memcpy(rgb_buf, src_buf, src_len);
+    } else if(format == PIXFORMAT_RGB565) {
+        int i;
+        uint8_t hb, lb;
+        pix_count = src_len / 2;
+        for(i=0; i<pix_count; i++) {
+            hb = *src_buf++;
+            lb = *src_buf++;
+            *rgb_buf++ = (lb & 0x1F) << 3;
+            *rgb_buf++ = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
+            *rgb_buf++ = hb & 0xF8;
+        }
+    } else if(format == PIXFORMAT_GRAYSCALE) {
+        int i;
+        uint8_t b;
+        pix_count = src_len;
+        for(i=0; i<pix_count; i++) {
+            b = *src_buf++;
+            *rgb_buf++ = b;
+            *rgb_buf++ = b;
+            *rgb_buf++ = b;
+        }
+    } else if(format == PIXFORMAT_YUV422) {
+        pix_count = src_len / 2;
+        int i, maxi = pix_count / 2;
+        uint8_t y0, y1, u, v;
+        uint8_t r, g, b;
+        for(i=0; i<maxi; i++) {
+            y0 = *src_buf++;
+            u = *src_buf++;
+            y1 = *src_buf++;
+            v = *src_buf++;
+
+            yuv2rgb(y0, u, v, &r, &g, &b);
+            *rgb_buf++ = b;
+            *rgb_buf++ = g;
+            *rgb_buf++ = r;
+
+            yuv2rgb(y1, u, v, &r, &g, &b);
+            *rgb_buf++ = b;
+            *rgb_buf++ = g;
+            *rgb_buf++ = r;
+        }
+    }
+    return true;
+}
+
+bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t ** out, size_t * out_len)
+{
+    if(format == PIXFORMAT_JPEG) {
+        return jpg2bmp(src, src_len, out, out_len);
+    }
+
+    *out = NULL;
+    *out_len = 0;
+
+    int pix_count = width*height;
+    size_t out_size = (pix_count * 3) + BMP_HEADER_LEN;
+    uint8_t * out_buf = (uint8_t *)_malloc(out_size);
+    if(!out_buf) {
+        ESP_LOGE(TAG, "_malloc failed! %u", out_size);
+        return false;
+    }
+
+    out_buf[0] = 'B';
+    out_buf[1] = 'M';
+    bmp_header_t * bitmap  = (bmp_header_t*)&out_buf[2];
+    bitmap->reserved = 0;
+    bitmap->filesize = out_size;
+    bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN;
+    bitmap->dibheadersize = 40;
+    bitmap->width = width;
+    bitmap->height = -height;//set negative for top to bottom
+    bitmap->planes = 1;
+    bitmap->bitsperpixel = 24;
+    bitmap->compression = 0;
+    bitmap->imagesize = pix_count * 3;
+    bitmap->ypixelpermeter = 0x0B13 ; //2835 , 72 DPI
+    bitmap->xpixelpermeter = 0x0B13 ; //2835 , 72 DPI
+    bitmap->numcolorspallette = 0;
+    bitmap->mostimpcolor = 0;
+
+    uint8_t * rgb_buf = out_buf + BMP_HEADER_LEN;
+    uint8_t * src_buf = src;
+
+
+    //convert data to RGB888
+    if(format == PIXFORMAT_RGB888) {
+        memcpy(rgb_buf, src_buf, pix_count*3);
+    } else if(format == PIXFORMAT_RGB565) {
+        int i;
+        uint8_t hb, lb;
+        for(i=0; i<pix_count; i++) {
+            hb = *src_buf++;
+            lb = *src_buf++;
+            *rgb_buf++ = (lb & 0x1F) << 3;
+            *rgb_buf++ = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
+            *rgb_buf++ = hb & 0xF8;
+        }
+    } else if(format == PIXFORMAT_GRAYSCALE) {
+        int i;
+        uint8_t b;
+        for(i=0; i<pix_count; i++) {
+            b = *src_buf++;
+            *rgb_buf++ = b;
+            *rgb_buf++ = b;
+            *rgb_buf++ = b;
+        }
+    } else if(format == PIXFORMAT_YUV422) {
+        int i, maxi = pix_count / 2;
+        uint8_t y0, y1, u, v;
+        uint8_t r, g, b;
+        for(i=0; i<maxi; i++) {
+            y0 = *src_buf++;
+            u = *src_buf++;
+            y1 = *src_buf++;
+            v = *src_buf++;
+
+            yuv2rgb(y0, u, v, &r, &g, &b);
+            *rgb_buf++ = b;
+            *rgb_buf++ = g;
+            *rgb_buf++ = r;
+
+            yuv2rgb(y1, u, v, &r, &g, &b);
+            *rgb_buf++ = b;
+            *rgb_buf++ = g;
+            *rgb_buf++ = r;
+        }
+    }
+    *out = out_buf;
+    *out_len = out_size;
+    return true;
+}
+
+bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len)
+{
+    return fmt2bmp(fb->buf, fb->len, fb->width, fb->height, fb->format, out, out_len);
+}

+ 245 - 0
std/camera/conversions/to_jpg.cpp

@@ -0,0 +1,245 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include <stddef.h>
+#include <string.h>
+#include "esp_attr.h"
+#include "soc/efuse_reg.h"
+#include "esp_heap_caps.h"
+#include "esp_camera.h"
+#include "img_converters.h"
+#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 ""
+#else
+#include "esp_log.h"
+static const char* TAG = "to_jpg";
+#endif
+
+static void *_malloc(size_t size)
+{
+    void * res = malloc(size);
+    if(res) {
+        return res;
+    }
+    return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+}
+
+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)
+{
+    int i=0, o=0, l=0;
+    if(format == PIXFORMAT_GRAYSCALE) {
+        memcpy(dst, src + line * width, width);
+    } else if(format == PIXFORMAT_RGB888) {
+        l = width * 3;
+        src += l * line;
+        for(i=0; i<l; i+=3) {
+            dst[o++] = src[i+2];
+            dst[o++] = src[i+1];
+            dst[o++] = src[i];
+        }
+    } else if(format == PIXFORMAT_RGB565) {
+        l = width * 2;
+        src += l * line;
+        for(i=0; i<l; i+=2) {
+            dst[o++] = src[i] & 0xF8;
+            dst[o++] = (src[i] & 0x07) << 5 | (src[i+1] & 0xE0) >> 3;
+            dst[o++] = (src[i+1] & 0x1F) << 3;
+        }
+    } else if(format == PIXFORMAT_YUV422) {
+        uint8_t y0, y1, u, v;
+        uint8_t r, g, b;
+        l = width * 2;
+        src += l * line;
+        for(i=0; i<l; i+=4) {
+            y0 = src[i];
+            u = src[i+1];
+            y1 = src[i+2];
+            v = src[i+3];
+
+            yuv2rgb(y0, u, v, &r, &g, &b);
+            dst[o++] = r;
+            dst[o++] = g;
+            dst[o++] = b;
+
+            yuv2rgb(y1, u, v, &r, &g, &b);
+            dst[o++] = r;
+            dst[o++] = g;
+            dst[o++] = b;
+        }
+    }
+}
+
+bool convert_image(uint8_t *src, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, jpge::output_stream *dst_stream)
+{
+    int num_channels = 3;
+    jpge::subsampling_t subsampling = jpge::H2V2;
+
+    if(format == PIXFORMAT_GRAYSCALE) {
+        num_channels = 1;
+        subsampling = jpge::Y_ONLY;
+    }
+
+    if(!quality) {
+        quality = 1;
+    } else if(quality > 100) {
+        quality = 100;
+    }
+
+    jpge::params comp_params = jpge::params();
+    comp_params.m_subsampling = subsampling;
+    comp_params.m_quality = quality;
+
+    jpge::jpeg_encoder dst_image;
+
+    if (!dst_image.init(dst_stream, width, height, num_channels, comp_params)) {
+        ESP_LOGE(TAG, "JPG encoder init failed");
+        return false;
+    }
+
+    uint8_t* line = (uint8_t*)_malloc(width * num_channels);
+    if(!line) {
+        ESP_LOGE(TAG, "Scan line malloc failed");
+        return false;
+    }
+
+    for (int i = 0; i < height; i++) {
+        convert_line_format(src, format, line, width, num_channels, i);
+        if (!dst_image.process_scanline(line)) {
+            ESP_LOGE(TAG, "JPG process line %u failed", i);
+            free(line);
+            return false;
+        }
+    }
+    free(line);
+
+    if (!dst_image.process_scanline(NULL)) {
+        ESP_LOGE(TAG, "JPG image finish failed");
+        return false;
+    }
+    dst_image.deinit();
+    return true;
+}
+
+class callback_stream : public jpge::output_stream {
+protected:
+    jpg_out_cb ocb;
+    void * oarg;
+    size_t index;
+
+public:
+    callback_stream(jpg_out_cb cb, void * arg) : ocb(cb), oarg(arg), index(0) { }
+    virtual ~callback_stream() { }
+    virtual bool put_buf(const void* data, int len)
+    {
+        index += ocb(oarg, index, data, len);
+        return true;
+    }
+    virtual size_t get_size() const
+    {
+        return index;
+    }
+};
+
+bool fmt2jpg_cb(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, jpg_out_cb cb, void * arg)
+{
+    callback_stream dst_stream(cb, arg);
+    return convert_image(src, width, height, format, quality, &dst_stream);
+}
+
+bool frame2jpg_cb(camera_fb_t * fb, uint8_t quality, jpg_out_cb cb, void * arg)
+{
+    return fmt2jpg_cb(fb->buf, fb->len, fb->width, fb->height, fb->format, quality, cb, arg);
+}
+
+
+
+class memory_stream : public jpge::output_stream {
+protected:
+    uint8_t *out_buf;
+    size_t max_len, index;
+
+public:
+    memory_stream(void *pBuf, uint buf_size) : out_buf(static_cast<uint8_t*>(pBuf)), max_len(buf_size), index(0) { }
+
+    virtual ~memory_stream() { }
+
+    virtual bool put_buf(const void* pBuf, int len)
+    {
+        if (!pBuf) {
+            //end of image
+            return true;
+        }
+        if ((size_t)len > (max_len - index)) {
+            //ESP_LOGW(TAG, "JPG output overflow: %d bytes (%d,%d,%d)", len - (max_len - index), len, index, max_len);
+            len = max_len - index;
+        }
+        if (len) {
+            memcpy(out_buf + index, pBuf, len);
+            index += len;
+        }
+        return true;
+    }
+
+    virtual size_t get_size() const
+    {
+        return index;
+    }
+};
+
+bool fmt2jpg(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, uint8_t ** out, size_t * out_len)
+{
+    //todo: allocate proper buffer for holding JPEG data
+    //this should be enough for CIF frame size
+    int jpg_buf_len = 128*1024;
+
+
+    uint8_t * jpg_buf = (uint8_t *)_malloc(jpg_buf_len);
+    if(jpg_buf == NULL) {
+        ESP_LOGE(TAG, "JPG buffer malloc failed");
+        return false;
+    }
+    memory_stream dst_stream(jpg_buf, jpg_buf_len);
+
+    if(!convert_image(src, width, height, format, quality, &dst_stream)) {
+        free(jpg_buf);
+        return false;
+    }
+
+    *out = jpg_buf;
+    *out_len = dst_stream.get_size();
+    return true;
+}
+
+bool frame2jpg(camera_fb_t * fb, uint8_t quality, uint8_t ** out, size_t * out_len)
+{
+    return fmt2jpg(fb->buf, fb->len, fb->width, fb->height, fb->format, quality, out, out_len);
+}

+ 298 - 0
std/camera/conversions/yuv.c

@@ -0,0 +1,298 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "yuv.h"
+#include "esp_attr.h"
+
+typedef struct {
+        int16_t vY;
+        int16_t vVr;
+        int16_t vVg;
+        int16_t vUg;
+        int16_t vUb;
+} yuv_table_row;
+
+static const yuv_table_row yuv_table[256] = {
+    //  Y    Vr    Vg    Ug    Ub     // #
+    {  -18, -204,   50,  104, -258 }, // 0
+    {  -17, -202,   49,  103, -256 }, // 1
+    {  -16, -201,   49,  102, -254 }, // 2
+    {  -15, -199,   48,  101, -252 }, // 3
+    {  -13, -197,   48,  100, -250 }, // 4
+    {  -12, -196,   48,   99, -248 }, // 5
+    {  -11, -194,   47,   99, -246 }, // 6
+    {  -10, -193,   47,   98, -244 }, // 7
+    {   -9, -191,   46,   97, -242 }, // 8
+    {   -8, -189,   46,   96, -240 }, // 9
+    {   -6, -188,   46,   95, -238 }, // 10
+    {   -5, -186,   45,   95, -236 }, // 11
+    {   -4, -185,   45,   94, -234 }, // 12
+    {   -3, -183,   44,   93, -232 }, // 13
+    {   -2, -181,   44,   92, -230 }, // 14
+    {   -1, -180,   44,   91, -228 }, // 15
+    {    0, -178,   43,   91, -226 }, // 16
+    {    1, -177,   43,   90, -223 }, // 17
+    {    2, -175,   43,   89, -221 }, // 18
+    {    3, -173,   42,   88, -219 }, // 19
+    {    4, -172,   42,   87, -217 }, // 20
+    {    5, -170,   41,   86, -215 }, // 21
+    {    6, -169,   41,   86, -213 }, // 22
+    {    8, -167,   41,   85, -211 }, // 23
+    {    9, -165,   40,   84, -209 }, // 24
+    {   10, -164,   40,   83, -207 }, // 25
+    {   11, -162,   39,   82, -205 }, // 26
+    {   12, -161,   39,   82, -203 }, // 27
+    {   13, -159,   39,   81, -201 }, // 28
+    {   15, -158,   38,   80, -199 }, // 29
+    {   16, -156,   38,   79, -197 }, // 30
+    {   17, -154,   37,   78, -195 }, // 31
+    {   18, -153,   37,   78, -193 }, // 32
+    {   19, -151,   37,   77, -191 }, // 33
+    {   20, -150,   36,   76, -189 }, // 34
+    {   22, -148,   36,   75, -187 }, // 35
+    {   23, -146,   35,   74, -185 }, // 36
+    {   24, -145,   35,   73, -183 }, // 37
+    {   25, -143,   35,   73, -181 }, // 38
+    {   26, -142,   34,   72, -179 }, // 39
+    {   27, -140,   34,   71, -177 }, // 40
+    {   29, -138,   34,   70, -175 }, // 41
+    {   30, -137,   33,   69, -173 }, // 42
+    {   31, -135,   33,   69, -171 }, // 43
+    {   32, -134,   32,   68, -169 }, // 44
+    {   33, -132,   32,   67, -167 }, // 45
+    {   34, -130,   32,   66, -165 }, // 46
+    {   36, -129,   31,   65, -163 }, // 47
+    {   37, -127,   31,   65, -161 }, // 48
+    {   38, -126,   30,   64, -159 }, // 49
+    {   39, -124,   30,   63, -157 }, // 50
+    {   40, -122,   30,   62, -155 }, // 51
+    {   41, -121,   29,   61, -153 }, // 52
+    {   43, -119,   29,   60, -151 }, // 53
+    {   44, -118,   28,   60, -149 }, // 54
+    {   45, -116,   28,   59, -147 }, // 55
+    {   46, -114,   28,   58, -145 }, // 56
+    {   47, -113,   27,   57, -143 }, // 57
+    {   48, -111,   27,   56, -141 }, // 58
+    {   50, -110,   26,   56, -139 }, // 59
+    {   51, -108,   26,   55, -137 }, // 60
+    {   52, -106,   26,   54, -135 }, // 61
+    {   53, -105,   25,   53, -133 }, // 62
+    {   54, -103,   25,   52, -131 }, // 63
+    {   55, -102,   25,   52, -129 }, // 64
+    {   57, -100,   24,   51, -127 }, // 65
+    {   58,  -98,   24,   50, -125 }, // 66
+    {   59,  -97,   23,   49, -123 }, // 67
+    {   60,  -95,   23,   48, -121 }, // 68
+    {   61,  -94,   23,   47, -119 }, // 69
+    {   62,  -92,   22,   47, -117 }, // 70
+    {   64,  -90,   22,   46, -115 }, // 71
+    {   65,  -89,   21,   45, -113 }, // 72
+    {   66,  -87,   21,   44, -110 }, // 73
+    {   67,  -86,   21,   43, -108 }, // 74
+    {   68,  -84,   20,   43, -106 }, // 75
+    {   69,  -82,   20,   42, -104 }, // 76
+    {   71,  -81,   19,   41, -102 }, // 77
+    {   72,  -79,   19,   40, -100 }, // 78
+    {   73,  -78,   19,   39,  -98 }, // 79
+    {   74,  -76,   18,   39,  -96 }, // 80
+    {   75,  -75,   18,   38,  -94 }, // 81
+    {   76,  -73,   17,   37,  -92 }, // 82
+    {   77,  -71,   17,   36,  -90 }, // 83
+    {   79,  -70,   17,   35,  -88 }, // 84
+    {   80,  -68,   16,   34,  -86 }, // 85
+    {   81,  -67,   16,   34,  -84 }, // 86
+    {   82,  -65,   16,   33,  -82 }, // 87
+    {   83,  -63,   15,   32,  -80 }, // 88
+    {   84,  -62,   15,   31,  -78 }, // 89
+    {   86,  -60,   14,   30,  -76 }, // 90
+    {   87,  -59,   14,   30,  -74 }, // 91
+    {   88,  -57,   14,   29,  -72 }, // 92
+    {   89,  -55,   13,   28,  -70 }, // 93
+    {   90,  -54,   13,   27,  -68 }, // 94
+    {   91,  -52,   12,   26,  -66 }, // 95
+    {   93,  -51,   12,   26,  -64 }, // 96
+    {   94,  -49,   12,   25,  -62 }, // 97
+    {   95,  -47,   11,   24,  -60 }, // 98
+    {   96,  -46,   11,   23,  -58 }, // 99
+    {   97,  -44,   10,   22,  -56 }, // 100
+    {   98,  -43,   10,   21,  -54 }, // 101
+    {  100,  -41,   10,   21,  -52 }, // 102
+    {  101,  -39,    9,   20,  -50 }, // 103
+    {  102,  -38,    9,   19,  -48 }, // 104
+    {  103,  -36,    8,   18,  -46 }, // 105
+    {  104,  -35,    8,   17,  -44 }, // 106
+    {  105,  -33,    8,   17,  -42 }, // 107
+    {  107,  -31,    7,   16,  -40 }, // 108
+    {  108,  -30,    7,   15,  -38 }, // 109
+    {  109,  -28,    7,   14,  -36 }, // 110
+    {  110,  -27,    6,   13,  -34 }, // 111
+    {  111,  -25,    6,   13,  -32 }, // 112
+    {  112,  -23,    5,   12,  -30 }, // 113
+    {  114,  -22,    5,   11,  -28 }, // 114
+    {  115,  -20,    5,   10,  -26 }, // 115
+    {  116,  -19,    4,    9,  -24 }, // 116
+    {  117,  -17,    4,    8,  -22 }, // 117
+    {  118,  -15,    3,    8,  -20 }, // 118
+    {  119,  -14,    3,    7,  -18 }, // 119
+    {  121,  -12,    3,    6,  -16 }, // 120
+    {  122,  -11,    2,    5,  -14 }, // 121
+    {  123,   -9,    2,    4,  -12 }, // 122
+    {  124,   -7,    1,    4,  -10 }, // 123
+    {  125,   -6,    1,    3,   -8 }, // 124
+    {  126,   -4,    1,    2,   -6 }, // 125
+    {  128,   -3,    0,    1,   -4 }, // 126
+    {  129,   -1,    0,    0,   -2 }, // 127
+    {  130,    0,    0,    0,    0 }, // 128
+    {  131,    1,    0,    0,    2 }, // 129
+    {  132,    3,    0,   -1,    4 }, // 130
+    {  133,    4,   -1,   -2,    6 }, // 131
+    {  135,    6,   -1,   -3,    8 }, // 132
+    {  136,    7,   -1,   -4,   10 }, // 133
+    {  137,    9,   -2,   -4,   12 }, // 134
+    {  138,   11,   -2,   -5,   14 }, // 135
+    {  139,   12,   -3,   -6,   16 }, // 136
+    {  140,   14,   -3,   -7,   18 }, // 137
+    {  142,   15,   -3,   -8,   20 }, // 138
+    {  143,   17,   -4,   -8,   22 }, // 139
+    {  144,   19,   -4,   -9,   24 }, // 140
+    {  145,   20,   -5,  -10,   26 }, // 141
+    {  146,   22,   -5,  -11,   28 }, // 142
+    {  147,   23,   -5,  -12,   30 }, // 143
+    {  148,   25,   -6,  -13,   32 }, // 144
+    {  150,   27,   -6,  -13,   34 }, // 145
+    {  151,   28,   -7,  -14,   36 }, // 146
+    {  152,   30,   -7,  -15,   38 }, // 147
+    {  153,   31,   -7,  -16,   40 }, // 148
+    {  154,   33,   -8,  -17,   42 }, // 149
+    {  155,   35,   -8,  -17,   44 }, // 150
+    {  157,   36,   -8,  -18,   46 }, // 151
+    {  158,   38,   -9,  -19,   48 }, // 152
+    {  159,   39,   -9,  -20,   50 }, // 153
+    {  160,   41,  -10,  -21,   52 }, // 154
+    {  161,   43,  -10,  -21,   54 }, // 155
+    {  162,   44,  -10,  -22,   56 }, // 156
+    {  164,   46,  -11,  -23,   58 }, // 157
+    {  165,   47,  -11,  -24,   60 }, // 158
+    {  166,   49,  -12,  -25,   62 }, // 159
+    {  167,   51,  -12,  -26,   64 }, // 160
+    {  168,   52,  -12,  -26,   66 }, // 161
+    {  169,   54,  -13,  -27,   68 }, // 162
+    {  171,   55,  -13,  -28,   70 }, // 163
+    {  172,   57,  -14,  -29,   72 }, // 164
+    {  173,   59,  -14,  -30,   74 }, // 165
+    {  174,   60,  -14,  -30,   76 }, // 166
+    {  175,   62,  -15,  -31,   78 }, // 167
+    {  176,   63,  -15,  -32,   80 }, // 168
+    {  178,   65,  -16,  -33,   82 }, // 169
+    {  179,   67,  -16,  -34,   84 }, // 170
+    {  180,   68,  -16,  -34,   86 }, // 171
+    {  181,   70,  -17,  -35,   88 }, // 172
+    {  182,   71,  -17,  -36,   90 }, // 173
+    {  183,   73,  -17,  -37,   92 }, // 174
+    {  185,   75,  -18,  -38,   94 }, // 175
+    {  186,   76,  -18,  -39,   96 }, // 176
+    {  187,   78,  -19,  -39,   98 }, // 177
+    {  188,   79,  -19,  -40,  100 }, // 178
+    {  189,   81,  -19,  -41,  102 }, // 179
+    {  190,   82,  -20,  -42,  104 }, // 180
+    {  192,   84,  -20,  -43,  106 }, // 181
+    {  193,   86,  -21,  -43,  108 }, // 182
+    {  194,   87,  -21,  -44,  110 }, // 183
+    {  195,   89,  -21,  -45,  113 }, // 184
+    {  196,   90,  -22,  -46,  115 }, // 185
+    {  197,   92,  -22,  -47,  117 }, // 186
+    {  199,   94,  -23,  -47,  119 }, // 187
+    {  200,   95,  -23,  -48,  121 }, // 188
+    {  201,   97,  -23,  -49,  123 }, // 189
+    {  202,   98,  -24,  -50,  125 }, // 190
+    {  203,  100,  -24,  -51,  127 }, // 191
+    {  204,  102,  -25,  -52,  129 }, // 192
+    {  206,  103,  -25,  -52,  131 }, // 193
+    {  207,  105,  -25,  -53,  133 }, // 194
+    {  208,  106,  -26,  -54,  135 }, // 195
+    {  209,  108,  -26,  -55,  137 }, // 196
+    {  210,  110,  -26,  -56,  139 }, // 197
+    {  211,  111,  -27,  -56,  141 }, // 198
+    {  213,  113,  -27,  -57,  143 }, // 199
+    {  214,  114,  -28,  -58,  145 }, // 200
+    {  215,  116,  -28,  -59,  147 }, // 201
+    {  216,  118,  -28,  -60,  149 }, // 202
+    {  217,  119,  -29,  -60,  151 }, // 203
+    {  218,  121,  -29,  -61,  153 }, // 204
+    {  219,  122,  -30,  -62,  155 }, // 205
+    {  221,  124,  -30,  -63,  157 }, // 206
+    {  222,  126,  -30,  -64,  159 }, // 207
+    {  223,  127,  -31,  -65,  161 }, // 208
+    {  224,  129,  -31,  -65,  163 }, // 209
+    {  225,  130,  -32,  -66,  165 }, // 210
+    {  226,  132,  -32,  -67,  167 }, // 211
+    {  228,  134,  -32,  -68,  169 }, // 212
+    {  229,  135,  -33,  -69,  171 }, // 213
+    {  230,  137,  -33,  -69,  173 }, // 214
+    {  231,  138,  -34,  -70,  175 }, // 215
+    {  232,  140,  -34,  -71,  177 }, // 216
+    {  233,  142,  -34,  -72,  179 }, // 217
+    {  235,  143,  -35,  -73,  181 }, // 218
+    {  236,  145,  -35,  -73,  183 }, // 219
+    {  237,  146,  -35,  -74,  185 }, // 220
+    {  238,  148,  -36,  -75,  187 }, // 221
+    {  239,  150,  -36,  -76,  189 }, // 222
+    {  240,  151,  -37,  -77,  191 }, // 223
+    {  242,  153,  -37,  -78,  193 }, // 224
+    {  243,  154,  -37,  -78,  195 }, // 225
+    {  244,  156,  -38,  -79,  197 }, // 226
+    {  245,  158,  -38,  -80,  199 }, // 227
+    {  246,  159,  -39,  -81,  201 }, // 228
+    {  247,  161,  -39,  -82,  203 }, // 229
+    {  249,  162,  -39,  -82,  205 }, // 230
+    {  250,  164,  -40,  -83,  207 }, // 231
+    {  251,  165,  -40,  -84,  209 }, // 232
+    {  252,  167,  -41,  -85,  211 }, // 233
+    {  253,  169,  -41,  -86,  213 }, // 234
+    {  254,  170,  -41,  -86,  215 }, // 235
+    {  256,  172,  -42,  -87,  217 }, // 236
+    {  257,  173,  -42,  -88,  219 }, // 237
+    {  258,  175,  -43,  -89,  221 }, // 238
+    {  259,  177,  -43,  -90,  223 }, // 239
+    {  260,  178,  -43,  -91,  226 }, // 240
+    {  261,  180,  -44,  -91,  228 }, // 241
+    {  263,  181,  -44,  -92,  230 }, // 242
+    {  264,  183,  -44,  -93,  232 }, // 243
+    {  265,  185,  -45,  -94,  234 }, // 244
+    {  266,  186,  -45,  -95,  236 }, // 245
+    {  267,  188,  -46,  -95,  238 }, // 246
+    {  268,  189,  -46,  -96,  240 }, // 247
+    {  270,  191,  -46,  -97,  242 }, // 248
+    {  271,  193,  -47,  -98,  244 }, // 249
+    {  272,  194,  -47,  -99,  246 }, // 250
+    {  273,  196,  -48,  -99,  248 }, // 251
+    {  274,  197,  -48, -100,  250 }, // 252
+    {  275,  199,  -48, -101,  252 }, // 253
+    {  277,  201,  -49, -102,  254 }, // 254
+    {  278,  202,  -49, -103,  256 }  // 255
+};
+
+#define YUYV_CONSTRAIN(v) ((v)<0)?0:(((v)>255)?255:(v))
+
+void IRAM_ATTR yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b)
+{
+    int16_t ri, gi, bi;
+
+    ri = yuv_table[y].vY + yuv_table[v].vVr;
+    gi = yuv_table[y].vY + yuv_table[u].vUg + yuv_table[v].vVg;
+    bi = yuv_table[y].vY + yuv_table[u].vUb;
+
+    *r = YUYV_CONSTRAIN(ri);
+    *g = YUYV_CONSTRAIN(gi);
+    *b = YUYV_CONSTRAIN(bi);
+}

+ 495 - 0
std/camera/driver/cam_hal.c

@@ -0,0 +1,495 @@
+// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+#include <string.h>
+#include "esp_heap_caps.h"
+#include "ll_cam.h"
+#include "cam_hal.h"
+
+#if (ESP_IDF_VERSION_MAJOR == 3) && (ESP_IDF_VERSION_MINOR == 3)
+#include "rom/ets_sys.h"
+#else
+#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
+static const uint16_t JPEG_EOI_MARKER = 0xD9FF;  // written in little-endian for esp32
+
+static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length)
+{
+    uint32_t sig = *((uint32_t *)inbuf) & 0xFFFFFF;
+    if(sig != JPEG_SOI_MARKER) {
+        for (uint32_t i = 0; i < length; i++) {
+            sig = *((uint32_t *)(&inbuf[i])) & 0xFFFFFF;
+            if (sig == JPEG_SOI_MARKER) {
+                ESP_LOGW(TAG, "SOI: %d", i);
+                return i;
+            }
+        }
+        ESP_LOGW(TAG, "NO-SOI");
+        return -1;
+    }
+    return 0;
+}
+
+static int cam_verify_jpeg_eoi(const uint8_t *inbuf, uint32_t length)
+{
+    int offset = -1;
+    uint8_t *dptr = (uint8_t *)inbuf + length - 2;
+    while (dptr > inbuf) {
+        uint16_t sig = *((uint16_t *)dptr);
+        if (JPEG_EOI_MARKER == sig) {
+            offset = dptr - inbuf;
+            //ESP_LOGW(TAG, "EOI: %d", length - (offset + 2));
+            return offset;
+        }
+        dptr--;
+    }
+    return -1;
+}
+
+static bool cam_get_next_frame(int * frame_pos)
+{
+    if(!cam_obj->frames[*frame_pos].en){
+        for (int x = 0; x < cam_obj->frame_cnt; x++) {
+            if (cam_obj->frames[x].en) {
+                *frame_pos = x;
+                return true;
+            }
+        }
+    } else {
+        return true;
+    }
+    return false;
+}
+
+static bool cam_start_frame(int * frame_pos)
+{
+    if (cam_get_next_frame(frame_pos)) {
+        if(ll_cam_start(cam_obj, *frame_pos)){
+            // Vsync the frame manually
+            ll_cam_do_vsync(cam_obj);
+            uint64_t us = (uint64_t)esp_timer_get_time();
+            cam_obj->frames[*frame_pos].fb.timestamp.tv_sec = us / 1000000UL;
+            cam_obj->frames[*frame_pos].fb.timestamp.tv_usec = us % 1000000UL;
+            return true;
+        }
+    }
+    return false;
+}
+
+void IRAM_ATTR ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType_t * HPTaskAwoken)
+{
+    if (xQueueSendFromISR(cam->event_queue, (void *)&cam_event, HPTaskAwoken) != pdTRUE) {
+        ll_cam_stop(cam);
+        cam->state = CAM_STATE_IDLE;
+        ESP_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"));
+    }
+}
+
+//Copy fram from DMA dma_buffer to fram dma_buffer
+static void cam_task(void *arg)
+{
+    int cnt = 0;
+    int frame_pos = 0;
+    cam_obj->state = CAM_STATE_IDLE;
+    cam_event_t cam_event = 0;
+    
+    xQueueReset(cam_obj->event_queue);
+
+    while (1) {
+        xQueueReceive(cam_obj->event_queue, (void *)&cam_event, portMAX_DELAY);
+        DBG_PIN_SET(1);
+        switch (cam_obj->state) {
+
+            case CAM_STATE_IDLE: {
+                if (cam_event == CAM_VSYNC_EVENT) {
+                    //DBG_PIN_SET(1);
+                    if(cam_start_frame(&frame_pos)){
+                        cam_obj->frames[frame_pos].fb.len = 0;
+                        cam_obj->state = CAM_STATE_READ_BUF;
+                    }
+                    cnt = 0;
+                }
+            }
+            break;
+
+            case CAM_STATE_READ_BUF: {
+                camera_fb_t * frame_buffer_event = &cam_obj->frames[frame_pos].fb;
+                size_t pixels_per_dma = (cam_obj->dma_half_buffer_size * cam_obj->fb_bytes_per_pixel) / (cam_obj->dma_bytes_per_item * cam_obj->in_bytes_per_pixel);
+                
+                if (cam_event == CAM_IN_SUC_EOF_EVENT) {
+                    if(!cam_obj->psram_mode){
+                        if (cam_obj->fb_size < (frame_buffer_event->len + pixels_per_dma)) {
+                            ESP_LOGW(TAG, "FB-OVF");
+                            ll_cam_stop(cam_obj);
+                            DBG_PIN_SET(0);
+                            continue;
+                        }
+                        frame_buffer_event->len += ll_cam_memcpy(cam_obj,
+                            &frame_buffer_event->buf[frame_buffer_event->len], 
+                            &cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size], 
+                            cam_obj->dma_half_buffer_size);
+                    }
+                    //Check for JPEG SOI in the first buffer. stop if not found
+                    if (cam_obj->jpeg_mode && cnt == 0 && cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len) != 0) {
+                        ll_cam_stop(cam_obj);
+                        cam_obj->state = CAM_STATE_IDLE;
+                    }
+                    cnt++;
+
+                } else if (cam_event == CAM_VSYNC_EVENT) {
+                    //DBG_PIN_SET(1);
+                    ll_cam_stop(cam_obj);
+
+                    if (cnt || !cam_obj->jpeg_mode || cam_obj->psram_mode) {
+                        if (cam_obj->jpeg_mode) {
+                            if (!cam_obj->psram_mode) {
+                                if (cam_obj->fb_size < (frame_buffer_event->len + pixels_per_dma)) {
+                                    ESP_LOGW(TAG, "FB-OVF");
+                                    cnt--;
+                                } else {
+                                    frame_buffer_event->len += ll_cam_memcpy(cam_obj,
+                                        &frame_buffer_event->buf[frame_buffer_event->len], 
+                                        &cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size], 
+                                        cam_obj->dma_half_buffer_size);
+                                }
+                            }
+                            cnt++;
+                        }
+
+                        cam_obj->frames[frame_pos].en = 0;
+
+                        if (cam_obj->psram_mode) {
+                            if (cam_obj->jpeg_mode) {
+                                frame_buffer_event->len = cnt * cam_obj->dma_half_buffer_size;
+                            } else {
+                                frame_buffer_event->len = cam_obj->recv_size;
+                            }
+                        } else if (!cam_obj->jpeg_mode) {
+                            if (frame_buffer_event->len != cam_obj->fb_size) {
+                                cam_obj->frames[frame_pos].en = 1;
+                                ESP_LOGE(TAG, "FB-SIZE: %u != %u", frame_buffer_event->len, cam_obj->fb_size);
+                            }
+                        }
+                        //send frame
+                        if(!cam_obj->frames[frame_pos].en && xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, 0) != pdTRUE) {
+                            //pop frame buffer from the queue
+                            camera_fb_t * fb2 = NULL;
+                            if(xQueueReceive(cam_obj->frame_buffer_queue, &fb2, 0) == pdTRUE) {
+                                //push the new frame to the end of the queue
+                                if (xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, 0) != pdTRUE) {
+                                    cam_obj->frames[frame_pos].en = 1;
+                                    ESP_LOGE(TAG, "FBQ-SND");
+                                }
+                                //free the popped buffer
+                                cam_give(fb2);
+                            } else {
+                                //queue is full and we could not pop a frame from it
+                                cam_obj->frames[frame_pos].en = 1;
+                                ESP_LOGE(TAG, "FBQ-RCV");
+                            }
+                        }
+                    }
+
+                    if(!cam_start_frame(&frame_pos)){
+                        cam_obj->state = CAM_STATE_IDLE;
+                    } else {
+                        cam_obj->frames[frame_pos].fb.len = 0;
+                    }
+                    cnt = 0;
+                }
+            }
+            break;
+        }
+        DBG_PIN_SET(0);
+    }
+}
+
+static lldesc_t * allocate_dma_descriptors(uint32_t count, uint16_t size, uint8_t * buffer)
+{
+    lldesc_t *dma = (lldesc_t *)heap_caps_malloc(count * sizeof(lldesc_t), MALLOC_CAP_DMA);
+    if (dma == NULL) {
+        return dma;
+    }
+
+    for (int x = 0; x < count; x++) {
+        dma[x].size = size;
+        dma[x].length = 0;
+        dma[x].sosf = 0;
+        dma[x].eof = 0;
+        dma[x].owner = 1;
+        dma[x].buf = (buffer + size * x);
+        dma[x].empty = (uint32_t)&dma[(x + 1) % count];
+    }
+    return dma;
+}
+
+static esp_err_t cam_dma_config(const camera_config_t *config)
+{
+    bool ret = ll_cam_dma_sizes(cam_obj);
+    if (0 == ret) {
+        return ESP_FAIL;
+    }
+
+    cam_obj->dma_node_cnt = (cam_obj->dma_buffer_size) / cam_obj->dma_node_buffer_size; // Number of DMA nodes
+    cam_obj->frame_copy_cnt = cam_obj->recv_size / cam_obj->dma_half_buffer_size; // Number of interrupted copies, ping-pong copy
+
+    ESP_LOGI(TAG, "buffer_size: %d, half_buffer_size: %d, node_buffer_size: %d, node_cnt: %d, total_cnt: %d", 
+             cam_obj->dma_buffer_size, cam_obj->dma_half_buffer_size, cam_obj->dma_node_buffer_size, cam_obj->dma_node_cnt, cam_obj->frame_copy_cnt);
+
+    cam_obj->dma_buffer = NULL;
+    cam_obj->dma = NULL;
+
+    cam_obj->frames = (cam_frame_t *)heap_caps_calloc(1, cam_obj->frame_cnt * sizeof(cam_frame_t), MALLOC_CAP_DEFAULT);
+    CAM_CHECK(cam_obj->frames != NULL, "frames malloc failed", ESP_FAIL);
+
+    uint8_t dma_align = 0;
+    size_t fb_size = cam_obj->fb_size;
+    if (cam_obj->psram_mode) {
+        dma_align = ll_cam_get_dma_align(cam_obj);
+        if (cam_obj->fb_size < cam_obj->recv_size) {
+            fb_size = cam_obj->recv_size;
+        }
+    }
+
+    /* Allocate memory for frame buffer */
+    size_t alloc_size = fb_size * sizeof(uint8_t) + dma_align;
+    uint32_t _caps = MALLOC_CAP_8BIT;
+    if (CAMERA_FB_IN_DRAM == config->fb_location) {
+        _caps |= MALLOC_CAP_INTERNAL;
+    } else {
+        _caps |= MALLOC_CAP_SPIRAM;
+    }
+    for (int x = 0; x < cam_obj->frame_cnt; x++) {
+        cam_obj->frames[x].dma = NULL;
+        cam_obj->frames[x].fb_offset = 0;
+        cam_obj->frames[x].en = 0;
+        ESP_LOGI(TAG, "Allocating %d Byte frame buffer in %s", alloc_size, _caps & MALLOC_CAP_SPIRAM ? "PSRAM" : "OnBoard RAM");
+        cam_obj->frames[x].fb.buf = (uint8_t *)heap_caps_malloc(alloc_size, _caps);
+        CAM_CHECK(cam_obj->frames[x].fb.buf != NULL, "frame buffer malloc failed", ESP_FAIL);
+        if (cam_obj->psram_mode) {
+            //align PSRAM buffer. TODO: save the offset so proper address can be freed later
+            cam_obj->frames[x].fb_offset = dma_align - ((uint32_t)cam_obj->frames[x].fb.buf & (dma_align - 1));
+            cam_obj->frames[x].fb.buf += cam_obj->frames[x].fb_offset;
+            ESP_LOGI(TAG, "Frame[%d]: Offset: %u, Addr: 0x%08X", x, cam_obj->frames[x].fb_offset, (uint32_t)cam_obj->frames[x].fb.buf);
+            cam_obj->frames[x].dma = allocate_dma_descriptors(cam_obj->dma_node_cnt, cam_obj->dma_node_buffer_size, cam_obj->frames[x].fb.buf);
+            CAM_CHECK(cam_obj->frames[x].dma != NULL, "frame dma malloc failed", ESP_FAIL);
+        }
+        cam_obj->frames[x].en = 1;
+    }
+
+    if (!cam_obj->psram_mode) {
+        cam_obj->dma_buffer = (uint8_t *)heap_caps_malloc(cam_obj->dma_buffer_size * sizeof(uint8_t), MALLOC_CAP_DMA);
+        if(NULL == cam_obj->dma_buffer) {
+            ESP_LOGE(TAG,"%s(%d): DMA buffer %d Byte malloc failed, the current largest free block:%d Byte", __FUNCTION__, __LINE__, 
+                     cam_obj->dma_buffer_size, heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
+            return ESP_FAIL;
+        }
+
+        cam_obj->dma = allocate_dma_descriptors(cam_obj->dma_node_cnt, cam_obj->dma_node_buffer_size, cam_obj->dma_buffer);
+        CAM_CHECK(cam_obj->dma != NULL, "dma malloc failed", ESP_FAIL);
+    }
+
+    return ESP_OK;
+}
+
+esp_err_t cam_init(const camera_config_t *config)
+{
+    CAM_CHECK(NULL != config, "config pointer is invalid", ESP_ERR_INVALID_ARG);
+
+    esp_err_t ret = ESP_OK;
+    cam_obj = (cam_obj_t *)heap_caps_calloc(1, sizeof(cam_obj_t), MALLOC_CAP_DMA);
+    CAM_CHECK(NULL != cam_obj, "lcd_cam object malloc error", ESP_ERR_NO_MEM);
+
+    cam_obj->swap_data = 0;
+    cam_obj->vsync_pin = config->pin_vsync;
+    cam_obj->vsync_invert = true;
+
+    ll_cam_set_pin(cam_obj, config);
+    ret = ll_cam_config(cam_obj, config);
+    CAM_CHECK_GOTO(ret == ESP_OK, "ll_cam initialize failed", err);
+
+#if CAMERA_DBG_PIN_ENABLE
+    PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[DBG_PIN_NUM], PIN_FUNC_GPIO);
+    gpio_set_direction(DBG_PIN_NUM, GPIO_MODE_OUTPUT);
+    gpio_set_pull_mode(DBG_PIN_NUM, GPIO_FLOATING);
+#endif
+
+    ESP_LOGI(TAG, "cam init ok");
+    return ESP_OK;
+
+err:
+    free(cam_obj);
+    cam_obj = NULL;
+    return ESP_FAIL;
+}
+
+esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint16_t sensor_pid)
+{
+    CAM_CHECK(NULL != config, "config pointer is invalid", ESP_ERR_INVALID_ARG);
+    esp_err_t ret = ESP_OK;
+
+    ret = ll_cam_set_sample_mode(cam_obj, (pixformat_t)config->pixel_format, config->xclk_freq_hz, sensor_pid);
+
+    cam_obj->jpeg_mode = config->pixel_format == PIXFORMAT_JPEG;
+#if CONFIG_IDF_TARGET_ESP32
+    cam_obj->psram_mode = false;
+#else
+    cam_obj->psram_mode = (config->xclk_freq_hz == 16000000);
+#endif
+    cam_obj->frame_cnt = config->fb_count;
+    cam_obj->width = resolution[frame_size].width;
+    cam_obj->height = resolution[frame_size].height;
+
+    if(cam_obj->jpeg_mode){
+        cam_obj->recv_size = cam_obj->width * cam_obj->height / 5;
+        cam_obj->fb_size = cam_obj->recv_size;
+    } else {
+        cam_obj->recv_size = cam_obj->width * cam_obj->height * cam_obj->in_bytes_per_pixel;
+        cam_obj->fb_size = cam_obj->width * cam_obj->height * cam_obj->fb_bytes_per_pixel;
+    }
+    
+    ret = cam_dma_config(config);
+    CAM_CHECK_GOTO(ret == ESP_OK, "cam_dma_config failed", err);
+
+    cam_obj->event_queue = xQueueCreate(cam_obj->dma_half_buffer_cnt - 1, sizeof(cam_event_t));
+    CAM_CHECK_GOTO(cam_obj->event_queue != NULL, "event_queue create failed", err);
+
+    size_t frame_buffer_queue_len = cam_obj->frame_cnt;
+    if (config->grab_mode == CAMERA_GRAB_LATEST && cam_obj->frame_cnt > 1) {
+        frame_buffer_queue_len = cam_obj->frame_cnt - 1;
+    }
+    cam_obj->frame_buffer_queue = xQueueCreate(frame_buffer_queue_len, sizeof(camera_fb_t*));
+    CAM_CHECK_GOTO(cam_obj->frame_buffer_queue != NULL, "frame_buffer_queue create failed", err);
+
+    ret = ll_cam_init_isr(cam_obj);
+    CAM_CHECK_GOTO(ret == ESP_OK, "cam intr alloc failed", err);
+
+    
+#if CONFIG_CAMERA_CORE0
+    xTaskCreatePinnedToCore(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 0);
+#elif CONFIG_CAMERA_CORE1
+    xTaskCreatePinnedToCore(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 1);
+#else
+    xTaskCreate(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle);
+#endif
+
+    ESP_LOGI(TAG, "cam config ok");
+    return ESP_OK;
+
+err:
+    cam_deinit();
+    return ESP_FAIL;
+}
+
+esp_err_t cam_deinit(void)
+{
+    if (!cam_obj) {
+        return ESP_FAIL;
+    }
+
+    cam_stop();
+    if (cam_obj->task_handle) {
+        vTaskDelete(cam_obj->task_handle);
+    }
+    if (cam_obj->event_queue) {
+        vQueueDelete(cam_obj->event_queue);
+    }
+    if (cam_obj->frame_buffer_queue) {
+        vQueueDelete(cam_obj->frame_buffer_queue);
+    }
+    if (cam_obj->dma) {
+        free(cam_obj->dma);
+    }
+    if (cam_obj->dma_buffer) {
+        free(cam_obj->dma_buffer);
+    }
+    if (cam_obj->frames) {
+        for (int x = 0; x < cam_obj->frame_cnt; x++) {
+            free(cam_obj->frames[x].fb.buf - cam_obj->frames[x].fb_offset);
+            if (cam_obj->frames[x].dma) {
+                free(cam_obj->frames[x].dma);
+            }
+        }
+        free(cam_obj->frames);
+    }
+
+    ll_cam_deinit(cam_obj);
+
+    free(cam_obj);
+    cam_obj = NULL;
+    return ESP_OK;
+}
+
+void cam_stop(void)
+{
+    ll_cam_vsync_intr_enable(cam_obj, false);
+    ll_cam_stop(cam_obj);
+}
+
+void cam_start(void)
+{
+    ll_cam_vsync_intr_enable(cam_obj, true);
+}
+
+camera_fb_t *cam_take(TickType_t timeout)
+{
+    camera_fb_t *dma_buffer = NULL;
+    TickType_t start = xTaskGetTickCount();
+    xQueueReceive(cam_obj->frame_buffer_queue, (void *)&dma_buffer, timeout);
+    if (dma_buffer) {
+        if(cam_obj->jpeg_mode){
+            // find the end marker for JPEG. Data after that can be discarded
+            int offset_e = cam_verify_jpeg_eoi(dma_buffer->buf, dma_buffer->len);
+            if (offset_e >= 0) {
+                // adjust buffer length
+                dma_buffer->len = offset_e + sizeof(JPEG_EOI_MARKER);
+                return dma_buffer;
+            } else {
+                ESP_LOGW(TAG, "NO-EOI");
+                cam_give(dma_buffer);
+                return cam_take(timeout - (xTaskGetTickCount() - start));//recurse!!!!
+            }
+        } else if(cam_obj->psram_mode && cam_obj->in_bytes_per_pixel != cam_obj->fb_bytes_per_pixel){
+            //currently this is used only for YUV to GRAYSCALE
+            dma_buffer->len = ll_cam_memcpy(cam_obj, dma_buffer->buf, dma_buffer->buf, dma_buffer->len);
+        }
+        return dma_buffer;
+    } else {
+        ESP_LOGW(TAG, "Failed to get the frame on time!");
+    }
+    return NULL;
+}
+
+void cam_give(camera_fb_t *dma_buffer)
+{
+    for (int x = 0; x < cam_obj->frame_cnt; x++) {
+        if (&cam_obj->frames[x].fb == dma_buffer) {
+            cam_obj->frames[x].en = 1;
+            break;
+        }
+    }
+}

+ 418 - 0
std/camera/driver/esp_camera.c

@@ -0,0 +1,418 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "time.h"
+#include "sys/time.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/gpio.h"
+#include "esp_system.h"
+#include "nvs_flash.h"
+#include "nvs.h"
+#include "sensor.h"
+#include "sccb.h"
+#include "cam_hal.h"
+#include "esp_camera.h"
+#include "xclk.h"
+
+#if CONFIG_OV2640_SUPPORT
+#include "ov2640.h"
+#endif
+#if CONFIG_OV7725_SUPPORT
+#include "ov7725.h"
+#endif
+#if CONFIG_OV3660_SUPPORT
+#include "ov3660.h"
+#endif
+#if CONFIG_OV5640_SUPPORT
+#include "ov5640.h"
+#endif
+#if CONFIG_NT99141_SUPPORT
+#include "nt99141.h"
+#endif
+#if CONFIG_OV7670_SUPPORT
+#include "ov7670.h"
+#endif
+#if CONFIG_GC2145_SUPPORT
+#include "gc2145.h"
+#endif
+#if CONFIG_GC032A_SUPPORT
+#include "gc032a.h"
+#endif
+#if CONFIG_GC0308_SUPPORT
+#include "gc0308.h"
+#endif
+#if CONFIG_BF3005_SUPPORT
+#include "bf3005.h"
+#endif
+#if CONFIG_BF20A6_SUPPORT
+#include "bf20a6.h"
+#endif
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#define TAG ""
+#else
+#include "esp_log.h"
+static const char *TAG = "camera";
+#endif
+
+typedef struct {
+    sensor_t sensor;
+    camera_fb_t fb;
+} camera_state_t;
+
+static const char *CAMERA_SENSOR_NVS_KEY = "sensor";
+static const char *CAMERA_PIXFORMAT_NVS_KEY = "pixformat";
+static camera_state_t *s_state = NULL;
+
+#if CONFIG_IDF_TARGET_ESP32S3 // LCD_CAM module of ESP32-S3 will generate xclk
+#define CAMERA_ENABLE_OUT_CLOCK(v)
+#define CAMERA_DISABLE_OUT_CLOCK()
+#else
+#define CAMERA_ENABLE_OUT_CLOCK(v) camera_enable_out_clock((v))
+#define CAMERA_DISABLE_OUT_CLOCK() camera_disable_out_clock()
+#endif
+
+typedef struct {
+    int (*detect)(int slv_addr, sensor_id_t *id);
+    int (*init)(sensor_t *sensor);
+} sensor_func_t;
+
+static const sensor_func_t g_sensors[] = {
+#if CONFIG_OV7725_SUPPORT
+    {ov7725_detect, ov7725_init},
+#endif
+#if CONFIG_OV7670_SUPPORT
+    {ov7670_detect, ov7670_init},
+#endif
+#if CONFIG_OV2640_SUPPORT
+    {ov2640_detect, ov2640_init},
+#endif
+#if CONFIG_OV3660_SUPPORT
+    {ov3660_detect, ov3660_init},
+#endif
+#if CONFIG_OV5640_SUPPORT
+    {ov5640_detect, ov5640_init},
+#endif
+#if CONFIG_NT99141_SUPPORT
+    {nt99141_detect, nt99141_init},
+#endif
+#if CONFIG_GC2145_SUPPORT
+    {gc2145_detect, gc2145_init},
+#endif
+#if CONFIG_GC032A_SUPPORT
+    {gc032a_detect, gc032a_init},
+#endif
+#if CONFIG_GC0308_SUPPORT
+    {gc0308_detect, gc0308_init},
+#endif
+#if CONFIG_BF3005_SUPPORT
+    {bf3005_detect, bf3005_init},
+#endif
+#if CONFIG_BF20A6_SUPPORT
+    {bf20a6_detect, bf20a6_init},
+#endif
+};
+
+static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
+{
+    *out_camera_model = CAMERA_NONE;
+    if (s_state != NULL) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    s_state = (camera_state_t *) calloc(sizeof(camera_state_t), 1);
+    if (!s_state) {
+        return ESP_ERR_NO_MEM;
+    }
+
+    if (config->pin_xclk >= 0) {
+        ESP_LOGD(TAG, "Enabling XCLK output");
+        CAMERA_ENABLE_OUT_CLOCK(config);
+    }
+
+    if (config->pin_sscb_sda != -1) {
+        ESP_LOGD(TAG, "Initializing SSCB");
+        SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl);
+    }
+
+    if (config->pin_pwdn >= 0) {
+        ESP_LOGD(TAG, "Resetting camera by power down line");
+        gpio_config_t conf = { 0 };
+        conf.pin_bit_mask = 1LL << config->pin_pwdn;
+        conf.mode = GPIO_MODE_OUTPUT;
+        gpio_config(&conf);
+
+        // carefull, logic is inverted compared to reset pin
+        gpio_set_level(config->pin_pwdn, 1);
+        vTaskDelay(10 / portTICK_PERIOD_MS);
+        gpio_set_level(config->pin_pwdn, 0);
+        vTaskDelay(10 / portTICK_PERIOD_MS);
+    }
+
+    if (config->pin_reset >= 0) {
+        ESP_LOGD(TAG, "Resetting camera");
+        gpio_config_t conf = { 0 };
+        conf.pin_bit_mask = 1LL << config->pin_reset;
+        conf.mode = GPIO_MODE_OUTPUT;
+        gpio_config(&conf);
+
+        gpio_set_level(config->pin_reset, 0);
+        vTaskDelay(10 / portTICK_PERIOD_MS);
+        gpio_set_level(config->pin_reset, 1);
+        vTaskDelay(10 / portTICK_PERIOD_MS);
+    }
+
+    #if CONFIG_OV2640_SUPPORT
+        SCCB_Write(0x30, 0xff, 0x01);
+    #endif
+
+    ESP_LOGD(TAG, "Searching for camera address");
+    vTaskDelay(10 / portTICK_PERIOD_MS);
+
+    uint8_t slv_addr = SCCB_Probe();
+
+    if (slv_addr == 0) {
+        CAMERA_DISABLE_OUT_CLOCK();
+        return ESP_ERR_NOT_FOUND;
+    }
+
+    ESP_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr);
+    s_state->sensor.slv_addr = slv_addr;
+    s_state->sensor.xclk_freq_hz = config->xclk_freq_hz;
+
+    /**
+     * Read sensor ID and then initialize sensor
+     * Attention: Some sensors have the same SCCB address. Therefore, several attempts may be made in the detection process
+     */
+    sensor_id_t *id = &s_state->sensor.id;
+    for (size_t i = 0; i < sizeof(g_sensors) / sizeof(sensor_func_t); i++) {
+        if (g_sensors[i].detect(slv_addr, id)) {
+            camera_sensor_info_t *info = esp_camera_sensor_get_info(id);
+            if (NULL != info) {
+                *out_camera_model = info->model;
+                ESP_LOGI(TAG, "Detected %s camera", info->name);
+                g_sensors[i].init(&s_state->sensor);
+                break;
+            }
+        }
+    }
+
+    if (CAMERA_NONE == *out_camera_model) { //If no supported sensors are detected
+        CAMERA_DISABLE_OUT_CLOCK();
+        ESP_LOGE(TAG, "Detected camera not supported.");
+        return ESP_ERR_NOT_SUPPORTED;
+    }
+
+    ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x",
+             id->PID, id->VER, id->MIDH, id->MIDL);
+
+    ESP_LOGD(TAG, "Doing SW reset of sensor");
+    vTaskDelay(10 / portTICK_PERIOD_MS);
+    s_state->sensor.reset(&s_state->sensor);
+
+    return ESP_OK;
+}
+
+esp_err_t esp_camera_init(const camera_config_t *config)
+{
+    esp_err_t err;
+    err = cam_init(config);
+    if (err != ESP_OK) {
+        ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
+        return err;
+    }
+
+    camera_model_t camera_model = CAMERA_NONE;
+    err = camera_probe(config, &camera_model);
+    if (err != ESP_OK) {
+        ESP_LOGE(TAG, "Camera probe failed with error 0x%x(%s)", err, esp_err_to_name(err));
+        goto fail;
+    }
+
+    framesize_t frame_size = (framesize_t) config->frame_size;
+    pixformat_t pix_format = (pixformat_t) config->pixel_format;
+
+    if (PIXFORMAT_JPEG == pix_format && (!camera_sensor[camera_model].support_jpeg)) {
+        ESP_LOGE(TAG, "JPEG format is not supported on this sensor");
+        err = ESP_ERR_NOT_SUPPORTED;
+        goto fail;
+    }
+
+    if (frame_size > camera_sensor[camera_model].max_size) {
+        ESP_LOGW(TAG, "The frame size exceeds the maximum for this sensor, it will be forced to the maximum possible value");
+        frame_size = camera_sensor[camera_model].max_size;
+    }
+
+    err = cam_config(config, frame_size, s_state->sensor.id.PID);
+    if (err != ESP_OK) {
+        ESP_LOGE(TAG, "Camera config failed with error 0x%x", err);
+        goto fail;
+    }
+
+    s_state->sensor.status.framesize = frame_size;
+    s_state->sensor.pixformat = pix_format;
+    ESP_LOGD(TAG, "Setting frame size to %dx%d", resolution[frame_size].width, resolution[frame_size].height);
+    if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
+        ESP_LOGE(TAG, "Failed to set frame size");
+        err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE;
+        goto fail;
+    }
+    s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
+
+    if (s_state->sensor.id.PID == OV2640_PID) {
+        s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);
+        s_state->sensor.set_bpc(&s_state->sensor, false);
+        s_state->sensor.set_wpc(&s_state->sensor, true);
+        s_state->sensor.set_lenc(&s_state->sensor, true);
+    }
+
+    if (pix_format == PIXFORMAT_JPEG) {
+        s_state->sensor.set_quality(&s_state->sensor, config->jpeg_quality);
+    }
+    s_state->sensor.init_status(&s_state->sensor);
+
+    cam_start();
+
+    return ESP_OK;
+
+fail:
+    esp_camera_deinit();
+    return err;
+}
+
+esp_err_t esp_camera_deinit()
+{
+    esp_err_t ret = cam_deinit();
+    CAMERA_DISABLE_OUT_CLOCK();
+    if (s_state) {
+        SCCB_Deinit();
+
+        free(s_state);
+        s_state = NULL;
+    }
+
+    return ret;
+}
+
+#define FB_GET_TIMEOUT (4000 / portTICK_PERIOD_MS)
+
+camera_fb_t *esp_camera_fb_get()
+{
+    if (s_state == NULL) {
+        return NULL;
+    }
+    camera_fb_t *fb = cam_take(FB_GET_TIMEOUT);
+    //set the frame properties
+    if (fb) {
+        fb->width = resolution[s_state->sensor.status.framesize].width;
+        fb->height = resolution[s_state->sensor.status.framesize].height;
+        fb->format = s_state->sensor.pixformat;
+    }
+    return fb;
+}
+
+void esp_camera_fb_return(camera_fb_t *fb)
+{
+    if (s_state == NULL) {
+        return;
+    }
+    cam_give(fb);
+}
+
+sensor_t *esp_camera_sensor_get()
+{
+    if (s_state == NULL) {
+        return NULL;
+    }
+    return &s_state->sensor;
+}
+
+esp_err_t esp_camera_save_to_nvs(const char *key)
+{
+#if ESP_IDF_VERSION_MAJOR > 3
+    nvs_handle_t handle;
+#else
+    nvs_handle handle;
+#endif
+    esp_err_t ret = nvs_open(key, NVS_READWRITE, &handle);
+
+    if (ret == ESP_OK) {
+        sensor_t *s = esp_camera_sensor_get();
+        if (s != NULL) {
+            ret = nvs_set_blob(handle, CAMERA_SENSOR_NVS_KEY, &s->status, sizeof(camera_status_t));
+            if (ret == ESP_OK) {
+                uint8_t pf = s->pixformat;
+                ret = nvs_set_u8(handle, CAMERA_PIXFORMAT_NVS_KEY, pf);
+            }
+            return ret;
+        } else {
+            return ESP_ERR_CAMERA_NOT_DETECTED;
+        }
+        nvs_close(handle);
+        return ret;
+    } else {
+        return ret;
+    }
+}
+
+esp_err_t esp_camera_load_from_nvs(const char *key)
+{
+#if ESP_IDF_VERSION_MAJOR > 3
+    nvs_handle_t handle;
+#else
+    nvs_handle handle;
+#endif
+    uint8_t pf;
+
+    esp_err_t ret = nvs_open(key, NVS_READWRITE, &handle);
+
+    if (ret == ESP_OK) {
+        sensor_t *s = esp_camera_sensor_get();
+        camera_status_t st;
+        if (s != NULL) {
+            size_t size = sizeof(camera_status_t);
+            ret = nvs_get_blob(handle, CAMERA_SENSOR_NVS_KEY, &st, &size);
+            if (ret == ESP_OK) {
+                s->set_ae_level(s, st.ae_level);
+                s->set_aec2(s, st.aec2);
+                s->set_aec_value(s, st.aec_value);
+                s->set_agc_gain(s, st.agc_gain);
+                s->set_awb_gain(s, st.awb_gain);
+                s->set_bpc(s, st.bpc);
+                s->set_brightness(s, st.brightness);
+                s->set_colorbar(s, st.colorbar);
+                s->set_contrast(s, st.contrast);
+                s->set_dcw(s, st.dcw);
+                s->set_denoise(s, st.denoise);
+                s->set_exposure_ctrl(s, st.aec);
+                s->set_framesize(s, st.framesize);
+                s->set_gain_ctrl(s, st.agc);
+                s->set_gainceiling(s, st.gainceiling);
+                s->set_hmirror(s, st.hmirror);
+                s->set_lenc(s, st.lenc);
+                s->set_quality(s, st.quality);
+                s->set_raw_gma(s, st.raw_gma);
+                s->set_saturation(s, st.saturation);
+                s->set_sharpness(s, st.sharpness);
+                s->set_special_effect(s, st.special_effect);
+                s->set_vflip(s, st.vflip);
+                s->set_wb_mode(s, st.wb_mode);
+                s->set_whitebal(s, st.awb);
+                s->set_wpc(s, st.wpc);
+            }
+            ret = nvs_get_u8(handle, CAMERA_PIXFORMAT_NVS_KEY, &pf);
+            if (ret == ESP_OK) {
+                s->set_pixformat(s, pf);
+            }
+        } else {
+            return ESP_ERR_CAMERA_NOT_DETECTED;
+        }
+        nvs_close(handle);
+        return ret;
+    } else {
+        ESP_LOGW(TAG, "Error (%d) opening nvs key \"%s\"", ret, key);
+        return ret;
+    }
+}

+ 219 - 0
std/camera/driver/include/esp_camera.h

@@ -0,0 +1,219 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+/*
+ * Example Use
+ *
+    static camera_config_t camera_example_config = {
+        .pin_pwdn       = PIN_PWDN,
+        .pin_reset      = PIN_RESET,
+        .pin_xclk       = PIN_XCLK,
+        .pin_sscb_sda   = PIN_SIOD,
+        .pin_sscb_scl   = PIN_SIOC,
+        .pin_d7         = PIN_D7,
+        .pin_d6         = PIN_D6,
+        .pin_d5         = PIN_D5,
+        .pin_d4         = PIN_D4,
+        .pin_d3         = PIN_D3,
+        .pin_d2         = PIN_D2,
+        .pin_d1         = PIN_D1,
+        .pin_d0         = PIN_D0,
+        .pin_vsync      = PIN_VSYNC,
+        .pin_href       = PIN_HREF,
+        .pin_pclk       = PIN_PCLK,
+
+        .xclk_freq_hz   = 20000000,
+        .ledc_timer     = LEDC_TIMER_0,
+        .ledc_channel   = LEDC_CHANNEL_0,
+        .pixel_format   = PIXFORMAT_JPEG,
+        .frame_size     = FRAMESIZE_SVGA,
+        .jpeg_quality   = 10,
+        .fb_count       = 2,
+        .grab_mode      = CAMERA_GRAB_WHEN_EMPTY
+    };
+
+    esp_err_t camera_example_init(){
+        return esp_camera_init(&camera_example_config);
+    }
+
+    esp_err_t camera_example_capture(){
+        //capture a frame
+        camera_fb_t * fb = esp_camera_fb_get();
+        if (!fb) {
+            ESP_LOGE(TAG, "Frame buffer could not be acquired");
+            return ESP_FAIL;
+        }
+
+        //replace this with your own function
+        display_image(fb->width, fb->height, fb->pixformat, fb->buf, fb->len);
+
+        //return the frame buffer back to be reused
+        esp_camera_fb_return(fb);
+
+        return ESP_OK;
+    }
+*/
+
+#pragma once
+
+#include "esp_err.h"
+#include "driver/ledc.h"
+#include "sensor.h"
+#include "sys/time.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LEDC_TIMER_(NUM)                 LEDC_TIMER_##NUM
+#define LEDC_TIMER(NUM)                 LEDC_TIMER_(NUM)
+#define LEDC_CHANNEL_(NUM)               LEDC_CHANNEL_##NUM
+#define LEDC_CHANNEL(NUM)               LEDC_CHANNEL_(NUM)
+
+/**
+ * @brief Configuration structure for camera initialization
+ */
+typedef enum {
+    CAMERA_GRAB_WHEN_EMPTY,         /*!< Fills buffers when they are empty. Less resources but first 'fb_count' frames might be old */
+    CAMERA_GRAB_LATEST              /*!< Except when 1 frame buffer is used, queue will always contain the last 'fb_count' frames */
+} camera_grab_mode_t;
+
+/**
+ * @brief Camera frame buffer location 
+ */
+typedef enum {
+    CAMERA_FB_IN_PSRAM,         /*!< Frame buffer is placed in external PSRAM */
+    CAMERA_FB_IN_DRAM           /*!< Frame buffer is placed in internal DRAM */
+} camera_fb_location_t;
+
+/**
+ * @brief Configuration structure for camera initialization
+ */
+typedef struct {
+    int pin_pwdn;                   /*!< GPIO pin for camera power down line */
+    int pin_reset;                  /*!< GPIO pin for camera reset line */
+    int pin_xclk;                   /*!< GPIO pin for camera XCLK line */
+    int pin_sscb_sda;               /*!< GPIO pin for camera SDA line */
+    int pin_sscb_scl;               /*!< GPIO pin for camera SCL line */
+    int pin_d7;                     /*!< GPIO pin for camera D7 line */
+    int pin_d6;                     /*!< GPIO pin for camera D6 line */
+    int pin_d5;                     /*!< GPIO pin for camera D5 line */
+    int pin_d4;                     /*!< GPIO pin for camera D4 line */
+    int pin_d3;                     /*!< GPIO pin for camera D3 line */
+    int pin_d2;                     /*!< GPIO pin for camera D2 line */
+    int pin_d1;                     /*!< GPIO pin for camera D1 line */
+    int pin_d0;                     /*!< GPIO pin for camera D0 line */
+    int pin_vsync;                  /*!< GPIO pin for camera VSYNC line */
+    int pin_href;                   /*!< GPIO pin for camera HREF line */
+    int pin_pclk;                   /*!< GPIO pin for camera PCLK line */
+
+    int xclk_freq_hz;               /*!< Frequency of XCLK signal, in Hz. EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode */
+
+    ledc_timer_t ledc_timer;        /*!< LEDC timer to be used for generating XCLK  */
+    ledc_channel_t ledc_channel;    /*!< LEDC channel to be used for generating XCLK  */
+
+    pixformat_t pixel_format;       /*!< Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG  */
+    framesize_t frame_size;         /*!< Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA  */
+
+    int jpeg_quality;               /*!< Quality of JPEG output. 0-63 lower means higher quality  */
+    size_t fb_count;                /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed)  */
+    camera_fb_location_t fb_location; /*!< The location where the frame buffer will be allocated */
+    camera_grab_mode_t grab_mode;   /*!< When buffers should be filled */
+} camera_config_t;
+
+/**
+ * @brief Data structure of camera frame buffer
+ */
+typedef struct {
+    uint8_t * buf;              /*!< Pointer to the pixel data */
+    size_t len;                 /*!< Length of the buffer in bytes */
+    size_t width;               /*!< Width of the buffer in pixels */
+    size_t height;              /*!< Height of the buffer in pixels */
+    pixformat_t format;         /*!< Format of the pixel data */
+    struct timeval timestamp;   /*!< Timestamp since boot of the first DMA buffer of the frame */
+} camera_fb_t;
+
+#define ESP_ERR_CAMERA_BASE 0x20000
+#define ESP_ERR_CAMERA_NOT_DETECTED             (ESP_ERR_CAMERA_BASE + 1)
+#define ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE (ESP_ERR_CAMERA_BASE + 2)
+#define ESP_ERR_CAMERA_FAILED_TO_SET_OUT_FORMAT (ESP_ERR_CAMERA_BASE + 3)
+#define ESP_ERR_CAMERA_NOT_SUPPORTED            (ESP_ERR_CAMERA_BASE + 4)
+
+/**
+ * @brief Initialize the camera driver
+ *
+ * @note call camera_probe before calling this function
+ *
+ * This function detects and configures camera over I2C interface,
+ * allocates framebuffer and DMA buffers,
+ * initializes parallel I2S input, and sets up DMA descriptors.
+ *
+ * Currently this function can only be called once and there is
+ * no way to de-initialize this module.
+ *
+ * @param config  Camera configuration parameters
+ *
+ * @return ESP_OK on success
+ */
+esp_err_t esp_camera_init(const camera_config_t* config);
+
+/**
+ * @brief Deinitialize the camera driver
+ *
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_STATE if the driver hasn't been initialized yet
+ */
+esp_err_t esp_camera_deinit();
+
+/**
+ * @brief Obtain pointer to a frame buffer.
+ *
+ * @return pointer to the frame buffer
+ */
+camera_fb_t* esp_camera_fb_get();
+
+/**
+ * @brief Return the frame buffer to be reused again.
+ *
+ * @param fb    Pointer to the frame buffer
+ */
+void esp_camera_fb_return(camera_fb_t * fb);
+
+/**
+ * @brief Get a pointer to the image sensor control structure
+ *
+ * @return pointer to the sensor
+ */
+sensor_t * esp_camera_sensor_get();
+
+/**
+ * @brief Save camera settings to non-volatile-storage (NVS)
+ * 
+ * @param key   A unique nvs key name for the camera settings 
+ */
+esp_err_t esp_camera_save_to_nvs(const char *key);
+
+/**
+ * @brief Load camera settings from non-volatile-storage (NVS)
+ * 
+ * @param key   A unique nvs key name for the camera settings 
+ */
+esp_err_t esp_camera_load_from_nvs(const char *key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#include "img_converters.h"
+

+ 251 - 0
std/camera/driver/include/sensor.h

@@ -0,0 +1,251 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * Sensor abstraction layer.
+ *
+ */
+#ifndef __SENSOR_H__
+#define __SENSOR_H__
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    OV9650_PID = 0x96,
+    OV7725_PID = 0x77,
+    OV2640_PID = 0x26,
+    OV3660_PID = 0x3660,
+    OV5640_PID = 0x5640,
+    OV7670_PID = 0x76,
+    NT99141_PID = 0x1410,
+    GC2145_PID = 0x2145,
+    GC032A_PID = 0x232a,
+    GC0308_PID = 0x9b,
+    BF3005_PID = 0x30,
+    BF20A6_PID = 0x20a6,
+} camera_pid_t;
+
+typedef enum {
+    CAMERA_OV7725,
+    CAMERA_OV2640,
+    CAMERA_OV3660,
+    CAMERA_OV5640,
+    CAMERA_OV7670,
+    CAMERA_NT99141,
+    CAMERA_GC2145,
+    CAMERA_GC032A,
+    CAMERA_GC0308,
+    CAMERA_BF3005,
+    CAMERA_BF20A6,
+    CAMERA_MODEL_MAX,
+    CAMERA_NONE,
+} camera_model_t;
+
+typedef enum {
+    OV2640_SCCB_ADDR   = 0x30,// 0x60 >> 1
+    OV5640_SCCB_ADDR   = 0x3C,// 0x78 >> 1
+    OV3660_SCCB_ADDR   = 0x3C,// 0x78 >> 1
+    OV7725_SCCB_ADDR   = 0x21,// 0x42 >> 1
+    OV7670_SCCB_ADDR   = 0x21,// 0x42 >> 1
+    NT99141_SCCB_ADDR  = 0x2A,// 0x54 >> 1
+    GC2145_SCCB_ADDR   = 0x3C,// 0x78 >> 1
+    GC032A_SCCB_ADDR   = 0x21,// 0x42 >> 1
+    GC0308_SCCB_ADDR   = 0x21,// 0x42 >> 1
+    BF3005_SCCB_ADDR   = 0x6E,
+    BF20A6_SCCB_ADDR   = 0x6E,
+} camera_sccb_addr_t;
+
+typedef enum {
+    PIXFORMAT_RGB565,    // 2BPP/RGB565
+    PIXFORMAT_YUV422,    // 2BPP/YUV422
+    PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
+    PIXFORMAT_JPEG,      // JPEG/COMPRESSED
+    PIXFORMAT_RGB888,    // 3BPP/RGB888
+    PIXFORMAT_RAW,       // RAW
+    PIXFORMAT_RGB444,    // 3BP2P/RGB444
+    PIXFORMAT_RGB555,    // 3BP2P/RGB555
+} pixformat_t;
+
+typedef enum {
+    FRAMESIZE_96X96,    // 96x96
+    FRAMESIZE_QQVGA,    // 160x120
+    FRAMESIZE_QCIF,     // 176x144
+    FRAMESIZE_HQVGA,    // 240x176
+    FRAMESIZE_240X240,  // 240x240
+    FRAMESIZE_QVGA,     // 320x240
+    FRAMESIZE_CIF,      // 400x296
+    FRAMESIZE_HVGA,     // 480x320
+    FRAMESIZE_VGA,      // 640x480
+    FRAMESIZE_SVGA,     // 800x600
+    FRAMESIZE_XGA,      // 1024x768
+    FRAMESIZE_HD,       // 1280x720
+    FRAMESIZE_SXGA,     // 1280x1024
+    FRAMESIZE_UXGA,     // 1600x1200
+    // 3MP Sensors
+    FRAMESIZE_FHD,      // 1920x1080
+    FRAMESIZE_P_HD,     //  720x1280
+    FRAMESIZE_P_3MP,    //  864x1536
+    FRAMESIZE_QXGA,     // 2048x1536
+    // 5MP Sensors
+    FRAMESIZE_QHD,      // 2560x1440
+    FRAMESIZE_WQXGA,    // 2560x1600
+    FRAMESIZE_P_FHD,    // 1080x1920
+    FRAMESIZE_QSXGA,    // 2560x1920
+    FRAMESIZE_INVALID
+} framesize_t;
+
+typedef struct {
+    const camera_model_t model;
+    const char *name;
+    const camera_sccb_addr_t sccb_addr;
+    const camera_pid_t pid;
+    const framesize_t max_size;
+    const bool support_jpeg;
+} camera_sensor_info_t;
+
+typedef enum {
+    ASPECT_RATIO_4X3,
+    ASPECT_RATIO_3X2,
+    ASPECT_RATIO_16X10,
+    ASPECT_RATIO_5X3,
+    ASPECT_RATIO_16X9,
+    ASPECT_RATIO_21X9,
+    ASPECT_RATIO_5X4,
+    ASPECT_RATIO_1X1,
+    ASPECT_RATIO_9X16
+} aspect_ratio_t;
+
+typedef enum {
+    GAINCEILING_2X,
+    GAINCEILING_4X,
+    GAINCEILING_8X,
+    GAINCEILING_16X,
+    GAINCEILING_32X,
+    GAINCEILING_64X,
+    GAINCEILING_128X,
+} gainceiling_t;
+
+typedef struct {
+        uint16_t max_width;
+        uint16_t max_height;
+        uint16_t start_x;
+        uint16_t start_y;
+        uint16_t end_x;
+        uint16_t end_y;
+        uint16_t offset_x;
+        uint16_t offset_y;
+        uint16_t total_x;
+        uint16_t total_y;
+} ratio_settings_t;
+
+typedef struct {
+        const uint16_t width;
+        const uint16_t height;
+        const aspect_ratio_t aspect_ratio;
+} resolution_info_t;
+
+// Resolution table (in sensor.c)
+extern const resolution_info_t resolution[];
+// camera sensor table (in sensor.c)
+extern const camera_sensor_info_t camera_sensor[];
+
+typedef struct {
+    uint8_t MIDH;
+    uint8_t MIDL;
+    uint16_t PID;
+    uint8_t VER;
+} sensor_id_t;
+
+typedef struct {
+    framesize_t framesize;//0 - 10
+    bool scale;
+    bool binning;
+    uint8_t quality;//0 - 63
+    int8_t brightness;//-2 - 2
+    int8_t contrast;//-2 - 2
+    int8_t saturation;//-2 - 2
+    int8_t sharpness;//-2 - 2
+    uint8_t denoise;
+    uint8_t special_effect;//0 - 6
+    uint8_t wb_mode;//0 - 4
+    uint8_t awb;
+    uint8_t awb_gain;
+    uint8_t aec;
+    uint8_t aec2;
+    int8_t ae_level;//-2 - 2
+    uint16_t aec_value;//0 - 1200
+    uint8_t agc;
+    uint8_t agc_gain;//0 - 30
+    uint8_t gainceiling;//0 - 6
+    uint8_t bpc;
+    uint8_t wpc;
+    uint8_t raw_gma;
+    uint8_t lenc;
+    uint8_t hmirror;
+    uint8_t vflip;
+    uint8_t dcw;
+    uint8_t colorbar;
+} camera_status_t;
+
+typedef struct _sensor sensor_t;
+typedef struct _sensor {
+    sensor_id_t id;             // Sensor ID.
+    uint8_t  slv_addr;          // Sensor I2C slave address.
+    pixformat_t pixformat;
+    camera_status_t status;
+    int xclk_freq_hz;
+
+    // Sensor function pointers
+    int  (*init_status)         (sensor_t *sensor);
+    int  (*reset)               (sensor_t *sensor);
+    int  (*set_pixformat)       (sensor_t *sensor, pixformat_t pixformat);
+    int  (*set_framesize)       (sensor_t *sensor, framesize_t framesize);
+    int  (*set_contrast)        (sensor_t *sensor, int level);
+    int  (*set_brightness)      (sensor_t *sensor, int level);
+    int  (*set_saturation)      (sensor_t *sensor, int level);
+    int  (*set_sharpness)       (sensor_t *sensor, int level);
+    int  (*set_denoise)         (sensor_t *sensor, int level);
+    int  (*set_gainceiling)     (sensor_t *sensor, gainceiling_t gainceiling);
+    int  (*set_quality)         (sensor_t *sensor, int quality);
+    int  (*set_colorbar)        (sensor_t *sensor, int enable);
+    int  (*set_whitebal)        (sensor_t *sensor, int enable);
+    int  (*set_gain_ctrl)       (sensor_t *sensor, int enable);
+    int  (*set_exposure_ctrl)   (sensor_t *sensor, int enable);
+    int  (*set_hmirror)         (sensor_t *sensor, int enable);
+    int  (*set_vflip)           (sensor_t *sensor, int enable);
+
+    int  (*set_aec2)            (sensor_t *sensor, int enable);
+    int  (*set_awb_gain)        (sensor_t *sensor, int enable);
+    int  (*set_agc_gain)        (sensor_t *sensor, int gain);
+    int  (*set_aec_value)       (sensor_t *sensor, int gain);
+
+    int  (*set_special_effect)  (sensor_t *sensor, int effect);
+    int  (*set_wb_mode)         (sensor_t *sensor, int mode);
+    int  (*set_ae_level)        (sensor_t *sensor, int level);
+
+    int  (*set_dcw)             (sensor_t *sensor, int enable);
+    int  (*set_bpc)             (sensor_t *sensor, int enable);
+    int  (*set_wpc)             (sensor_t *sensor, int enable);
+
+    int  (*set_raw_gma)         (sensor_t *sensor, int enable);
+    int  (*set_lenc)            (sensor_t *sensor, int enable);
+
+    int  (*get_reg)             (sensor_t *sensor, int reg, int mask);
+    int  (*set_reg)             (sensor_t *sensor, int reg, int mask, int value);
+    int  (*set_res_raw)         (sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning);
+    int  (*set_pll)             (sensor_t *sensor, int bypass, int mul, int sys, int root, int pre, int seld5, int pclken, int pclk);
+    int  (*set_xclk)            (sensor_t *sensor, int timer, int xclk);
+} sensor_t;
+
+camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SENSOR_H__ */

+ 60 - 0
std/camera/driver/private_include/cam_hal.h

@@ -0,0 +1,60 @@
+// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include "esp_camera.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Uninitialize the lcd_cam module
+ *
+ * @param handle Provide handle pointer to release resources
+ *
+ * @return
+ *     - ESP_OK Success
+ *     - ESP_FAIL Uninitialize fail
+ */
+esp_err_t cam_deinit(void);
+
+/**
+ * @brief Initialize the lcd_cam module
+ *
+ * @param config Configurations - see lcd_cam_config_t struct
+ *
+ * @return
+ *     - ESP_OK Success
+ *     - ESP_ERR_INVALID_ARG Parameter error
+ *     - ESP_ERR_NO_MEM No memory to initialize lcd_cam
+ *     - ESP_FAIL Initialize fail
+ */
+esp_err_t cam_init(const camera_config_t *config);
+
+esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint16_t sensor_pid);
+
+void cam_stop(void);
+
+void cam_start(void);
+
+camera_fb_t *cam_take(TickType_t timeout);
+
+void cam_give(camera_fb_t *dma_buffer);
+
+#ifdef __cplusplus
+}
+#endif

+ 19 - 0
std/camera/driver/private_include/sccb.h

@@ -0,0 +1,19 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * SCCB (I2C like) driver.
+ *
+ */
+#ifndef __SCCB_H__
+#define __SCCB_H__
+#include <stdint.h>
+int SCCB_Init(int pin_sda, int pin_scl);
+int SCCB_Deinit(void);
+uint8_t SCCB_Probe();
+uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg);
+uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data);
+uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg);
+uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data);
+#endif // __SCCB_H__

+ 9 - 0
std/camera/driver/private_include/xclk.h

@@ -0,0 +1,9 @@
+#pragma once
+
+#include "esp_system.h"
+
+esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
+
+esp_err_t camera_enable_out_clock();
+
+void camera_disable_out_clock();

+ 191 - 0
std/camera/driver/sccb.c

@@ -0,0 +1,191 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * SCCB (I2C like) driver.
+ *
+ */
+#include <stdbool.h>
+#include <string.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+#include "sccb.h"
+#include "sensor.h"
+#include <stdio.h>
+#include "sdkconfig.h"
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char* TAG = "sccb";
+#endif
+
+#define LITTLETOBIG(x)          ((x<<8)|(x>>8))
+
+#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 */
+#define ACK_CHECK_EN            0x1                   /*!< I2C master will check ack from slave*/
+#define ACK_CHECK_DIS           0x0                   /*!< I2C master will not check ack from slave */
+#define ACK_VAL                 0x0                   /*!< I2C ack value */
+#define NACK_VAL                0x1                   /*!< I2C nack value */
+#if CONFIG_SCCB_HARDWARE_I2C_PORT1
+const int SCCB_I2C_PORT         = 1;
+#else
+const int SCCB_I2C_PORT         = 0;
+#endif
+
+int SCCB_Init(int pin_sda, int pin_scl)
+{
+    ESP_LOGI(TAG, "pin_sda %d pin_scl %d", pin_sda, pin_scl);
+    i2c_config_t conf;
+    memset(&conf, 0, sizeof(i2c_config_t));
+    conf.mode = I2C_MODE_MASTER;
+    conf.sda_io_num = pin_sda;
+    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
+    conf.scl_io_num = pin_scl;
+    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
+    conf.master.clk_speed = SCCB_FREQ;
+
+    i2c_param_config(SCCB_I2C_PORT, &conf);
+    i2c_driver_install(SCCB_I2C_PORT, conf.mode, 0, 0, 0);
+    return 0;
+}
+
+int SCCB_Deinit(void)
+{
+    return i2c_driver_delete(SCCB_I2C_PORT);
+}
+
+uint8_t SCCB_Probe(void)
+{
+    uint8_t slave_addr = 0x0;
+    // for (size_t i = 0; i < 10; i++) {
+    //     i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+    //     i2c_master_start(cmd);
+    //     i2c_master_write_byte(cmd, ( 0x30 << 1 ) | WRITE_BIT, ACK_CHECK_EN);
+    //     i2c_master_stop(cmd);
+    //     esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    //     i2c_cmd_link_delete(cmd);
+    //     if( ret == ESP_OK) {
+    //         ESP_LOGW(TAG, "Found I2C Device at 0x%02X", i);
+    //     }
+    // }
+    for (size_t i = 0; i < CAMERA_MODEL_MAX; i++) {
+        if (slave_addr == camera_sensor[i].sccb_addr) {
+            continue;
+        }
+        slave_addr = camera_sensor[i].sccb_addr;
+        ESP_LOGD(TAG, "i2c addr = 0x%X", slave_addr);
+        i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+        i2c_master_start(cmd);
+        i2c_master_write_byte(cmd, ( slave_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
+        i2c_master_stop(cmd);
+        esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+        i2c_cmd_link_delete(cmd);
+        if( ret == ESP_OK) {
+            return slave_addr;
+        }
+        vTaskDelay(10 / portTICK_PERIOD_MS);
+    }
+    return 0;
+}
+
+uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
+{
+    uint8_t data=0;
+    esp_err_t ret = ESP_FAIL;
+    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+    i2c_master_start(cmd);
+    i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
+    i2c_master_stop(cmd);
+    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    i2c_cmd_link_delete(cmd);
+    if(ret != ESP_OK) return -1;
+    cmd = i2c_cmd_link_create();
+    i2c_master_start(cmd);
+    i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
+    i2c_master_read_byte(cmd, &data, NACK_VAL);
+    i2c_master_stop(cmd);
+    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    i2c_cmd_link_delete(cmd);
+    if(ret != ESP_OK) {
+        ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
+    }
+    return data;
+}
+
+uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data)
+{
+    esp_err_t ret = ESP_FAIL;
+    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+    i2c_master_start(cmd);
+    i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
+    i2c_master_stop(cmd);
+    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    i2c_cmd_link_delete(cmd);
+    if(ret != ESP_OK) {
+        ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
+    }
+    return ret == ESP_OK ? 0 : -1;
+}
+
+uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg)
+{
+    uint8_t data=0;
+    esp_err_t ret = ESP_FAIL;
+    uint16_t reg_htons = LITTLETOBIG(reg);
+    uint8_t *reg_u8 = (uint8_t *)&reg_htons;
+    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+    i2c_master_start(cmd);
+    i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
+    i2c_master_stop(cmd);
+    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    i2c_cmd_link_delete(cmd);
+    if(ret != ESP_OK) return -1;
+    cmd = i2c_cmd_link_create();
+    i2c_master_start(cmd);
+    i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
+    i2c_master_read_byte(cmd, &data, NACK_VAL);
+    i2c_master_stop(cmd);
+    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    i2c_cmd_link_delete(cmd);
+    if(ret != ESP_OK) {
+        ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data);
+    }
+    return data;
+}
+
+uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data)
+{
+    static uint16_t i = 0;
+    esp_err_t ret = ESP_FAIL;
+    uint16_t reg_htons = LITTLETOBIG(reg);
+    uint8_t *reg_u8 = (uint8_t *)&reg_htons;
+    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+    i2c_master_start(cmd);
+    i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
+    i2c_master_stop(cmd);
+    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    i2c_cmd_link_delete(cmd);
+    if(ret != ESP_OK) {
+        ESP_LOGE(TAG, "W [%04x]=%02x %d fail\n", reg, data, i++);
+    }
+    return ret == ESP_OK ? 0 : -1;
+}

+ 54 - 0
std/camera/driver/sensor.c

@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include "sensor.h"
+
+const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
+    // The sequence must be consistent with camera_model_t
+    {CAMERA_OV7725, "OV7725", OV7725_SCCB_ADDR, OV7725_PID, FRAMESIZE_VGA, false},
+    {CAMERA_OV2640, "OV2640", OV2640_SCCB_ADDR, OV2640_PID, FRAMESIZE_UXGA, true},
+    {CAMERA_OV3660, "OV3660", OV3660_SCCB_ADDR, OV3660_PID, FRAMESIZE_QXGA, true},
+    {CAMERA_OV5640, "OV5640", OV5640_SCCB_ADDR, OV5640_PID, FRAMESIZE_QSXGA, true},
+    {CAMERA_OV7670, "OV7670", OV7670_SCCB_ADDR, OV7670_PID, FRAMESIZE_VGA, false},
+    {CAMERA_NT99141, "NT99141", NT99141_SCCB_ADDR, NT99141_PID, FRAMESIZE_HD, true},
+    {CAMERA_GC2145, "GC2145", GC2145_SCCB_ADDR, GC2145_PID, FRAMESIZE_UXGA, false},
+    {CAMERA_GC032A, "GC032A", GC032A_SCCB_ADDR, GC032A_PID, FRAMESIZE_VGA, false},
+    {CAMERA_GC0308, "GC0308", GC0308_SCCB_ADDR, GC0308_PID, FRAMESIZE_VGA, false},
+    {CAMERA_BF3005, "BF3005", BF3005_SCCB_ADDR, BF3005_PID, FRAMESIZE_VGA, false},
+    {CAMERA_BF20A6, "BF20A6", BF20A6_SCCB_ADDR, BF20A6_PID, FRAMESIZE_VGA, false},
+};
+
+const resolution_info_t resolution[FRAMESIZE_INVALID] = {
+    {   96,   96, ASPECT_RATIO_1X1   }, /* 96x96 */
+    {  160,  120, ASPECT_RATIO_4X3   }, /* QQVGA */
+    {  176,  144, ASPECT_RATIO_5X4   }, /* QCIF  */
+    {  240,  176, ASPECT_RATIO_4X3   }, /* HQVGA */
+    {  240,  240, ASPECT_RATIO_1X1   }, /* 240x240 */
+    {  320,  240, ASPECT_RATIO_4X3   }, /* QVGA  */
+    {  400,  296, ASPECT_RATIO_4X3   }, /* CIF   */
+    {  480,  320, ASPECT_RATIO_3X2   }, /* HVGA  */
+    {  640,  480, ASPECT_RATIO_4X3   }, /* VGA   */
+    {  800,  600, ASPECT_RATIO_4X3   }, /* SVGA  */
+    { 1024,  768, ASPECT_RATIO_4X3   }, /* XGA   */
+    { 1280,  720, ASPECT_RATIO_16X9  }, /* HD    */
+    { 1280, 1024, ASPECT_RATIO_5X4   }, /* SXGA  */
+    { 1600, 1200, ASPECT_RATIO_4X3   }, /* UXGA  */
+    // 3MP Sensors
+    { 1920, 1080, ASPECT_RATIO_16X9  }, /* FHD   */
+    {  720, 1280, ASPECT_RATIO_9X16  }, /* Portrait HD   */
+    {  864, 1536, ASPECT_RATIO_9X16  }, /* Portrait 3MP   */
+    { 2048, 1536, ASPECT_RATIO_4X3   }, /* QXGA  */
+    // 5MP Sensors
+    { 2560, 1440, ASPECT_RATIO_16X9  }, /* QHD    */
+    { 2560, 1600, ASPECT_RATIO_16X10 }, /* WQXGA  */
+    { 1088, 1920, ASPECT_RATIO_9X16  }, /* Portrait FHD   */
+    { 2560, 1920, ASPECT_RATIO_4X3   }, /* QSXGA  */
+};
+
+camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id)
+{
+    for (int i = 0; i < CAMERA_MODEL_MAX; i++) {
+        if (id->PID == camera_sensor[i].pid) {
+            return (camera_sensor_info_t *)&camera_sensor[i];
+        }
+    }
+    return NULL;
+}

+ 404 - 0
std/camera/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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "sccb.h"
+#include "bf20a6.h"
+#include "bf20a6_regs.h"
+#include "bf20a6_settings.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char *TAG = "bf20a6";
+#endif
+
+#define H8(v) ((v)>>8)
+#define L8(v) ((v)&0xff)
+
+//#define REG_DEBUG_ON
+
+static int read_reg(uint8_t slv_addr, const uint16_t reg)
+{
+    int ret = SCCB_Read(slv_addr, reg);
+    // ESP_LOGI(TAG, "READ Register 0x%02x VALUE: 0x%02x", reg, ret);
+#ifdef REG_DEBUG_ON
+    if (ret < 0) {
+        ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
+{
+    int ret = SCCB_Write(slv_addr, reg, value);
+#ifdef REG_DEBUG_ON
+    if (ret < 0) {
+        ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+#ifdef DEBUG_PRINT_REG
+static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
+{
+    return (read_reg(slv_addr, reg) & mask) == mask;
+}
+
+static void print_regs(uint8_t slv_addr)
+{
+    vTaskDelay(pdMS_TO_TICKS(100));
+    ESP_LOGI(TAG, "REG list look ======================");
+    for (size_t i = 0xf0; i <= 0xfe; i++) {
+        ESP_LOGI(TAG, "reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    ESP_LOGI(TAG, "\npage 0 ===");
+    write_reg(slv_addr, 0xfe, 0x00); // page 0
+    for (size_t i = 0x03; i <= 0x24; i++) {
+        ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    for (size_t i = 0x40; i <= 0x95; i++) {
+        ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    ESP_LOGI(TAG, "\npage 3 ===");
+    write_reg(slv_addr, 0xfe, 0x03); // page 3
+    for (size_t i = 0x01; i <= 0x43; i++) {
+        ESP_LOGI(TAG, "p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+}
+
+static int read_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
+{
+    int i = 0, ret = 0;
+    while (regs[i][0] != REGLIST_TAIL) {
+        if (regs[i][0] == REG_DLY) {
+            vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+        } else {
+            ret = read_reg(slv_addr, regs[i][0]);
+        }
+        i++;
+    }
+    return ret;
+}
+#endif
+
+static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
+{
+    int ret = 0;
+
+    ret = SCCB_Read(sensor->slv_addr, reg);
+    if (ret < 0) {
+        return ret;
+    }
+    uint8_t mask = ((1 << length) - 1) << offset;
+    value = (ret & ~mask) | ((value << offset) & mask);
+    ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
+    return ret;
+}
+
+static int write_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
+{
+    int i = 0, ret = 0;
+    while (!ret && regs[i][0] != REGLIST_TAIL) {
+        if (regs[i][0] == REG_DLY) {
+            vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+        } else {
+            ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
+        }
+        i++;
+    }
+    return ret;
+}
+
+static int reset(sensor_t *sensor)
+{
+    int ret;
+    // Software Reset: clear all registers and reset them to their default values
+    ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x01);
+    if (ret) {
+        ESP_LOGE(TAG, "Software Reset FAILED!");
+        return ret;
+    }
+    vTaskDelay(100 / portTICK_PERIOD_MS);
+
+    ret = write_regs(sensor->slv_addr, bf20a6_default_init_regs);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Camera defaults loaded");
+        vTaskDelay(100 / portTICK_PERIOD_MS);
+    }
+
+    // int test_value = read_regs(sensor->slv_addr, bf20a6_default_init_regs);
+
+    return ret;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret = 0;
+    switch (pixformat) {
+    case PIXFORMAT_YUV422:
+        set_reg_bits(sensor, 0x12, 0, 1, 0);
+        break;
+    case PIXFORMAT_RAW:
+        set_reg_bits(sensor, 0x12, 0, 1, 0x1);
+        break;
+    default:
+        ESP_LOGW(TAG, "set_pix unsupport format");
+        ret = -1;
+        break;
+    }
+    if (ret == 0) {
+        sensor->pixformat = pixformat;
+        ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
+    }
+
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    int ret = 0;
+    if (framesize > FRAMESIZE_VGA) {
+        return -1;
+    }
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+
+    sensor->status.framesize = framesize;
+
+    // Write MSBs
+    ret |= SCCB_Write(sensor->slv_addr, 0x17, 0);
+    ret |= SCCB_Write(sensor->slv_addr, 0x18, w >> 2);
+
+    ret |= SCCB_Write(sensor->slv_addr, 0x19, 0);
+    ret |= SCCB_Write(sensor->slv_addr, 0x1a, h >> 2);
+
+    // Write LSBs
+    ret |= SCCB_Write(sensor->slv_addr, 0x1b, 0);
+
+    if ((w <= 320) && (h <= 240))     {
+        ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 4));
+        ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 4));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 4));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 4));
+
+    } else if ((w <= 640) && (h <= 480))     {
+        ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 8));
+        ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 8));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 8));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 8));
+    }
+
+    // Delay
+    vTaskDelay(30 / portTICK_PERIOD_MS);
+
+    return ret;
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.hmirror = enable;
+    //ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor, 0x4a, 3, 0x01, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set h-mirror to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.vflip = enable;
+    //ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor, 0x4a, 2, 0x01, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set v-flip to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int value)
+{
+    int ret = 0;
+    ret = write_reg(sensor->slv_addr, 0xb6, value);
+    if (ret == 0) {
+        sensor->status.colorbar = value;
+        ESP_LOGD(TAG, "Set colorbar to: %d", value);
+    }
+    return ret;
+}
+
+static int set_sharpness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    ret = SCCB_Write(sensor->slv_addr, 0x70, level);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set sharpness to: %d", level);
+        sensor->status.sharpness = level;
+    }
+    return ret;
+}
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = 0;
+    if (mask > 0xFF) {
+        ESP_LOGE(TAG, "mask should not more than 0xff");
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if (ret > 0) {
+        ret &= mask;
+    }
+    return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0;
+    if (mask > 0xFF) {
+        ESP_LOGE(TAG, "mask should not more than 0xff");
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if (ret < 0) {
+        return ret;
+    }
+    value = (ret & ~mask) | (value & mask);
+
+    if (mask > 0xFF) {
+
+    } else {
+        ret = write_reg(sensor->slv_addr, reg, value);
+    }
+    return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    // write_reg(sensor->slv_addr, 0xfe, 0x00);
+    sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x6f);
+    sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0xd6);
+    sensor->status.saturation = 0;
+    sensor->status.sharpness = SCCB_Read(sensor->slv_addr, 0x70);
+    sensor->status.denoise = 0;
+    sensor->status.ae_level = 0;
+    sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x13);
+    sensor->status.awb = 0;
+    sensor->status.dcw = 0;
+    sensor->status.agc = 0;
+    sensor->status.aec = 0;
+    sensor->status.hmirror = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x01);
+    sensor->status.vflip = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x02);
+    sensor->status.colorbar = 0;
+    sensor->status.bpc = 0;
+    sensor->status.wpc = 0;
+    sensor->status.raw_gma = 0;
+    sensor->status.lenc = 0;
+    sensor->status.quality = 0;
+    sensor->status.special_effect = 0;
+    sensor->status.wb_mode = 0;
+    sensor->status.awb_gain = 0;
+    sensor->status.agc_gain = 0;
+    sensor->status.aec_value = 0;
+    sensor->status.aec2 = 0;
+    return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val)
+{
+    ESP_LOGW(TAG, "dummy Unsupported");
+    return -1;
+}
+static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
+{
+    ESP_LOGW(TAG, "gainceiling Unsupported");
+    return -1;
+}
+
+int bf20a6_detect(int slv_addr, sensor_id_t *id)
+{
+    if (BF20A6_SCCB_ADDR == slv_addr) {
+        uint8_t MIDL = SCCB_Read(slv_addr, SENSOR_ID_LOW);
+        uint8_t MIDH = SCCB_Read(slv_addr, SENSOR_ID_HIGH);
+        uint16_t PID = MIDH << 8 | MIDL;
+        if (BF20A6_PID == PID) {
+            id->PID = PID;
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int bf20a6_init(sensor_t *sensor)
+{
+    sensor->init_status = init_status;
+    sensor->reset = reset;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_contrast = set_dummy;
+    sensor->set_brightness = set_dummy;
+    sensor->set_saturation = set_dummy;
+    sensor->set_sharpness = set_sharpness;
+    sensor->set_denoise = set_dummy;
+    sensor->set_gainceiling = set_gainceiling_dummy;
+    sensor->set_quality = set_dummy;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_whitebal = set_dummy;
+    sensor->set_gain_ctrl = set_dummy;
+    sensor->set_exposure_ctrl = set_dummy;
+    sensor->set_hmirror = set_hmirror; // set_hmirror;
+    sensor->set_vflip = set_vflip; // set_vflip;
+
+    sensor->set_aec2 = set_dummy;
+    sensor->set_awb_gain = set_dummy;
+    sensor->set_agc_gain = set_dummy;
+    sensor->set_aec_value = set_dummy;
+
+    sensor->set_special_effect = set_dummy;
+    sensor->set_wb_mode = set_dummy;
+    sensor->set_ae_level = set_dummy;
+
+    sensor->set_dcw = set_dummy;
+    sensor->set_bpc = set_dummy;
+    sensor->set_wpc = set_dummy;
+
+    sensor->set_raw_gma = set_dummy;
+    sensor->set_lenc = set_dummy;
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_res_raw = NULL;
+    sensor->set_pll = NULL;
+    sensor->set_xclk = NULL;
+
+    ESP_LOGD(TAG, "BF20A6 Attached");
+    return 0;
+}

+ 541 - 0
std/camera/sensors/bf3005.c

@@ -0,0 +1,541 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * BF3005 driver.
+ * 
+ * Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "sccb.h"
+#include "xclk.h"
+#include "bf3005.h"
+#include "bf3005_regs.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char* TAG = "bf3005";
+#endif
+
+static const uint8_t default_regs[][2] = {
+  {0x12, 0x40}, //soft reset
+  {0xff, 0xff}, //delay 
+  {0xff, 0xff}, //delay 
+  {0xff, 0xff}, //delay 
+  {0xff, 0xff}, //delay 
+  {0x13, 0x10},	
+  {0x8c, 0x00},
+  {0x8d, 0x64},
+  {0x87, 0x10},
+  {0x13, 0x17},
+  {0x00, 0x20},
+  {0x01, 0x1a},
+  {0x02, 0x22},
+  {0x09, 0x03},
+  {0x0c, 0x80},
+  {0x0d, 0x24},
+  {0x0e, 0x21},
+  {0x0f, 0x28},
+  {0x11, 0x08},
+  {0x15, 0x10}, // 0X10
+  {0x16, 0x03},
+  {0x1e, 0x30},
+  {0x20, 0x8a},
+  {0x21, 0x03},
+  {0x23, 0x55},
+  {0x24, 0x68},
+  {0x25, 0x78},
+  {0x2a, 0x00},
+  {0x2b, 0x00},
+  {0x2d, 0x4f},
+  {0x2e, 0x98},
+  {0x2f, 0x04},
+  {0x30, 0xad},
+  {0x31, 0x17},
+  {0x32, 0x6e},
+  {0x33, 0x20},
+  {0x35, 0xa6},
+  {0x3b, 0x00},
+  {0x3e, 0x00},
+  {0x3f, 0xA8},
+  {0x40, 0x38},
+  {0x41, 0x32},
+  {0x42, 0x2b},
+  {0x43, 0x26},
+  {0x44, 0x1a},
+  {0x45, 0x16},
+  {0x46, 0x10},
+  {0x47, 0x0f},
+  {0x48, 0x0c},
+  {0x49, 0x0a},
+  {0x4b, 0x09},
+  {0x4c, 0x08},
+  {0x4d, 0x3c},
+  {0x4e, 0x06},
+  {0x4f, 0x05},
+  {0x50, 0x03},
+  {0x51, 0x25},
+  {0x52, 0x88},
+  {0x53, 0x03},
+  {0x63, 0x20},
+  {0x64, 0x02},
+  {0x65, 0xa6},
+  {0x66, 0xb6},
+  {0x69, 0x00},
+  {0x70, 0xFF},
+  {0x71, 0xa6},
+  {0x72, 0x2f},
+  {0x73, 0x2f},
+  {0x74, 0x2F},
+  {0x75, 0x0e},
+  {0x76, 0x1e},
+  {0x77, 0x00},
+  {0x78, 0x1e},
+  {0x79, 0x8a},
+  {0x7d, 0xe2},
+  {0x80, 0x44},
+  {0x81, 0x00},
+  {0x82, 0x18},
+  {0x83, 0x1b},
+  {0x84, 0x24},
+  {0x85, 0x2a},
+  {0x86, 0x4f},
+  {0x89, 0x82}, //0x82
+  {0x8b, 0x02},
+  {0x8e, 0x03},
+  {0x8f, 0xFC},
+  {0x9d, 0x4d},
+  {0x9e, 0x41},
+  {0xa1, 0x21},
+  {0xa2, 0x12},
+  {0xa3, 0x32},
+  {0xa4, 0x05},
+  {0xa5, 0x32},
+  {0xa6, 0x04},
+  {0xa7, 0x7f},
+  {0xa8, 0x7f},
+  {0xa9, 0x21},
+  {0xaa, 0x21},
+  {0xab, 0x21},
+  {0xac, 0x0a},
+  {0xad, 0xf0},
+  {0xae, 0xff},
+  {0xaf, 0x1d},
+  {0xb0, 0x94},
+  {0xb1, 0xc0},
+  {0xb2, 0xc0},
+  {0xd2, 0x30},
+  {0xe0, 0x0d},
+  {0xe1, 0x44},
+  {0xe7, 0x7c},
+  {0xe8, 0x89},
+  {0xe9, 0x01},
+  {0xea, 0x01},
+  {0xf0, 0x01},
+  {0xf3, 0x49},
+  {0xf4, 0xff},
+  {0xf5, 0x01},
+  {0xf6, 0xf2},
+  {0xf7, 0x6f},
+  {0x1b, 0x80},
+  {0x00, 0x00},
+};
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
+    if(ret > 0){
+        ret &= mask;
+    }
+    return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0;
+    ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
+    if(ret < 0){
+        return ret;
+    }
+    value = (ret & ~mask) | (value & mask);
+    ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
+    return ret;
+}
+
+static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
+{
+    int ret = 0;
+    ret = SCCB_Read(sensor->slv_addr, reg);
+    if(ret < 0){
+        return ret;
+    }
+    uint8_t mask = ((1 << length) - 1) << offset;
+    value = (ret & ~mask) | ((value << offset) & mask);
+    ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
+    return ret;
+}
+
+static int get_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length)
+{
+    int ret = 0;
+    ret = SCCB_Read(sensor->slv_addr, reg);
+    if(ret < 0){
+        return ret;
+    }
+    uint8_t mask = ((1 << length) - 1) << offset;
+    return (ret & mask) >> offset;
+}
+
+
+static int reset(sensor_t *sensor)
+{
+    int i=0;
+    const uint8_t (*regs)[2];
+
+    // Write default regsiters
+    for (i=0, regs = default_regs; regs[i][0]; i++) {
+        SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
+    }
+
+    // Delay
+    vTaskDelay(50 / portTICK_PERIOD_MS);
+
+    return 0;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret=0;
+    sensor->pixformat = pixformat;
+
+    switch (pixformat) {
+    case PIXFORMAT_RGB565:
+        set_reg_bits(sensor, 0x12, 2, 1, 1);
+        break;
+    case PIXFORMAT_RAW:
+        set_reg_bits(sensor, 0x12, 0, 3, 0x4);
+        break;
+    case PIXFORMAT_YUV422:
+    case PIXFORMAT_GRAYSCALE:
+       set_reg_bits(sensor, 0x12, 2, 1, 0);
+        break;
+    default:
+        return -1;
+    }
+
+    // Delay
+    vTaskDelay(30 / portTICK_PERIOD_MS);
+
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    int ret=0;
+    if (framesize > FRAMESIZE_VGA) {
+        return -1;
+    }
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+    // uint8_t reg = SCCB_Read(sensor->slv_addr, COM7);
+
+    sensor->status.framesize = framesize;
+
+    // Write MSBs
+    ret |= SCCB_Write(sensor->slv_addr, 0x17, 0);
+    ret |= SCCB_Write(sensor->slv_addr, 0x18, w>>2);
+
+    ret |= SCCB_Write(sensor->slv_addr, 0x19, 0);
+    ret |= SCCB_Write(sensor->slv_addr, 0x1a, h>>2);
+
+    // Write LSBs
+    ret |= SCCB_Write(sensor->slv_addr, 0x03, 0);
+    printf("%s %d\r\n", __func__, __LINE__);
+    if((w<=320)&&(h<=240))
+    {
+        printf("%s %d\r\n", __func__, __LINE__);
+        // Enable auto-scaling/zooming factors
+        //ret |= SCCB_Write(sensor->slv_addr, 0x12, 0x50);
+        set_reg_bits(sensor, 0x12, 4, 1, 1);
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x17, (80-w/4));
+        ret |= SCCB_Write(sensor->slv_addr, 0x18, (80+w/4));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x19, (60-h/4));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60+h/4));
+        ret |= SCCB_Write(sensor->slv_addr, 0x03, 0);
+
+    } else if((w<=640)&&(h<=480))
+    	{
+       // Enable auto-scaling/zooming factors
+        //ret |= SCCB_Write(sensor->slv_addr, 0x12, 0x40);
+        set_reg_bits(sensor, 0x12, 4, 1, 0);
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x17, (80-w/8));
+        ret |= SCCB_Write(sensor->slv_addr, 0x18, (80+w/8));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x19, (60-h/8));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60+h/8));
+        ret |= SCCB_Write(sensor->slv_addr, 0x03, 0);
+    }
+
+    // Delay
+    vTaskDelay(30 / portTICK_PERIOD_MS);
+
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int value)
+{
+    int ret=0;
+    sensor->status.colorbar = value;
+
+    ret |= SCCB_Write(sensor->slv_addr, 0xb9, value);
+
+    return ret;
+}
+
+static int set_whitebal(sensor_t *sensor, int enable)
+{
+    if(set_reg_bits(sensor, 0x13, 1, 1, enable) >= 0){
+        sensor->status.awb = !!enable;
+    }
+    return sensor->status.awb;
+}
+
+
+static int set_gain_ctrl(sensor_t *sensor, int enable)
+{
+    if(set_reg_bits(sensor, 0x13, 2, 1, enable) >= 0){
+        sensor->status.agc = !!enable;
+    }
+    return sensor->status.agc;
+}
+
+
+static int set_exposure_ctrl(sensor_t *sensor, int enable)
+{
+    if(set_reg_bits(sensor, 0x13, 0, 1, enable) >= 0){
+        sensor->status.aec = !!enable;
+    }
+    return sensor->status.aec;
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    if(set_reg_bits(sensor, 0x1e, 5, 1, enable) >= 0){
+        sensor->status.hmirror = !!enable;
+    }
+    return sensor->status.hmirror;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+    if(set_reg_bits(sensor, 0x1e, 4, 1, enable) >= 0){
+        sensor->status.vflip = !!enable;
+    }
+    return sensor->status.vflip;
+}
+
+static int set_raw_gma_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = set_reg_bits(sensor, 0xf1, 1, 1, !enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set raw_gma to: %d", !enable);
+        sensor->status.raw_gma = !enable;
+    }
+    return ret;
+}
+
+
+static int set_lenc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = set_reg_bits(sensor, 0xf1, 0, 1, !enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set lenc to: %d", !enable);
+        sensor->status.lenc = !enable;
+    }
+    return ret;
+}
+
+static int set_agc_gain(sensor_t *sensor, int option)
+{
+    int ret = 0;
+    ret = set_reg_bits(sensor, 0x13, 4, 1, !!option);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set gain to: %d", !!option);
+        sensor->status.agc_gain = !!option;
+    }
+    return ret;
+}
+
+static int set_awb_gain_dsp(sensor_t *sensor, int value)
+{
+    int ret = 0;
+    ret = SCCB_Write(sensor->slv_addr, 0xa6, value);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set awb gain threthold to: %d", value);
+        sensor->status.awb_gain = value;
+    }
+    return ret;
+}
+
+static int set_brightness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    ret = SCCB_Write(sensor->slv_addr, 0x55, level);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set brightness to: %d", level);
+        sensor->status.brightness = level;
+    }
+    return ret;
+}
+
+static int set_contrast(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    ret = SCCB_Write(sensor->slv_addr, 0x56, level);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set contrast to: %d", level);
+        sensor->status.contrast = level;
+    }
+    return ret;
+}
+
+static int set_sharpness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    ret = SCCB_Write(sensor->slv_addr, 0x70, level);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set sharpness to: %d", level);
+        sensor->status.sharpness = level;
+    }
+    return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x55);
+    sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0x56);
+    sensor->status.saturation = 0;
+    sensor->status.ae_level = 0;
+    
+    sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x87);
+    sensor->status.awb = get_reg_bits(sensor, 0x13, 1, 1);
+    sensor->status.awb_gain = SCCB_Read(sensor->slv_addr, 0xa6);
+    sensor->status.aec = get_reg_bits(sensor, 0x13, 0, 1);
+
+    sensor->status.agc = get_reg_bits(sensor, 0x13, 2, 1);
+    
+    sensor->status.raw_gma = get_reg_bits(sensor, 0xf1, 1, 1);
+    sensor->status.lenc = get_reg_bits(sensor, 0xf1, 0, 1);
+    sensor->status.hmirror = get_reg_bits(sensor, 0x1e, 5, 1);
+    sensor->status.vflip = get_reg_bits(sensor, 0x1e, 4, 1);
+    
+    sensor->status.colorbar = SCCB_Read(sensor->slv_addr, 0xb9);
+    sensor->status.sharpness = SCCB_Read(sensor->slv_addr, 0x70);
+    
+    return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val){ return -1; }
+static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
+static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning){return -1;}
+static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div){return -1;}
+
+static int set_xclk(sensor_t *sensor, int timer, int xclk)
+{
+    int ret = 0;
+    sensor->xclk_freq_hz = xclk * 1000000U;
+    ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
+    return ret;
+}
+
+int bf3005_detect(int slv_addr, sensor_id_t *id)
+{
+    if (BF3005_SCCB_ADDR == slv_addr) {
+        uint16_t PID = SCCB_Read(slv_addr, 0xFC);
+        if (BF3005_PID == PID) {
+            id->PID = PID;
+            id->VER = SCCB_Read(slv_addr, 0xFD);
+            id->MIDL = SCCB_Read(slv_addr, 0xFC);
+            id->MIDH = SCCB_Read(slv_addr, 0xFD);
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int bf3005_init(sensor_t *sensor)
+{
+    // Set function pointers
+    sensor->reset = reset;
+    sensor->init_status = init_status;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_brightness = set_brightness;
+    sensor->set_contrast = set_contrast;
+
+    sensor->set_colorbar = set_colorbar;
+
+    sensor->set_gain_ctrl = set_gain_ctrl;
+    sensor->set_exposure_ctrl = set_exposure_ctrl;
+    sensor->set_hmirror = set_hmirror;
+    sensor->set_vflip = set_vflip;
+
+    sensor->set_whitebal = set_whitebal;
+
+    sensor->set_awb_gain = set_awb_gain_dsp;
+    sensor->set_agc_gain = set_agc_gain;
+    
+    sensor->set_raw_gma = set_raw_gma_dsp;
+    sensor->set_lenc = set_lenc_dsp;
+
+    sensor->set_sharpness = set_sharpness;
+    //not supported
+    sensor->set_saturation= set_dummy;
+    sensor->set_denoise = set_dummy;
+    sensor->set_quality = set_dummy;
+    sensor->set_special_effect = set_dummy;
+    sensor->set_wb_mode = set_dummy;
+    sensor->set_ae_level = set_dummy;
+    sensor->set_gainceiling = set_gainceiling_dummy;
+
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_res_raw = set_res_raw;
+    sensor->set_pll = _set_pll;
+    sensor->set_xclk = set_xclk;
+    
+    ESP_LOGD(TAG, "BF3005 Attached");
+
+    return 0;
+}

+ 467 - 0
std/camera/sensors/gc0308.c

@@ -0,0 +1,467 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "sccb.h"
+#include "gc0308.h"
+#include "gc0308_regs.h"
+#include "gc0308_settings.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char *TAG = "gc0308";
+#endif
+
+#define H8(v) ((v)>>8)
+#define L8(v) ((v)&0xff)
+
+//#define REG_DEBUG_ON
+
+static int read_reg(uint8_t slv_addr, const uint16_t reg)
+{
+    int ret = SCCB_Read(slv_addr, reg);
+#ifdef REG_DEBUG_ON
+    if (ret < 0) {
+        ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
+{
+    int ret = 0;
+#ifndef REG_DEBUG_ON
+    ret = SCCB_Write(slv_addr, reg, value);
+#else
+    int old_value = read_reg(slv_addr, reg);
+    if (old_value < 0) {
+        return old_value;
+    }
+    if ((uint8_t)old_value != value) {
+        ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
+        ret = SCCB_Write(slv_addr, reg, value);
+    } else {
+        ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
+        ret = SCCB_Write(slv_addr, reg, value);//maybe not?
+    }
+    if (ret < 0) {
+        ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
+{
+    return (read_reg(slv_addr, reg) & mask) == mask;
+}
+
+static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
+{
+    int ret = 0;
+    uint8_t c_value, new_value;
+    ret = read_reg(slv_addr, reg);
+    if (ret < 0) {
+        return ret;
+    }
+    c_value = ret;
+    new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
+    ret = write_reg(slv_addr, reg, new_value);
+    return ret;
+}
+
+static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
+{
+    int i = 0, ret = 0;
+    while (!ret && regs[i][0] != REGLIST_TAIL) {
+        if (regs[i][0] == REG_DLY) {
+            vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+        } else {
+            ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
+        }
+        i++;
+    }
+    return ret;
+}
+
+static void print_regs(uint8_t slv_addr)
+{
+#ifdef DEBUG_PRINT_REG
+    ESP_LOGI(TAG, "REG list look ======================");
+    for (size_t i = 0xf0; i <= 0xfe; i++) {
+        ESP_LOGI(TAG, "reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    ESP_LOGI(TAG, "\npage 0 ===");
+    write_reg(slv_addr, 0xfe, 0x00); // page 0
+    for (size_t i = 0x03; i <= 0xa2; i++) {
+        ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+
+    ESP_LOGI(TAG, "\npage 3 ===");
+    write_reg(slv_addr, 0xfe, 0x03); // page 3
+    for (size_t i = 0x01; i <= 0x43; i++) {
+        ESP_LOGI(TAG, "p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+#endif
+}
+
+static int reset(sensor_t *sensor)
+{
+    int ret = 0;
+    // Software Reset: clear all registers and reset them to their default values
+    ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xf0);
+    if (ret) {
+        ESP_LOGE(TAG, "Software Reset FAILED!");
+        return ret;
+    }
+    vTaskDelay(100 / portTICK_PERIOD_MS);
+    ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Camera defaults loaded");
+        vTaskDelay(100 / portTICK_PERIOD_MS);
+        write_reg(sensor->slv_addr, 0xfe, 0x00);
+#ifdef CONFIG_IDF_TARGET_ESP32
+        set_reg_bits(sensor->slv_addr, 0x28, 4, 0x07, 1);  //frequency division for esp32, ensure pclk <= 15MHz
+#endif
+    }
+    return ret;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret = 0;
+
+    switch (pixformat) {
+    case PIXFORMAT_RGB565:
+        write_reg(sensor->slv_addr, 0xfe, 0x00);
+        ret = set_reg_bits(sensor->slv_addr, 0x24, 0, 0x0f, 6);  //RGB565
+        break;
+
+    case PIXFORMAT_YUV422:
+        write_reg(sensor->slv_addr, 0xfe, 0x00);
+        ret = set_reg_bits(sensor->slv_addr, 0x24, 0, 0x0f, 2); //yuv422 Y Cb Y Cr
+        break;
+    default:
+        ESP_LOGW(TAG, "unsupport format");
+        ret = -1;
+        break;
+    }
+
+    if (ret == 0) {
+        sensor->pixformat = pixformat;
+        ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
+    }
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    int ret = 0;
+    if (framesize > FRAMESIZE_VGA) {
+        ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
+        framesize = FRAMESIZE_VGA;
+    }
+    sensor->status.framesize = framesize;
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+    uint16_t row_s = (resolution[FRAMESIZE_VGA].height - h) / 2;
+    uint16_t col_s = (resolution[FRAMESIZE_VGA].width - w) / 2;
+    (void)row_s;
+    (void)col_s;
+
+#if CONFIG_GC_SENSOR_SUBSAMPLE_MODE
+    struct subsample_cfg {
+        uint16_t ratio_numerator;
+        uint16_t ratio_denominator;
+        uint8_t reg0x54;
+        uint8_t reg0x56;
+        uint8_t reg0x57;
+        uint8_t reg0x58;
+        uint8_t reg0x59;
+    };
+    const struct subsample_cfg subsample_cfgs[] = { // define some subsample ratio
+        {84, 420, 0x55, 0x00, 0x00, 0x00, 0x00}, //1/5
+        {105, 420, 0x44, 0x00, 0x00, 0x00, 0x00},//1/4
+        {140, 420, 0x33, 0x00, 0x00, 0x00, 0x00},//1/3
+        {210, 420, 0x22, 0x00, 0x00, 0x00, 0x00},//1/2
+        {240, 420, 0x77, 0x02, 0x46, 0x02, 0x46},//4/7
+        {252, 420, 0x55, 0x02, 0x04, 0x02, 0x04},//3/5
+        {280, 420, 0x33, 0x02, 0x00, 0x02, 0x00},//2/3
+        {420, 420, 0x11, 0x00, 0x00, 0x00, 0x00},//1/1
+    };
+    uint16_t win_w = 640;
+    uint16_t win_h = 480;
+    const struct subsample_cfg *cfg = NULL;
+    /**
+     * Strategy: try to keep the maximum perspective
+     */
+    for (size_t i = 0; i < sizeof(subsample_cfgs) / sizeof(struct subsample_cfg); i++) {
+        cfg = &subsample_cfgs[i];
+        if ((win_w * cfg->ratio_numerator / cfg->ratio_denominator >= w) && (win_h * cfg->ratio_numerator / cfg->ratio_denominator >= h)) {
+            win_w = w * cfg->ratio_denominator / cfg->ratio_numerator;
+            win_h = h * cfg->ratio_denominator / cfg->ratio_numerator;
+            row_s = (resolution[FRAMESIZE_VGA].height - win_h) / 2;
+            col_s = (resolution[FRAMESIZE_VGA].width - win_w) / 2;
+            ESP_LOGI(TAG, "subsample win:%dx%d, ratio:%f", win_w, win_h, (float)cfg->ratio_numerator / (float)cfg->ratio_denominator);
+            break;
+        }
+    }
+
+    write_reg(sensor->slv_addr, 0xfe, 0x00);
+
+    write_reg(sensor->slv_addr, 0x05, H8(row_s));
+    write_reg(sensor->slv_addr, 0x06, L8(row_s));
+    write_reg(sensor->slv_addr, 0x07, H8(col_s));
+    write_reg(sensor->slv_addr, 0x08, L8(col_s));
+    write_reg(sensor->slv_addr, 0x09, H8(win_h + 8));
+    write_reg(sensor->slv_addr, 0x0a, L8(win_h + 8));
+    write_reg(sensor->slv_addr, 0x0b, H8(win_w + 8));
+    write_reg(sensor->slv_addr, 0x0c, L8(win_w + 8));
+
+    write_reg(sensor->slv_addr, 0xfe, 0x01);
+    set_reg_bits(sensor->slv_addr, 0x53, 7, 0x01, 1);
+    set_reg_bits(sensor->slv_addr, 0x55, 0, 0x01, 1);
+    write_reg(sensor->slv_addr, 0x54, cfg->reg0x54);
+    write_reg(sensor->slv_addr, 0x56, cfg->reg0x56);
+    write_reg(sensor->slv_addr, 0x57, cfg->reg0x57);
+    write_reg(sensor->slv_addr, 0x58, cfg->reg0x58);
+    write_reg(sensor->slv_addr, 0x59, cfg->reg0x59);
+
+    write_reg(sensor->slv_addr, 0xfe, 0x00);
+
+#elif CONFIG_GC_SENSOR_WINDOWING_MODE
+    write_reg(sensor->slv_addr, 0xfe, 0x00);
+
+    write_reg(sensor->slv_addr, 0xf7, col_s / 4);
+    write_reg(sensor->slv_addr, 0xf8, row_s / 4);
+    write_reg(sensor->slv_addr, 0xf9, (col_s + h) / 4);
+    write_reg(sensor->slv_addr, 0xfa, (row_s + w) / 4);
+
+    write_reg(sensor->slv_addr, 0x05, H8(row_s));
+    write_reg(sensor->slv_addr, 0x06, L8(row_s));
+    write_reg(sensor->slv_addr, 0x07, H8(col_s));
+    write_reg(sensor->slv_addr, 0x08, L8(col_s));
+
+    write_reg(sensor->slv_addr, 0x09, H8(h + 8));
+    write_reg(sensor->slv_addr, 0x0a, L8(h + 8));
+    write_reg(sensor->slv_addr, 0x0b, H8(w + 8));
+    write_reg(sensor->slv_addr, 0x0c, L8(w + 8));
+
+#endif
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
+    }
+    return 0;
+}
+
+static int set_contrast(sensor_t *sensor, int contrast)
+{
+    if (contrast != 0) {
+        write_reg(sensor->slv_addr, 0xfe, 0x00);
+        write_reg(sensor->slv_addr, 0xb3, contrast);
+    }
+    return 0;
+}
+
+static int set_global_gain(sensor_t *sensor, int gain_level)
+{
+    if (gain_level != 0) {
+        write_reg(sensor->slv_addr, 0xfe, 0x00);
+        write_reg(sensor->slv_addr, 0x50, gain_level);
+    }
+    return 0;
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.hmirror = enable;
+    ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor->slv_addr, 0x14, 0, 0x01, enable != 0);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set h-mirror to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.vflip = enable;
+    ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor->slv_addr, 0x14, 1, 0x01, enable != 0);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set v-flip to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor->slv_addr, 0x2e, 0, 0x01, enable);
+    if (ret == 0) {
+        sensor->status.colorbar = enable;
+        ESP_LOGD(TAG, "Set colorbar to: %d", enable);
+    }
+    return ret;
+}
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = 0;
+    if (mask > 0xFF) {
+        ESP_LOGE(TAG, "mask should not more than 0xff");
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if (ret > 0) {
+        ret &= mask;
+    }
+    return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0;
+    if (mask > 0xFF) {
+        ESP_LOGE(TAG, "mask should not more than 0xff");
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if (ret < 0) {
+        return ret;
+    }
+    value = (ret & ~mask) | (value & mask);
+
+    if (mask > 0xFF) {
+
+    } else {
+        ret = write_reg(sensor->slv_addr, reg, value);
+    }
+    return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    write_reg(sensor->slv_addr, 0xfe, 0x00);
+    sensor->status.brightness = 0;
+    sensor->status.contrast = 0;
+    sensor->status.saturation = 0;
+    sensor->status.sharpness = 0;
+    sensor->status.denoise = 0;
+    sensor->status.ae_level = 0;
+    sensor->status.gainceiling = 0;
+    sensor->status.awb = 0;
+    sensor->status.dcw = 0;
+    sensor->status.agc = 0;
+    sensor->status.aec = 0;
+    sensor->status.hmirror = check_reg_mask(sensor->slv_addr, 0x14, 0x01);
+    sensor->status.vflip = check_reg_mask(sensor->slv_addr, 0x14, 0x02);
+    sensor->status.colorbar = 0;
+    sensor->status.bpc = 0;
+    sensor->status.wpc = 0;
+    sensor->status.raw_gma = 0;
+    sensor->status.lenc = 0;
+    sensor->status.quality = 0;
+    sensor->status.special_effect = 0;
+    sensor->status.wb_mode = 0;
+    sensor->status.awb_gain = 0;
+    sensor->status.agc_gain = 0;
+    sensor->status.aec_value = 0;
+    sensor->status.aec2 = 0;
+
+    print_regs(sensor->slv_addr);
+    return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val)
+{
+    ESP_LOGW(TAG, "Unsupported");
+    return -1;
+}
+static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
+{
+    ESP_LOGW(TAG, "Unsupported");
+    return -1;
+}
+
+int gc0308_detect(int slv_addr, sensor_id_t *id)
+{
+    if (GC0308_SCCB_ADDR == slv_addr) {
+        write_reg(slv_addr, 0xfe, 0x00);
+        uint8_t PID = SCCB_Read(slv_addr, 0x00);
+        if (GC0308_PID == PID) {
+            id->PID = PID;
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int gc0308_init(sensor_t *sensor)
+{
+    sensor->init_status = init_status;
+    sensor->reset = reset;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_contrast = set_contrast;
+    sensor->set_brightness = set_dummy;
+    sensor->set_saturation = set_dummy;
+    sensor->set_sharpness = set_dummy;
+    sensor->set_denoise = set_dummy;
+    sensor->set_gainceiling = set_gainceiling_dummy;
+    sensor->set_quality = set_dummy;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_whitebal = set_dummy;
+    sensor->set_gain_ctrl = set_global_gain;
+    sensor->set_exposure_ctrl = set_dummy;
+    sensor->set_hmirror = set_hmirror;
+    sensor->set_vflip = set_vflip;
+
+    sensor->set_aec2 = set_dummy;
+    sensor->set_awb_gain = set_dummy;
+    sensor->set_agc_gain = set_dummy;
+    sensor->set_aec_value = set_dummy;
+
+    sensor->set_special_effect = set_dummy;
+    sensor->set_wb_mode = set_dummy;
+    sensor->set_ae_level = set_dummy;
+
+    sensor->set_dcw = set_dummy;
+    sensor->set_bpc = set_dummy;
+    sensor->set_wpc = set_dummy;
+
+    sensor->set_raw_gma = set_dummy;
+    sensor->set_lenc = set_dummy;
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_res_raw = NULL;
+    sensor->set_pll = NULL;
+    sensor->set_xclk = NULL;
+
+    ESP_LOGD(TAG, "GC0308 Attached");
+    return 0;
+}

+ 391 - 0
std/camera/sensors/gc032a.c

@@ -0,0 +1,391 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "sccb.h"
+#include "gc032a.h"
+#include "gc032a_regs.h"
+#include "gc032a_settings.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char *TAG = "gc032a";
+#endif
+
+#define H8(v) ((v)>>8)
+#define L8(v) ((v)&0xff)
+
+//#define REG_DEBUG_ON
+
+static int read_reg(uint8_t slv_addr, const uint16_t reg)
+{
+    int ret = SCCB_Read(slv_addr, reg);
+#ifdef REG_DEBUG_ON
+    if (ret < 0) {
+        ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
+{
+    int ret = 0;
+#ifndef REG_DEBUG_ON
+    ret = SCCB_Write(slv_addr, reg, value);
+#else
+    int old_value = read_reg(slv_addr, reg);
+    if (old_value < 0) {
+        return old_value;
+    }
+    if ((uint8_t)old_value != value) {
+        ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
+        ret = SCCB_Write(slv_addr, reg, value);
+    } else {
+        ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
+        ret = SCCB_Write(slv_addr, reg, value);//maybe not?
+    }
+    if (ret < 0) {
+        ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
+{
+    return (read_reg(slv_addr, reg) & mask) == mask;
+}
+
+static void print_regs(uint8_t slv_addr)
+{
+#ifdef DEBUG_PRINT_REG
+    vTaskDelay(pdMS_TO_TICKS(100));
+    ESP_LOGI(TAG, "REG list look ======================");
+    for (size_t i = 0xf0; i <= 0xfe; i++) {
+        ESP_LOGI(TAG, "reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    ESP_LOGI(TAG, "\npage 0 ===");
+    write_reg(slv_addr, 0xfe, 0x00); // page 0
+    for (size_t i = 0x03; i <= 0x24; i++) {
+        ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    for (size_t i = 0x40; i <= 0x95; i++) {
+        ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    ESP_LOGI(TAG, "\npage 3 ===");
+    write_reg(slv_addr, 0xfe, 0x03); // page 3
+    for (size_t i = 0x01; i <= 0x43; i++) {
+        ESP_LOGI(TAG, "p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+#endif
+}
+
+static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
+{
+    int ret = 0;
+    uint8_t c_value, new_value;
+    ret = read_reg(slv_addr, reg);
+    if (ret < 0) {
+        return ret;
+    }
+    c_value = ret;
+    new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
+    ret = write_reg(slv_addr, reg, new_value);
+    return ret;
+}
+
+static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
+{
+    int i = 0, ret = 0;
+    while (!ret && regs[i][0] != REGLIST_TAIL) {
+        if (regs[i][0] == REG_DLY) {
+            vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+        } else {
+            ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
+        }
+        i++;
+    }
+    return ret;
+}
+
+static int reset(sensor_t *sensor)
+{
+    int ret;
+    // Software Reset: clear all registers and reset them to their default values
+    ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xf0);
+    if (ret) {
+        ESP_LOGE(TAG, "Software Reset FAILED!");
+        return ret;
+    }
+    vTaskDelay(100 / portTICK_PERIOD_MS);
+
+    ret = write_regs(sensor->slv_addr, gc032a_default_regs);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Camera defaults loaded");
+        vTaskDelay(100 / portTICK_PERIOD_MS);
+        write_reg(sensor->slv_addr, 0xfe, 0x00);
+        set_reg_bits(sensor->slv_addr, 0xf7, 1, 0x01, 1); // PLL_mode1:div2en
+        set_reg_bits(sensor->slv_addr, 0xf7, 7, 0x01, 1); // PLL_mode1:dvp mode
+        set_reg_bits(sensor->slv_addr, 0xf8, 0, 0x3f, 8); //PLL_mode2 :divx4
+        set_reg_bits(sensor->slv_addr, 0xfa, 4, 0x0f, 2); //vlk div mode :divide_by
+    }
+
+    return ret;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret = 0;
+    switch (pixformat) {
+    case PIXFORMAT_RGB565:
+        write_reg(sensor->slv_addr, 0xfe, 0x00);
+        ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 6);  //RGB565
+        break;
+
+    case PIXFORMAT_YUV422:
+        write_reg(sensor->slv_addr, 0xfe, 0x00);
+        ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 3);
+        break;
+    default:
+        ESP_LOGW(TAG, "unsupport format");
+        ret = -1;
+        break;
+    }
+    if (ret == 0) {
+        sensor->pixformat = pixformat;
+        ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
+    }
+
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    ESP_LOGI(TAG, "set_framesize");
+    int ret = 0;
+    if (framesize > FRAMESIZE_VGA) {
+        ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
+        framesize = FRAMESIZE_VGA;
+    }
+    sensor->status.framesize = framesize;
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+    uint16_t row_s = (resolution[FRAMESIZE_VGA].height - h) / 2;
+    uint16_t col_s = (resolution[FRAMESIZE_VGA].width - w) / 2;
+
+    write_reg(sensor->slv_addr, 0xfe, 0x00);
+    write_reg(sensor->slv_addr, P0_ROW_START_HIGH, H8(row_s)); // Row_start[8]
+    write_reg(sensor->slv_addr, P0_ROW_START_LOW, L8(row_s)); // Row_start[7:0]
+    write_reg(sensor->slv_addr, P0_COLUMN_START_HIGH, H8(col_s)); // Column_start[9:8]
+    write_reg(sensor->slv_addr, P0_COLUMN_START_LOW, L8(col_s)); // Column_start[7:0]
+    write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_HIGH, H8(h + 8)); //window_height [8]
+    write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_LOW, L8(h + 8)); //window_height [7:0]
+    write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_HIGH, H8(w + 8)); //window_width [9:8]
+    write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_LOW, L8(w + 8)); //window_width [7:0]
+
+    write_reg(sensor->slv_addr, P0_WIN_MODE, 0x01);
+    write_reg(sensor->slv_addr, P0_OUT_WIN_HEIGHT_HIGH, H8(h));
+    write_reg(sensor->slv_addr, P0_OUT_WIN_HEIGHT_LOW, L8(h));
+    write_reg(sensor->slv_addr, P0_OUT_WIN_WIDTH_HIGH, H8(w));
+    write_reg(sensor->slv_addr, P0_OUT_WIN_WIDTH_LOW, L8(w));
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
+    }
+    print_regs(sensor->slv_addr);
+    return ret;
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.hmirror = enable;
+    ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor->slv_addr, P0_CISCTL_MODE1, 0, 0x01, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set h-mirror to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.vflip = enable;
+    ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor->slv_addr, P0_CISCTL_MODE1, 1, 0x01, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set v-flip to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor->slv_addr, P0_DEBUG_MODE2, 3, 0x01, enable);
+    if (ret == 0) {
+        sensor->status.colorbar = enable;
+        ESP_LOGD(TAG, "Set colorbar to: %d", enable);
+    }
+    return ret;
+}
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = 0;
+    if (mask > 0xFF) {
+        ESP_LOGE(TAG, "mask should not more than 0xff");
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if (ret > 0) {
+        ret &= mask;
+    }
+    return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0;
+    if (mask > 0xFF) {
+        ESP_LOGE(TAG, "mask should not more than 0xff");
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if (ret < 0) {
+        return ret;
+    }
+    value = (ret & ~mask) | (value & mask);
+
+    if (mask > 0xFF) {
+
+    } else {
+        ret = write_reg(sensor->slv_addr, reg, value);
+    }
+    return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    write_reg(sensor->slv_addr, 0xfe, 0x00);
+    sensor->status.brightness = 0;
+    sensor->status.contrast = 0;
+    sensor->status.saturation = 0;
+    sensor->status.sharpness = 0;
+    sensor->status.denoise = 0;
+    sensor->status.ae_level = 0;
+    sensor->status.gainceiling = 0;
+    sensor->status.awb = 0;
+    sensor->status.dcw = 0;
+    sensor->status.agc = 0;
+    sensor->status.aec = 0;
+    sensor->status.hmirror = check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x01);
+    sensor->status.vflip = check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x02);
+    sensor->status.colorbar = 0;
+    sensor->status.bpc = 0;
+    sensor->status.wpc = 0;
+    sensor->status.raw_gma = 0;
+    sensor->status.lenc = 0;
+    sensor->status.quality = 0;
+    sensor->status.special_effect = 0;
+    sensor->status.wb_mode = 0;
+    sensor->status.awb_gain = 0;
+    sensor->status.agc_gain = 0;
+    sensor->status.aec_value = 0;
+    sensor->status.aec2 = 0;
+    return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val)
+{
+    ESP_LOGW(TAG, "Unsupported");
+    return -1;
+}
+static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
+{
+    ESP_LOGW(TAG, "Unsupported");
+    return -1;
+}
+
+int gc032a_detect(int slv_addr, sensor_id_t *id)
+{
+    if (GC032A_SCCB_ADDR == slv_addr) {
+        uint8_t MIDL = SCCB_Read(slv_addr, SENSOR_ID_LOW);
+        uint8_t MIDH = SCCB_Read(slv_addr, SENSOR_ID_HIGH);
+        uint16_t PID = MIDH << 8 | MIDL;
+        if (GC032A_PID == PID) {
+            id->PID = PID;
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int gc032a_init(sensor_t *sensor)
+{
+    sensor->init_status = init_status;
+    sensor->reset = reset;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_contrast = set_dummy;
+    sensor->set_brightness = set_dummy;
+    sensor->set_saturation = set_dummy;
+    sensor->set_sharpness = set_dummy;
+    sensor->set_denoise = set_dummy;
+    sensor->set_gainceiling = set_gainceiling_dummy;
+    sensor->set_quality = set_dummy;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_whitebal = set_dummy;
+    sensor->set_gain_ctrl = set_dummy;
+    sensor->set_exposure_ctrl = set_dummy;
+    sensor->set_hmirror = set_hmirror;
+    sensor->set_vflip = set_vflip;
+
+    sensor->set_aec2 = set_dummy;
+    sensor->set_awb_gain = set_dummy;
+    sensor->set_agc_gain = set_dummy;
+    sensor->set_aec_value = set_dummy;
+
+    sensor->set_special_effect = set_dummy;
+    sensor->set_wb_mode = set_dummy;
+    sensor->set_ae_level = set_dummy;
+
+    sensor->set_dcw = set_dummy;
+    sensor->set_bpc = set_dummy;
+    sensor->set_wpc = set_dummy;
+
+    sensor->set_raw_gma = set_dummy;
+    sensor->set_lenc = set_dummy;
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_res_raw = NULL;
+    sensor->set_pll = NULL;
+    sensor->set_xclk = NULL;
+
+    ESP_LOGD(TAG, "GC032A Attached");
+    return 0;
+}

+ 477 - 0
std/camera/sensors/gc2145.c

@@ -0,0 +1,477 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "sccb.h"
+#include "gc2145.h"
+#include "gc2145_regs.h"
+#include "gc2145_settings.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char *TAG = "gc2145";
+#endif
+
+#define H8(v) ((v)>>8)
+#define L8(v) ((v)&0xff)
+
+//#define REG_DEBUG_ON
+
+static int read_reg(uint8_t slv_addr, const uint16_t reg)
+{
+    int ret = SCCB_Read(slv_addr, reg);
+#ifdef REG_DEBUG_ON
+    if (ret < 0) {
+        ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
+{
+    int ret = 0;
+#ifndef REG_DEBUG_ON
+    ret = SCCB_Write(slv_addr, reg, value);
+#else
+    int old_value = read_reg(slv_addr, reg);
+    if (old_value < 0) {
+        return old_value;
+    }
+    if ((uint8_t)old_value != value) {
+        ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
+        ret = SCCB_Write(slv_addr, reg, value);
+    } else {
+        ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
+        ret = SCCB_Write(slv_addr, reg, value);//maybe not?
+    }
+    if (ret < 0) {
+        ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
+{
+    return (read_reg(slv_addr, reg) & mask) == mask;
+}
+
+static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
+{
+    int ret = 0;
+    uint8_t c_value, new_value;
+    ret = read_reg(slv_addr, reg);
+    if (ret < 0) {
+        return ret;
+    }
+    c_value = ret;
+    new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
+    ret = write_reg(slv_addr, reg, new_value);
+    return ret;
+}
+
+static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
+{
+    int i = 0, ret = 0;
+    while (!ret && regs[i][0] != REGLIST_TAIL) {
+        if (regs[i][0] == REG_DLY) {
+            vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+        } else {
+            ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
+        }
+        i++;
+    }
+    return ret;
+}
+
+static void print_regs(uint8_t slv_addr)
+{
+#ifdef DEBUG_PRINT_REG
+    vTaskDelay(pdMS_TO_TICKS(100));
+    ESP_LOGI(TAG, "REG list look ======================");
+    for (size_t i = 0xf0; i <= 0xfe; i++) {
+        ESP_LOGI(TAG, "reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    ESP_LOGI(TAG, "\npage 0 ===");
+    write_reg(slv_addr, 0xfe, 0x00); // page 0
+    for (size_t i = 0x03; i <= 0x24; i++) {
+        ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    for (size_t i = 0x80; i <= 0xa2; i++) {
+        ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    ESP_LOGI(TAG, "\npage 3 ===");
+    write_reg(slv_addr, 0xfe, 0x03); // page 3
+    for (size_t i = 0x01; i <= 0x43; i++) {
+        ESP_LOGI(TAG, "p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+#endif
+}
+
+static int reset(sensor_t *sensor)
+{
+    int ret = 0;
+    // Software Reset: clear all registers and reset them to their default values
+    ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xe0);
+    if (ret) {
+        ESP_LOGE(TAG, "Software Reset FAILED!");
+        return ret;
+    }
+    vTaskDelay(100 / portTICK_PERIOD_MS);
+    ret = write_regs(sensor->slv_addr, gc2145_default_init_regs);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Camera defaults loaded");
+        vTaskDelay(100 / portTICK_PERIOD_MS);
+#ifdef CONFIG_IDF_TARGET_ESP32
+        write_reg(sensor->slv_addr, 0xfe, 0x00);
+        //ensure pclk <= 15MHz for esp32
+        set_reg_bits(sensor->slv_addr, 0xf8, 0, 0x3f, 2); // divx4
+        set_reg_bits(sensor->slv_addr, 0xfa, 4, 0x0f, 2); // divide_by
+#endif
+
+    }
+    return ret;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret = 0;
+
+    switch (pixformat) {
+    case PIXFORMAT_RGB565:
+        write_reg(sensor->slv_addr, 0xfe, 0x00);
+        ret = set_reg_bits(sensor->slv_addr, P0_OUTPUT_FORMAT, 0, 0x1f, 6);  //RGB565
+        break;
+
+    case PIXFORMAT_YUV422:
+        write_reg(sensor->slv_addr, 0xfe, 0x00);
+        ret = set_reg_bits(sensor->slv_addr, P0_OUTPUT_FORMAT, 0, 0x1f, 2); //yuv422
+        break;
+    default:
+        ESP_LOGW(TAG, "unsupport format");
+        ret = -1;
+        break;
+    }
+
+    if (ret == 0) {
+        sensor->pixformat = pixformat;
+        ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
+    }
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    int ret = 0;
+    if (framesize > FRAMESIZE_UXGA) {
+        ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
+        framesize = FRAMESIZE_UXGA;
+    }
+    sensor->status.framesize = framesize;
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+    uint16_t row_s = (resolution[FRAMESIZE_UXGA].height - h) / 2;
+    uint16_t col_s = (resolution[FRAMESIZE_UXGA].width - w) / 2;
+    (void)row_s;
+    (void)col_s;
+
+#if CONFIG_GC_SENSOR_SUBSAMPLE_MODE
+    struct subsample_cfg {
+        uint16_t ratio_numerator;
+        uint16_t ratio_denominator;
+        uint8_t reg0x99;
+        uint8_t reg0x9b;
+        uint8_t reg0x9c;
+        uint8_t reg0x9d;
+        uint8_t reg0x9e;
+        uint8_t reg0x9f;
+        uint8_t reg0xa0;
+        uint8_t reg0xa1;
+        uint8_t reg0xa2;
+    };
+    const struct subsample_cfg subsample_cfgs[] = { // define some subsample ratio
+        // {60, 420, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //1/7 // A smaller ratio brings a larger view, but it reduces the frame rate
+        // {84, 420, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //1/5
+        // {105, 420, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/4
+        {140, 420, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/3
+        {210, 420, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/2
+        {240, 420, 0x77, 0x02, 0x46, 0x02, 0x46, 0x02, 0x46, 0x02, 0x46},//4/7
+        {252, 420, 0x55, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04},//3/5
+        {280, 420, 0x33, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00},//2/3
+        {420, 420, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/1
+    };
+    uint16_t win_w = resolution[FRAMESIZE_UXGA].width;
+    uint16_t win_h = resolution[FRAMESIZE_UXGA].height;
+    const struct subsample_cfg *cfg = NULL;
+    /**
+     * Strategy: try to keep the maximum perspective
+     */
+    uint8_t i = 0;
+    if (framesize >= FRAMESIZE_QVGA) {
+        i = 1;
+    }
+    for (; i < sizeof(subsample_cfgs) / sizeof(struct subsample_cfg); i++) {
+        cfg = &subsample_cfgs[i];
+        if ((win_w * cfg->ratio_numerator / cfg->ratio_denominator >= w) && (win_h * cfg->ratio_numerator / cfg->ratio_denominator >= h)) {
+            win_w = w * cfg->ratio_denominator / cfg->ratio_numerator;
+            win_h = h * cfg->ratio_denominator / cfg->ratio_numerator;
+            row_s = (resolution[FRAMESIZE_UXGA].height - win_h) / 2;
+            col_s = (resolution[FRAMESIZE_UXGA].width - win_w) / 2;
+            ESP_LOGI(TAG, "subsample win:%dx%d, ratio:%f", win_w, win_h, (float)cfg->ratio_numerator / (float)cfg->ratio_denominator);
+            break;
+        }
+    }
+
+    write_reg(sensor->slv_addr, 0xfe, 0x00);
+    write_reg(sensor->slv_addr, P0_CROP_ENABLE, 0x01);
+    write_reg(sensor->slv_addr, 0x09, H8(row_s));
+    write_reg(sensor->slv_addr, 0x0a, L8(row_s));
+    write_reg(sensor->slv_addr, 0x0b, H8(col_s));
+    write_reg(sensor->slv_addr, 0x0c, L8(col_s));
+    write_reg(sensor->slv_addr, 0x0d, H8(win_h + 8));
+    write_reg(sensor->slv_addr, 0x0e, L8(win_h + 8));
+    write_reg(sensor->slv_addr, 0x0f, H8(win_w + 16));
+    write_reg(sensor->slv_addr, 0x10, L8(win_w + 16));
+
+    write_reg(sensor->slv_addr, 0x99, cfg->reg0x99);
+    write_reg(sensor->slv_addr, 0x9b, cfg->reg0x9b);
+    write_reg(sensor->slv_addr, 0x9c, cfg->reg0x9c);
+    write_reg(sensor->slv_addr, 0x9d, cfg->reg0x9d);
+    write_reg(sensor->slv_addr, 0x9e, cfg->reg0x9e);
+    write_reg(sensor->slv_addr, 0x9f, cfg->reg0x9f);
+    write_reg(sensor->slv_addr, 0xa0, cfg->reg0xa0);
+    write_reg(sensor->slv_addr, 0xa1, cfg->reg0xa1);
+    write_reg(sensor->slv_addr, 0xa2, cfg->reg0xa2);
+
+    write_reg(sensor->slv_addr, 0x95, H8(h));
+    write_reg(sensor->slv_addr, 0x96, L8(h));
+    write_reg(sensor->slv_addr, 0x97, H8(w));
+    write_reg(sensor->slv_addr, 0x98, L8(w));
+
+
+#elif CONFIG_GC_SENSOR_WINDOWING_MODE
+    write_reg(sensor->slv_addr, 0xfe, 0x00);
+
+    write_reg(sensor->slv_addr, P0_CROP_ENABLE, 0x01);
+    // write_reg(sensor->slv_addr, 0xec, col_s / 8); //measure window
+    // write_reg(sensor->slv_addr, 0xed, row_s / 8);
+    // write_reg(sensor->slv_addr, 0xee, (col_s + h) / 8);
+    // write_reg(sensor->slv_addr, 0xef, (row_s + w) / 8);
+
+    write_reg(sensor->slv_addr, 0x09, H8(row_s));
+    write_reg(sensor->slv_addr, 0x0a, L8(row_s));
+    write_reg(sensor->slv_addr, 0x0b, H8(col_s));
+    write_reg(sensor->slv_addr, 0x0c, L8(col_s));
+    write_reg(sensor->slv_addr, 0x0d, H8(h + 8));
+    write_reg(sensor->slv_addr, 0x0e, L8(h + 8));
+    write_reg(sensor->slv_addr, 0x0f, H8(w + 8));
+    write_reg(sensor->slv_addr, 0x10, L8(w + 8));
+
+    write_reg(sensor->slv_addr, 0x95, H8(h));
+    write_reg(sensor->slv_addr, 0x96, L8(h));
+    write_reg(sensor->slv_addr, 0x97, H8(w));
+    write_reg(sensor->slv_addr, 0x98, L8(w));
+
+#endif
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
+    }
+    return ret;
+
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.hmirror = enable;
+    ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor->slv_addr, P0_ANALOG_MODE1, 0, 0x01, enable != 0);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set h-mirror to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.vflip = enable;
+    ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor->slv_addr, P0_ANALOG_MODE1, 1, 0x01, enable != 0);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set v-flip to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    // ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    // ret |= set_reg_bits(sensor->slv_addr, P0_DEBUG_MODE3, 3, 0x01, enable);
+    if (ret == 0) {
+        sensor->status.colorbar = enable;
+        ESP_LOGD(TAG, "Set colorbar to: %d", enable);
+    }
+    return ret;
+}
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = 0;
+    if (mask > 0xFF) {
+        ESP_LOGE(TAG, "mask should not more than 0xff");
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if (ret > 0) {
+        ret &= mask;
+    }
+    return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0;
+    if (mask > 0xFF) {
+        ESP_LOGE(TAG, "mask should not more than 0xff");
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if (ret < 0) {
+        return ret;
+    }
+    value = (ret & ~mask) | (value & mask);
+
+    if (mask > 0xFF) {
+
+    } else {
+        ret = write_reg(sensor->slv_addr, reg, value);
+    }
+    return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    write_reg(sensor->slv_addr, 0xfe, 0x00);
+    sensor->status.brightness = 0;
+    sensor->status.contrast = 0;
+    sensor->status.saturation = 0;
+    sensor->status.sharpness = 0;
+    sensor->status.denoise = 0;
+    sensor->status.ae_level = 0;
+    sensor->status.gainceiling = 0;
+    sensor->status.awb = 0;
+    sensor->status.dcw = 0;
+    sensor->status.agc = 0;
+    sensor->status.aec = 0;
+    sensor->status.hmirror = check_reg_mask(sensor->slv_addr, P0_ANALOG_MODE1, 0x01);
+    sensor->status.vflip = check_reg_mask(sensor->slv_addr, P0_ANALOG_MODE1, 0x02);
+    sensor->status.colorbar = 0;
+    sensor->status.bpc = 0;
+    sensor->status.wpc = 0;
+    sensor->status.raw_gma = 0;
+    sensor->status.lenc = 0;
+    sensor->status.quality = 0;
+    sensor->status.special_effect = 0;
+    sensor->status.wb_mode = 0;
+    sensor->status.awb_gain = 0;
+    sensor->status.agc_gain = 0;
+    sensor->status.aec_value = 0;
+    sensor->status.aec2 = 0;
+
+    print_regs(sensor->slv_addr);
+    return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val)
+{
+    ESP_LOGW(TAG, "Unsupported");
+    return -1;
+}
+static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
+{
+    ESP_LOGW(TAG, "Unsupported");
+    return -1;
+}
+
+int gc2145_detect(int slv_addr, sensor_id_t *id)
+{
+    if (GC2145_SCCB_ADDR == slv_addr) {
+        uint8_t MIDL = SCCB_Read(slv_addr, CHIP_ID_LOW);
+        uint8_t MIDH = SCCB_Read(slv_addr, CHIP_ID_HIGH);
+        uint16_t PID = MIDH << 8 | MIDL;
+        if (GC2145_PID == PID) {
+            id->PID = PID;
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int gc2145_init(sensor_t *sensor)
+{
+    sensor->init_status = init_status;
+    sensor->reset = reset;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_contrast = set_dummy;
+    sensor->set_brightness = set_dummy;
+    sensor->set_saturation = set_dummy;
+    sensor->set_sharpness = set_dummy;
+    sensor->set_denoise = set_dummy;
+    sensor->set_gainceiling = set_gainceiling_dummy;
+    sensor->set_quality = set_dummy;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_whitebal = set_dummy;
+    sensor->set_gain_ctrl = set_dummy;
+    sensor->set_exposure_ctrl = set_dummy;
+    sensor->set_hmirror = set_hmirror;
+    sensor->set_vflip = set_vflip;
+
+    sensor->set_aec2 = set_dummy;
+    sensor->set_awb_gain = set_dummy;
+    sensor->set_agc_gain = set_dummy;
+    sensor->set_aec_value = set_dummy;
+
+    sensor->set_special_effect = set_dummy;
+    sensor->set_wb_mode = set_dummy;
+    sensor->set_ae_level = set_dummy;
+
+    sensor->set_dcw = set_dummy;
+    sensor->set_bpc = set_dummy;
+    sensor->set_wpc = set_dummy;
+
+    sensor->set_raw_gma = set_dummy;
+    sensor->set_lenc = set_dummy;
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_res_raw = NULL;
+    sensor->set_pll = NULL;
+    sensor->set_xclk = NULL;
+
+    ESP_LOGD(TAG, "GC2145 Attached");
+    return 0;
+}

+ 1022 - 0
std/camera/sensors/nt99141.c

@@ -0,0 +1,1022 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * NT99141 driver.
+ *
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sccb.h"
+#include "xclk.h"
+#include "nt99141.h"
+#include "nt99141_regs.h"
+#include "nt99141_settings.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char *TAG = "NT99141";
+#endif
+
+//#define REG_DEBUG_ON
+
+static int read_reg(uint8_t slv_addr, const uint16_t reg)
+{
+    int ret = SCCB_Read16(slv_addr, reg);
+#ifdef REG_DEBUG_ON
+
+    if (ret < 0) {
+        ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
+    }
+
+#endif
+    return ret;
+}
+
+static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
+{
+    return (read_reg(slv_addr, reg) & mask) == mask;
+}
+
+static int read_reg16(uint8_t slv_addr, const uint16_t reg)
+{
+    int ret = 0, ret2 = 0;
+    ret = read_reg(slv_addr, reg);
+
+    if (ret >= 0) {
+        ret = (ret & 0xFF) << 8;
+        ret2 = read_reg(slv_addr, reg + 1);
+
+        if (ret2 < 0) {
+            ret = ret2;
+        } else {
+            ret |= ret2 & 0xFF;
+        }
+    }
+
+    return ret;
+}
+
+
+static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
+{
+    int ret = 0;
+#ifndef REG_DEBUG_ON
+    ret = SCCB_Write16(slv_addr, reg, value);
+#else
+    int old_value = read_reg(slv_addr, reg);
+
+    if (old_value < 0) {
+        return old_value;
+    }
+
+    if ((uint8_t)old_value != value) {
+        ESP_LOGD(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
+        ret = SCCB_Write16(slv_addr, reg, value);
+    } else {
+        ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
+        ret = SCCB_Write16(slv_addr, reg, value);//maybe not?
+    }
+
+    if (ret < 0) {
+        ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
+    }
+
+#endif
+    return ret;
+}
+
+static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
+{
+    int ret = 0;
+    uint8_t c_value, new_value;
+    ret = read_reg(slv_addr, reg);
+
+    if (ret < 0) {
+        return ret;
+    }
+
+    c_value = ret;
+    new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
+    ret = write_reg(slv_addr, reg, new_value);
+    return ret;
+}
+
+static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
+{
+    int i = 0, ret = 0;
+
+    while (!ret && regs[i][0] != REGLIST_TAIL) {
+        if (regs[i][0] == REG_DLY) {
+            vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+        } else {
+            ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
+        }
+
+        i++;
+    }
+
+    return ret;
+}
+
+static int write_reg16(uint8_t slv_addr, const uint16_t reg, uint16_t value)
+{
+    if (write_reg(slv_addr, reg, value >> 8) || write_reg(slv_addr, reg + 1, value)) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value, uint16_t y_value)
+{
+    if (write_reg16(slv_addr, reg, x_value) || write_reg16(slv_addr, reg + 2, y_value)) {
+        return -1;
+    }
+
+    return 0;
+}
+
+#define write_reg_bits(slv_addr, reg, mask, enable) set_reg_bits(slv_addr, reg, 0, mask, enable?mask:0)
+
+static int set_pll(sensor_t *sensor, bool bypass, uint8_t multiplier, uint8_t sys_div, uint8_t pre_div, bool root_2x, uint8_t seld5, bool pclk_manual, uint8_t pclk_div)
+{
+    return -1;
+}
+
+static int set_ae_level(sensor_t *sensor, int level);
+
+static int reset(sensor_t *sensor)
+{
+
+    int ret = 0;
+    // Software Reset: clear all registers and reset them to their default values
+    ret = write_reg(sensor->slv_addr, SYSTEM_CTROL0, 0x01);
+
+    if (ret) {
+        ESP_LOGE(TAG, "Software Reset FAILED!");
+        return ret;
+    }
+
+    vTaskDelay(100 / portTICK_PERIOD_MS);
+    ret = write_regs(sensor->slv_addr, sensor_default_regs);   //re-initial
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Camera defaults loaded");
+        ret = set_ae_level(sensor, 0);
+        vTaskDelay(100 / portTICK_PERIOD_MS);
+    }
+
+    return ret;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret = 0;
+    const uint16_t (*regs)[2];
+
+    switch (pixformat) {
+        case PIXFORMAT_YUV422:
+            regs = sensor_fmt_yuv422;
+            break;
+
+        case PIXFORMAT_GRAYSCALE:
+            regs = sensor_fmt_grayscale;
+            break;
+
+        case PIXFORMAT_RGB565:
+        case PIXFORMAT_RGB888:
+            regs = sensor_fmt_rgb565;
+            break;
+
+        case PIXFORMAT_JPEG:
+            regs = sensor_fmt_jpeg;
+            break;
+
+        case PIXFORMAT_RAW:
+            regs = sensor_fmt_raw;
+            break;
+
+        default:
+            ESP_LOGE(TAG, "Unsupported pixformat: %u", pixformat);
+            return -1;
+    }
+
+    ret = write_regs(sensor->slv_addr, regs);
+
+    if (ret == 0) {
+        sensor->pixformat = pixformat;
+        ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
+    }
+
+    return ret;
+}
+
+static int set_image_options(sensor_t *sensor)
+{
+    int ret = 0;
+    uint8_t reg20 = 0;
+    uint8_t reg21 = 0;
+    uint8_t reg4514 = 0;
+    uint8_t reg4514_test = 0;
+
+    // V-Flip
+    if (sensor->status.vflip) {
+        reg20 |= 0x01;
+        reg4514_test |= 1;
+    }
+
+    // H-Mirror
+    if (sensor->status.hmirror) {
+        reg21 |= 0x02;
+        reg4514_test |= 2;
+    }
+
+    switch (reg4514_test) {
+
+    }
+
+    if (write_reg(sensor->slv_addr, TIMING_TC_REG20, reg20 | reg21)) {
+        ESP_LOGE(TAG, "Setting Image Options Failed");
+        ret = -1;
+    }
+
+    ESP_LOGD(TAG, "Set Image Options: Compression: %u, Binning: %u, V-Flip: %u, H-Mirror: %u, Reg-4514: 0x%02x",
+             sensor->pixformat == PIXFORMAT_JPEG, sensor->status.binning, sensor->status.vflip, sensor->status.hmirror, reg4514);
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    int ret = 0;
+
+    sensor->status.framesize = framesize;
+    ret = write_regs(sensor->slv_addr, sensor_default_regs);
+
+    if (framesize == FRAMESIZE_QVGA) {
+        ESP_LOGD(TAG, "Set FRAMESIZE_QVGA");
+        ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA);
+#if    CONFIG_NT99141_SUPPORT_XSKIP
+        ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: xskip mode");
+        ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA_xskip);
+#elif  CONFIG_NT99141_SUPPORT_CROP
+        ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: crop mode");
+        ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA_crop);
+#endif
+    } else if (framesize == FRAMESIZE_VGA) {
+        ESP_LOGD(TAG, "Set FRAMESIZE_VGA");
+        // ret = write_regs(sensor->slv_addr, sensor_framesize_VGA);
+        ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_xyskip);// Resolution:640*360 This configuration is equally-scaled without deforming
+#ifdef CONFIG_NT99141_SUPPORT_XSKIP
+        ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: xskip mode");
+        ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_xskip);
+#elif CONFIG_NT99141_SUPPORT_CROP
+        ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: crop mode");
+        ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_crop);
+#endif
+    } else if (framesize >= FRAMESIZE_HD) {
+        ESP_LOGD(TAG, "Set FRAMESIZE_HD");
+        ret = write_regs(sensor->slv_addr, sensor_framesize_HD);
+    } else {
+        ESP_LOGD(TAG, "Dont suppost this size, Set FRAMESIZE_VGA");
+        ret = write_regs(sensor->slv_addr, sensor_framesize_VGA);
+    }
+
+    return ret;
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.hmirror = enable;
+    ret = set_image_options(sensor);
+
+    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 = set_image_options(sensor);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set v-flip to: %d", enable);
+    }
+
+    return ret;
+}
+
+static int set_quality(sensor_t *sensor, int qs)
+{
+    int ret = 0;
+    ret = write_reg(sensor->slv_addr, COMPRESSION_CTRL07, qs & 0x3f);
+
+    if (ret == 0) {
+        sensor->status.quality = qs;
+        ESP_LOGD(TAG, "Set quality to: %d", qs);
+    }
+
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, PRE_ISP_TEST_SETTING_1, TEST_COLOR_BAR, enable);
+
+    if (ret == 0) {
+        sensor->status.colorbar = enable;
+        ESP_LOGD(TAG, "Set colorbar to: %d", enable);
+    }
+
+    return ret;
+}
+
+static int set_gain_ctrl(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x32bb, 0x87, enable);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set gain_ctrl to: %d", enable);
+        sensor->status.agc = enable;
+    }
+
+    return ret;
+}
+
+static int set_exposure_ctrl(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+       int data = 0;
+    // ret = write_reg_bits(sensor->slv_addr, 0x32bb, 0x87, enable);
+    data = read_reg(sensor->slv_addr, 0x3201);
+    ESP_LOGD(TAG, "set_exposure_ctrl:enable");
+    if (enable) {
+        ESP_LOGD(TAG, "set_exposure_ctrl:enable");
+        ret = write_reg(sensor->slv_addr, 0x3201, (1 << 5) | data);
+    } else {
+        ESP_LOGD(TAG, "set_exposure_ctrl:disable");
+        ret = write_reg(sensor->slv_addr, 0x3201, (~(1 << 5)) & data);
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set exposure_ctrl to: %d", enable);
+        sensor->status.aec = enable;
+    }
+
+    return ret;
+}
+
+static int set_whitebal(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set awb to: %d", enable);
+        sensor->status.awb = enable;
+    }
+
+    return ret;
+}
+
+//Advanced AWB
+static int set_dcw_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set dcw to: %d", enable);
+        sensor->status.dcw = enable;
+    }
+
+    return ret;
+}
+
+//night mode enable
+static int set_aec2(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set aec2 to: %d", enable);
+        sensor->status.aec2 = enable;
+    }
+
+    return ret;
+}
+
+static int set_bpc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set bpc to: %d", enable);
+        sensor->status.bpc = enable;
+    }
+
+    return ret;
+}
+
+static int set_wpc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set wpc to: %d", enable);
+        sensor->status.wpc = enable;
+    }
+
+    return ret;
+}
+
+//Gamma enable
+static int set_raw_gma_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set raw_gma to: %d", enable);
+        sensor->status.raw_gma = enable;
+    }
+
+    return ret;
+}
+
+static int set_lenc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set lenc to: %d", enable);
+        sensor->status.lenc = enable;
+    }
+
+    return ret;
+}
+
+static int get_agc_gain(sensor_t *sensor)
+{
+    ESP_LOGD(TAG, "get_agc_gain can not be configured at present");
+    return 0;
+}
+
+//real gain
+static int set_agc_gain(sensor_t *sensor, int gain)
+{
+    ESP_LOGD(TAG, "set_agc_gain can not be configured at present");
+    // ESP_LOGD(TAG, "GAIN = %d\n", gain);
+    int cnt = gain / 2;
+
+    switch (cnt) {
+        case 0:
+            ESP_LOGD(TAG, "set_agc_gain: 1x");
+            write_reg(sensor->slv_addr, 0X301D, 0X00);
+            break;
+
+        case 1:
+            ESP_LOGD(TAG,"set_agc_gain: 2x");
+            write_reg(sensor->slv_addr, 0X301D, 0X0F);
+            break;
+
+        case 2:
+            ESP_LOGD(TAG,"set_agc_gain: 4x");
+            write_reg(sensor->slv_addr, 0X301D, 0X2F);
+            break;
+
+        case 3:
+            ESP_LOGD(TAG,"set_agc_gain: 6x");
+            write_reg(sensor->slv_addr, 0X301D, 0X37);
+            break;
+
+        case 4:
+            ESP_LOGD(TAG,"set_agc_gain: 8x");
+            write_reg(sensor->slv_addr, 0X301D, 0X3F);
+            break;
+
+        default:
+            ESP_LOGD(TAG,"fail set_agc_gain");
+            break;
+    }
+
+    return 0;
+}
+
+static int get_aec_value(sensor_t *sensor)
+{
+    ESP_LOGD(TAG, "get_aec_value can not be configured at present");
+    return 0;
+}
+
+static int set_aec_value(sensor_t *sensor, int value)
+{
+    ESP_LOGD(TAG, "set_aec_value can not be configured at present");
+    int ret = 0;
+    // ESP_LOGD(TAG, " set_aec_value to: %d", value);
+    ret = write_reg_bits(sensor->slv_addr, 0x3012, 0x00, (value >> 8) & 0xff);
+    ret = write_reg_bits(sensor->slv_addr, 0x3013, 0x01, value & 0xff);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, " set_aec_value to: %d", value);
+        // sensor->status.aec = enable;
+    }
+
+    return ret;
+}
+
+static int set_ae_level(sensor_t *sensor, int level)
+{
+    ESP_LOGD(TAG, "set_ae_level can not be configured at present");
+    int ret = 0;
+
+    if (level < 0) {
+        level = 0;
+    } else if (level > 9) {
+        level = 9;
+    }
+
+    for (int i = 0; i < 5; i++) {
+        ret += write_reg(sensor->slv_addr, sensor_ae_level[ 5 * level + i ][0], sensor_ae_level[5 * level + i ][1]);
+    }
+
+    if (ret) {
+        ESP_LOGE(TAG, " fail to set ae level: %d", ret);
+    }
+
+    return 0;
+}
+
+static int set_wb_mode(sensor_t *sensor, int mode)
+{
+    int ret = 0;
+
+    if (mode < 0 || mode > 4) {
+        return -1;
+    }
+
+    ret = write_reg(sensor->slv_addr, 0x3201, (mode != 0));
+
+    if (ret) {
+        return ret;
+    }
+
+    switch (mode) {
+        case 1://Sunny
+            ret  = write_reg16(sensor->slv_addr, 0x3290, 0x01)
+                   || write_reg16(sensor->slv_addr, 0x3291, 0x38)
+                   || write_reg16(sensor->slv_addr, 0x3296, 0x01)
+                   || write_reg16(sensor->slv_addr, 0x3297, 0x68)
+                   || write_reg16(sensor->slv_addr, 0x3060, 0x01);
+
+            break;
+
+        case 2://Cloudy
+
+            ret  = write_reg16(sensor->slv_addr, 0x3290, 0x01)
+                   || write_reg16(sensor->slv_addr, 0x3291, 0x51)
+                   || write_reg16(sensor->slv_addr, 0x3296, 0x01)
+                   || write_reg16(sensor->slv_addr, 0x3297, 0x00)
+                   || write_reg16(sensor->slv_addr, 0x3060, 0x01);
+            break;
+
+        case 3://INCANDESCENCE]
+            ret  = write_reg16(sensor->slv_addr, 0x3290, 0x01)
+                   || write_reg16(sensor->slv_addr, 0x3291, 0x30)
+                   || write_reg16(sensor->slv_addr, 0x3296, 0x01)
+                   || write_reg16(sensor->slv_addr, 0x3297, 0xCB)
+                   || write_reg16(sensor->slv_addr, 0x3060, 0x01);
+            break;
+
+        case 4://FLUORESCENT
+            ret  = write_reg16(sensor->slv_addr, 0x3290, 0x01)
+                   || write_reg16(sensor->slv_addr, 0x3291, 0x70)
+                   || write_reg16(sensor->slv_addr, 0x3296, 0x01)
+                   || write_reg16(sensor->slv_addr, 0x3297, 0xFF)
+                   || write_reg16(sensor->slv_addr, 0x3060, 0x01);
+            break;
+
+        default://AUTO
+            break;
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set wb_mode to: %d", mode);
+        sensor->status.wb_mode = mode;
+    }
+
+    return ret;
+}
+
+static int set_awb_gain_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    int old_mode = sensor->status.wb_mode;
+    int mode = enable ? old_mode : 0;
+
+    ret = set_wb_mode(sensor, mode);
+
+    if (ret == 0) {
+        sensor->status.wb_mode = old_mode;
+        ESP_LOGD(TAG, "Set awb_gain to: %d", enable);
+        sensor->status.awb_gain = enable;
+    }
+
+    return ret;
+}
+
+static int set_special_effect(sensor_t *sensor, int effect)
+{
+    int ret = 0;
+
+    if (effect < 0 || effect > 6) {
+        return -1;
+    }
+
+    uint8_t *regs = (uint8_t *)sensor_special_effects[effect];
+    ret =  write_reg(sensor->slv_addr, 0x32F1, regs[0])
+           || write_reg(sensor->slv_addr, 0x32F4, regs[1])
+           || write_reg(sensor->slv_addr, 0x32F5, regs[2])
+           || write_reg(sensor->slv_addr, 0x3060, regs[3]);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set special_effect to: %d", effect);
+        sensor->status.special_effect = effect;
+    }
+
+    return ret;
+}
+
+static int set_brightness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    uint8_t value = 0;
+
+    switch (level) {
+        case 3:
+            value = 0xA0;
+            break;
+
+        case 2:
+            value = 0x90;
+            break;
+
+        case 1:
+            value = 0x88;
+            break;
+
+        case -1:
+            value = 0x78;
+            break;
+
+        case -2:
+            value = 0x70;
+            break;
+
+        case -3:
+            value = 0x60;
+            break;
+
+        default: // 0
+            break;
+    }
+
+    ret = write_reg(sensor->slv_addr, 0x32F2, value);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set brightness to: %d", level);
+        sensor->status.brightness = level;
+    }
+
+    return ret;
+}
+
+static int set_contrast(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    uint8_t value1 = 0, value2 = 0 ;
+
+    switch (level) {
+        case 3:
+            value1 = 0xD0;
+            value2 = 0xB0;
+            break;
+
+        case 2:
+            value1 = 0xE0;
+            value2 = 0xA0;
+            break;
+
+        case 1:
+            value1 = 0xF0;
+            value2 = 0x90;
+            break;
+
+        case 0:
+            value1 = 0x00;
+            value2 = 0x80;
+            break;
+
+        case -1:
+            value1 = 0x10;
+            value2 = 0x70;
+            break;
+
+        case -2:
+            value1 = 0x20;
+            value2 = 0x60;
+            break;
+
+        case -3:
+            value1 = 0x30;
+            value2 = 0x50;
+            break;
+
+        default: // 0
+            break;
+    }
+
+    ret = write_reg(sensor->slv_addr, 0x32FC, value1);
+    ret = write_reg(sensor->slv_addr, 0x32F2, value2);
+    ret = write_reg(sensor->slv_addr, 0x3060, 0x01);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set contrast to: %d", level);
+        sensor->status.contrast = level;
+    }
+
+    return ret;
+}
+
+static int set_saturation(sensor_t *sensor, int level)
+{
+    int ret = 0;
+
+    if (level > 4 || level < -4) {
+        return -1;
+    }
+
+    uint8_t *regs = (uint8_t *)sensor_saturation_levels[level + 4];
+    {
+        ret = write_reg(sensor->slv_addr, 0x32F3, regs[0]);
+
+        if (ret) {
+            return ret;
+        }
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set saturation to: %d", level);
+        sensor->status.saturation = level;
+    }
+
+    return ret;
+}
+
+static int set_sharpness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+
+    if (level > 3 || level < -3) {
+        return -1;
+    }
+
+    uint8_t mt_offset_2 = (level + 3) * 8;
+    uint8_t mt_offset_1 = mt_offset_2 + 1;
+
+    ret = write_reg_bits(sensor->slv_addr, 0x5308, 0x40, false)//0x40 means auto
+          || write_reg(sensor->slv_addr, 0x5300, 0x10)
+          || write_reg(sensor->slv_addr, 0x5301, 0x10)
+          || write_reg(sensor->slv_addr, 0x5302, mt_offset_1)
+          || write_reg(sensor->slv_addr, 0x5303, mt_offset_2)
+          || write_reg(sensor->slv_addr, 0x5309, 0x10)
+          || write_reg(sensor->slv_addr, 0x530a, 0x10)
+          || write_reg(sensor->slv_addr, 0x530b, 0x04)
+          || write_reg(sensor->slv_addr, 0x530c, 0x06);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set sharpness to: %d", level);
+        sensor->status.sharpness = level;
+    }
+
+    return ret;
+}
+
+static int set_gainceiling(sensor_t *sensor, gainceiling_t level)
+{
+    ESP_LOGD(TAG, "set_gainceiling can not be configured at present");
+    return 0;
+}
+
+static int get_denoise(sensor_t *sensor)
+{
+
+    return (read_reg(sensor->slv_addr, 0x5306) / 4) + 1;
+}
+
+static int set_denoise(sensor_t *sensor, int level)
+{
+    ESP_LOGD(TAG, "set_denoise can not be configured at present");
+    return 0;
+}
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = 0, ret2 = 0;
+
+    if (mask > 0xFF) {
+        ret = read_reg16(sensor->slv_addr, reg);
+
+        if (ret >= 0 && mask > 0xFFFF) {
+            ret2 = read_reg(sensor->slv_addr, reg + 2);
+
+            if (ret2 >= 0) {
+                ret = (ret << 8) | ret2 ;
+            } else {
+                ret = ret2;
+            }
+        }
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+
+    if (ret > 0) {
+        ret &= mask;
+    }
+
+    return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0, ret2 = 0;
+
+    if (mask > 0xFF) {
+        ret = read_reg16(sensor->slv_addr, reg);
+
+        if (ret >= 0 && mask > 0xFFFF) {
+            ret2 = read_reg(sensor->slv_addr, reg + 2);
+
+            if (ret2 >= 0) {
+                ret = (ret << 8) | ret2 ;
+            } else {
+                ret = ret2;
+            }
+        }
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+
+    if (ret < 0) {
+        return ret;
+    }
+
+    value = (ret & ~mask) | (value & mask);
+
+    if (mask > 0xFFFF) {
+        ret = write_reg16(sensor->slv_addr, reg, value >> 8);
+
+        if (ret >= 0) {
+            ret = write_reg(sensor->slv_addr, reg + 2, value & 0xFF);
+        }
+    } else if (mask > 0xFF) {
+        ret = write_reg16(sensor->slv_addr, reg, value);
+    } else {
+        ret = write_reg(sensor->slv_addr, reg, value);
+    }
+
+    return ret;
+}
+
+static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning)
+{
+    int ret = 0;
+    ret  = write_addr_reg(sensor->slv_addr, X_ADDR_ST_H, startX, startY)
+           || write_addr_reg(sensor->slv_addr, X_ADDR_END_H, endX, endY)
+           || write_addr_reg(sensor->slv_addr, X_OFFSET_H, offsetX, offsetY)
+           || write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, totalX, totalY)
+           || write_addr_reg(sensor->slv_addr, X_OUTPUT_SIZE_H, outputX, outputY);
+
+    if (!ret) {
+        sensor->status.scale = scale;
+        sensor->status.binning = binning;
+        ret = set_image_options(sensor);
+    }
+
+    return ret;
+}
+
+static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div)
+{
+    return set_pll(sensor, bypass > 0, multiplier, sys_div, pre_div, root_2x > 0, seld5, pclk_manual > 0, pclk_div);
+}
+
+static int set_xclk(sensor_t *sensor, int timer, int xclk)
+{
+    int ret = 0;
+    if (xclk > 10)
+    {
+        ESP_LOGE(TAG, "only XCLK under 10MHz is supported, and XCLK is now set to 10M");
+        xclk = 10;
+    }
+    sensor->xclk_freq_hz = xclk * 1000000U;
+    ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
+    return ret;
+}
+
+int nt99141_detect(int slv_addr, sensor_id_t *id)
+{
+    if (NT99141_SCCB_ADDR == slv_addr) {
+        SCCB_Write16(slv_addr, 0x3008, 0x01);//bank sensor
+        uint16_t h = SCCB_Read16(slv_addr, 0x3000);
+        uint16_t l = SCCB_Read16(slv_addr, 0x3001);
+        uint16_t PID = (h<<8) | l;
+        if (NT99141_PID == PID) {
+            id->PID = PID;
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    sensor->status.brightness = 0;
+    sensor->status.contrast = 0;
+    sensor->status.saturation = 0;
+    sensor->status.sharpness = (read_reg(sensor->slv_addr, 0x3301));
+    sensor->status.denoise = get_denoise(sensor);
+    sensor->status.ae_level = 0;
+    sensor->status.gainceiling = read_reg16(sensor->slv_addr, 0x32F0) & 0xFF;
+    sensor->status.awb = check_reg_mask(sensor->slv_addr, ISP_CONTROL_01, 0x10);
+    sensor->status.dcw = !check_reg_mask(sensor->slv_addr, 0x5183, 0x80);
+    sensor->status.agc = !check_reg_mask(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AGC_MANUALEN);
+    sensor->status.aec = !check_reg_mask(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AEC_MANUALEN);
+    sensor->status.hmirror = check_reg_mask(sensor->slv_addr, TIMING_TC_REG21, TIMING_TC_REG21_HMIRROR);
+    sensor->status.vflip = check_reg_mask(sensor->slv_addr, TIMING_TC_REG20, TIMING_TC_REG20_VFLIP);
+    sensor->status.colorbar = check_reg_mask(sensor->slv_addr, PRE_ISP_TEST_SETTING_1, TEST_COLOR_BAR);
+    sensor->status.bpc = check_reg_mask(sensor->slv_addr, 0x5000, 0x04);
+    sensor->status.wpc = check_reg_mask(sensor->slv_addr, 0x5000, 0x02);
+    sensor->status.raw_gma = check_reg_mask(sensor->slv_addr, 0x5000, 0x20);
+    sensor->status.lenc = check_reg_mask(sensor->slv_addr, 0x5000, 0x80);
+    sensor->status.quality = read_reg(sensor->slv_addr, COMPRESSION_CTRL07) & 0x3f;
+    sensor->status.special_effect = 0;
+    sensor->status.wb_mode = 0;
+    sensor->status.awb_gain = check_reg_mask(sensor->slv_addr, 0x3000, 0x01);
+    sensor->status.agc_gain = get_agc_gain(sensor);
+    sensor->status.aec_value = get_aec_value(sensor);
+    sensor->status.aec2 = check_reg_mask(sensor->slv_addr, 0x3000, 0x04);
+    return 0;
+}
+
+int nt99141_init(sensor_t *sensor)
+{
+    sensor->reset = reset;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_contrast = set_contrast;
+    sensor->set_brightness = set_brightness;
+    sensor->set_saturation = set_saturation;
+    sensor->set_sharpness = set_sharpness;
+    sensor->set_gainceiling = set_gainceiling;
+    sensor->set_quality = set_quality;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_gain_ctrl = set_gain_ctrl;
+    sensor->set_exposure_ctrl = set_exposure_ctrl;
+    sensor->set_whitebal = set_whitebal;
+    sensor->set_hmirror = set_hmirror;
+    sensor->set_vflip = set_vflip;
+    sensor->init_status = init_status;
+    sensor->set_aec2 = set_aec2;
+    sensor->set_aec_value = set_aec_value;
+    sensor->set_special_effect = set_special_effect;
+    sensor->set_wb_mode = set_wb_mode;
+    sensor->set_ae_level = set_ae_level;
+    sensor->set_dcw = set_dcw_dsp;
+    sensor->set_bpc = set_bpc_dsp;
+    sensor->set_wpc = set_wpc_dsp;
+    sensor->set_awb_gain = set_awb_gain_dsp;
+    sensor->set_agc_gain = set_agc_gain;
+    sensor->set_raw_gma = set_raw_gma_dsp;
+    sensor->set_lenc = set_lenc_dsp;
+    sensor->set_denoise = set_denoise;
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_res_raw = set_res_raw;
+    sensor->set_pll = _set_pll;
+    sensor->set_xclk = set_xclk;
+    return 0;
+}

+ 612 - 0
std/camera/sensors/ov2640.c

@@ -0,0 +1,612 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV2640 driver.
+ *
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sccb.h"
+#include "xclk.h"
+#include "ov2640.h"
+#include "ov2640_regs.h"
+#include "ov2640_settings.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char* TAG = "ov2640";
+#endif
+
+static volatile ov2640_bank_t reg_bank = BANK_MAX;
+static int set_bank(sensor_t *sensor, ov2640_bank_t bank)
+{
+    int res = 0;
+    if (bank != reg_bank) {
+        reg_bank = bank;
+        res = SCCB_Write(sensor->slv_addr, BANK_SEL, bank);
+    }
+    return res;
+}
+
+static int write_regs(sensor_t *sensor, const uint8_t (*regs)[2])
+{
+    int i=0, res = 0;
+    while (regs[i][0]) {
+        if (regs[i][0] == BANK_SEL) {
+            res = set_bank(sensor, regs[i][1]);
+        } else {
+            res = SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
+        }
+        if (res) {
+            return res;
+        }
+        i++;
+    }
+    return res;
+}
+
+static int write_reg(sensor_t *sensor, ov2640_bank_t bank, uint8_t reg, uint8_t value)
+{
+    int ret = set_bank(sensor, bank);
+    if(!ret) {
+        ret = SCCB_Write(sensor->slv_addr, reg, value);
+    }
+    return ret;
+}
+
+static int set_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t offset, uint8_t mask, uint8_t value)
+{
+    int ret = 0;
+    uint8_t c_value, new_value;
+
+    ret = set_bank(sensor, bank);
+    if(ret) {
+        return ret;
+    }
+    c_value = SCCB_Read(sensor->slv_addr, reg);
+    new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
+    ret = SCCB_Write(sensor->slv_addr, reg, new_value);
+    return ret;
+}
+
+static int read_reg(sensor_t *sensor, ov2640_bank_t bank, uint8_t reg)
+{
+    if(set_bank(sensor, bank)){
+        return 0;
+    }
+    return SCCB_Read(sensor->slv_addr, reg);
+}
+
+static uint8_t get_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t offset, uint8_t mask)
+{
+    return (read_reg(sensor, bank, reg) >> offset) & mask;
+}
+
+static int write_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t mask, int enable)
+{
+    return set_reg_bits(sensor, bank, reg, 0, mask, enable?mask:0);
+}
+
+#define WRITE_REGS_OR_RETURN(regs) ret = write_regs(sensor, regs); if(ret){return ret;}
+#define WRITE_REG_OR_RETURN(bank, reg, val) ret = write_reg(sensor, bank, reg, val); if(ret){return ret;}
+#define SET_REG_BITS_OR_RETURN(bank, reg, offset, mask, val) ret = set_reg_bits(sensor, bank, reg, offset, mask, val); if(ret){return ret;}
+
+static int reset(sensor_t *sensor)
+{
+    int ret = 0;
+    WRITE_REG_OR_RETURN(BANK_SENSOR, COM7, COM7_SRST);
+    vTaskDelay(10 / portTICK_PERIOD_MS);
+    WRITE_REGS_OR_RETURN(ov2640_settings_cif);
+    return ret;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret = 0;
+    sensor->pixformat = pixformat;
+    switch (pixformat) {
+    case PIXFORMAT_RGB565:
+    case PIXFORMAT_RGB888:
+        WRITE_REGS_OR_RETURN(ov2640_settings_rgb565);
+        break;
+    case PIXFORMAT_YUV422:
+    case PIXFORMAT_GRAYSCALE:
+        WRITE_REGS_OR_RETURN(ov2640_settings_yuv422);
+        break;
+    case PIXFORMAT_JPEG:
+        WRITE_REGS_OR_RETURN(ov2640_settings_jpeg3);
+        break;
+    default:
+        ret = -1;
+        break;
+    }
+    if(!ret) {
+        vTaskDelay(10 / portTICK_PERIOD_MS);
+    }
+
+    return ret;
+}
+
+static int set_window(sensor_t *sensor, ov2640_sensor_mode_t mode, int offset_x, int offset_y, int max_x, int max_y, int w, int h){
+    int ret = 0;
+    const uint8_t (*regs)[2];
+    ov2640_clk_t c;
+    c.reserved = 0;
+
+    max_x /= 4;
+    max_y /= 4;
+    w /= 4;
+    h /= 4;
+    uint8_t win_regs[][2] = {
+        {BANK_SEL, BANK_DSP},
+        {HSIZE, max_x & 0xFF},
+        {VSIZE, max_y & 0xFF},
+        {XOFFL, offset_x & 0xFF},
+        {YOFFL, offset_y & 0xFF},
+        {VHYX, ((max_y >> 1) & 0X80) | ((offset_y >> 4) & 0X70) | ((max_x >> 5) & 0X08) | ((offset_x >> 8) & 0X07)},
+        {TEST, (max_x >> 2) & 0X80},
+        {ZMOW, (w)&0xFF},
+        {ZMOH, (h)&0xFF},
+        {ZMHH, ((h>>6)&0x04)|((w>>8)&0x03)},
+        {0, 0}
+    };
+
+    if (sensor->pixformat == PIXFORMAT_JPEG) {
+        c.clk_2x = 0;
+        c.clk_div = 0;
+        c.pclk_auto = 0;
+        c.pclk_div = 8;
+        if(mode == OV2640_MODE_UXGA) {
+            c.pclk_div = 12;
+        }
+        // if (sensor->xclk_freq_hz == 16000000) {
+        //     c.pclk_div = c.pclk_div / 2;
+        // }
+    } else {
+#if CONFIG_IDF_TARGET_ESP32
+        c.clk_2x = 0;
+#else
+        c.clk_2x = 1;
+#endif
+        c.clk_div = 7;
+        c.pclk_auto = 1;
+        c.pclk_div = 8;
+        if (mode == OV2640_MODE_CIF) {
+            c.clk_div = 3;
+        } else if(mode == OV2640_MODE_UXGA) {
+            c.pclk_div = 12;
+        }
+    }
+    ESP_LOGI(TAG, "Set PLL: clk_2x: %u, clk_div: %u, pclk_auto: %u, pclk_div: %u", c.clk_2x, c.clk_div, c.pclk_auto, c.pclk_div);
+
+    if (mode == OV2640_MODE_CIF) {
+        regs = ov2640_settings_to_cif;
+    } else if (mode == OV2640_MODE_SVGA) {
+        regs = ov2640_settings_to_svga;
+    } else {
+        regs = ov2640_settings_to_uxga;
+    }
+
+    WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_BYPAS);
+    WRITE_REGS_OR_RETURN(regs);
+    WRITE_REGS_OR_RETURN(win_regs);
+    WRITE_REG_OR_RETURN(BANK_SENSOR, CLKRC, c.clk);
+    WRITE_REG_OR_RETURN(BANK_DSP, R_DVP_SP, c.pclk);
+    WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_EN);
+
+    vTaskDelay(10 / portTICK_PERIOD_MS);
+    //required when changing resolution
+    set_pixformat(sensor, sensor->pixformat);
+
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    int ret = 0;
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+    aspect_ratio_t ratio = resolution[framesize].aspect_ratio;
+    uint16_t max_x = ratio_table[ratio].max_x;
+    uint16_t max_y = ratio_table[ratio].max_y;
+    uint16_t offset_x = ratio_table[ratio].offset_x;
+    uint16_t offset_y = ratio_table[ratio].offset_y;
+    ov2640_sensor_mode_t mode = OV2640_MODE_UXGA;
+
+    sensor->status.framesize = framesize;
+
+
+
+    if (framesize <= FRAMESIZE_CIF) {
+        mode = OV2640_MODE_CIF;
+        max_x /= 4;
+        max_y /= 4;
+        offset_x /= 4;
+        offset_y /= 4;
+        if(max_y > 296){
+            max_y = 296;
+        }
+    } else if (framesize <= FRAMESIZE_SVGA) {
+        mode = OV2640_MODE_SVGA;
+        max_x /= 2;
+        max_y /= 2;
+        offset_x /= 2;
+        offset_y /= 2;
+    }
+
+    ret = set_window(sensor, mode, offset_x, offset_y, max_x, max_y, w, h);
+    return ret;
+}
+
+static int set_contrast(sensor_t *sensor, int level)
+{
+    int ret=0;
+    level += 3;
+    if (level <= 0 || level > NUM_CONTRAST_LEVELS) {
+        return -1;
+    }
+    sensor->status.contrast = level-3;
+    for (int i=0; i<7; i++) {
+        WRITE_REG_OR_RETURN(BANK_DSP, contrast_regs[0][i], contrast_regs[level][i]);
+    }
+    return ret;
+}
+
+static int set_brightness(sensor_t *sensor, int level)
+{
+    int ret=0;
+    level += 3;
+    if (level <= 0 || level > NUM_BRIGHTNESS_LEVELS) {
+        return -1;
+    }
+    sensor->status.brightness = level-3;
+    for (int i=0; i<5; i++) {
+        WRITE_REG_OR_RETURN(BANK_DSP, brightness_regs[0][i], brightness_regs[level][i]);
+    }
+    return ret;
+}
+
+static int set_saturation(sensor_t *sensor, int level)
+{
+    int ret=0;
+    level += 3;
+    if (level <= 0 || level > NUM_SATURATION_LEVELS) {
+        return -1;
+    }
+    sensor->status.saturation = level-3;
+    for (int i=0; i<5; i++) {
+        WRITE_REG_OR_RETURN(BANK_DSP, saturation_regs[0][i], saturation_regs[level][i]);
+    }
+    return ret;
+}
+
+static int set_special_effect(sensor_t *sensor, int effect)
+{
+    int ret=0;
+    effect++;
+    if (effect <= 0 || effect > NUM_SPECIAL_EFFECTS) {
+        return -1;
+    }
+    sensor->status.special_effect = effect-1;
+    for (int i=0; i<5; i++) {
+        WRITE_REG_OR_RETURN(BANK_DSP, special_effects_regs[0][i], special_effects_regs[effect][i]);
+    }
+    return ret;
+}
+
+static int set_wb_mode(sensor_t *sensor, int mode)
+{
+    int ret=0;
+    if (mode < 0 || mode > NUM_WB_MODES) {
+        return -1;
+    }
+    sensor->status.wb_mode = mode;
+    SET_REG_BITS_OR_RETURN(BANK_DSP, 0XC7, 6, 1, mode?1:0);
+    if(mode) {
+        for (int i=0; i<3; i++) {
+            WRITE_REG_OR_RETURN(BANK_DSP, wb_modes_regs[0][i], wb_modes_regs[mode][i]);
+        }
+    }
+    return ret;
+}
+
+static int set_ae_level(sensor_t *sensor, int level)
+{
+    int ret=0;
+    level += 3;
+    if (level <= 0 || level > NUM_AE_LEVELS) {
+        return -1;
+    }
+    sensor->status.ae_level = level-3;
+    for (int i=0; i<3; i++) {
+        WRITE_REG_OR_RETURN(BANK_SENSOR, ae_levels_regs[0][i], ae_levels_regs[level][i]);
+    }
+    return ret;
+}
+
+static int set_quality(sensor_t *sensor, int quality)
+{
+    if(quality < 0) {
+        quality = 0;
+    } else if(quality > 63) {
+        quality = 63;
+    }
+    sensor->status.quality = quality;
+    return write_reg(sensor, BANK_DSP, QS, quality);
+}
+
+static int set_agc_gain(sensor_t *sensor, int gain)
+{
+    if(gain < 0) {
+        gain = 0;
+    } else if(gain > 30) {
+        gain = 30;
+    }
+    sensor->status.agc_gain = gain;
+    return write_reg(sensor, BANK_SENSOR, GAIN, agc_gain_tbl[gain]);
+}
+
+static int set_gainceiling_sensor(sensor_t *sensor, gainceiling_t gainceiling)
+{
+    sensor->status.gainceiling = gainceiling;
+    //return write_reg(sensor, BANK_SENSOR, COM9, COM9_AGC_SET(gainceiling));
+    return set_reg_bits(sensor, BANK_SENSOR, COM9, 5, 7, gainceiling);
+}
+
+static int set_aec_value(sensor_t *sensor, int value)
+{
+    if(value < 0) {
+        value = 0;
+    } else if(value > 1200) {
+        value = 1200;
+    }
+    sensor->status.aec_value = value;
+    return set_reg_bits(sensor, BANK_SENSOR, REG04, 0, 3, value & 0x3)
+           || write_reg(sensor, BANK_SENSOR, AEC, (value >> 2) & 0xFF)
+           || set_reg_bits(sensor, BANK_SENSOR, REG45, 0, 0x3F, value >> 10);
+}
+
+static int set_aec2(sensor_t *sensor, int enable)
+{
+    sensor->status.aec2 = enable;
+    return set_reg_bits(sensor, BANK_DSP, CTRL0, 6, 1, enable?0:1);
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+    sensor->status.colorbar = enable;
+    return write_reg_bits(sensor, BANK_SENSOR, COM7, COM7_COLOR_BAR, enable?1:0);
+}
+
+static int set_agc_sensor(sensor_t *sensor, int enable)
+{
+    sensor->status.agc = enable;
+    return write_reg_bits(sensor, BANK_SENSOR, COM8, COM8_AGC_EN, enable?1:0);
+}
+
+static int set_aec_sensor(sensor_t *sensor, int enable)
+{
+    sensor->status.aec = enable;
+    return write_reg_bits(sensor, BANK_SENSOR, COM8, COM8_AEC_EN, enable?1:0);
+}
+
+static int set_hmirror_sensor(sensor_t *sensor, int enable)
+{
+    sensor->status.hmirror = enable;
+    return write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_HFLIP_IMG, enable?1:0);
+}
+
+static int set_vflip_sensor(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.vflip = enable;
+    ret = write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_VREF_EN, enable?1:0);
+    return ret & write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_VFLIP_IMG, enable?1:0);
+}
+
+static int set_raw_gma_dsp(sensor_t *sensor, int enable)
+{
+    sensor->status.raw_gma = enable;
+    return set_reg_bits(sensor, BANK_DSP, CTRL1, 5, 1, enable?1:0);
+}
+
+static int set_awb_dsp(sensor_t *sensor, int enable)
+{
+    sensor->status.awb = enable;
+    return set_reg_bits(sensor, BANK_DSP, CTRL1, 3, 1, enable?1:0);
+}
+
+static int set_awb_gain_dsp(sensor_t *sensor, int enable)
+{
+    sensor->status.awb_gain = enable;
+    return set_reg_bits(sensor, BANK_DSP, CTRL1, 2, 1, enable?1:0);
+}
+
+static int set_lenc_dsp(sensor_t *sensor, int enable)
+{
+    sensor->status.lenc = enable;
+    return set_reg_bits(sensor, BANK_DSP, CTRL1, 1, 1, enable?1:0);
+}
+
+static int set_dcw_dsp(sensor_t *sensor, int enable)
+{
+    sensor->status.dcw = enable;
+    return set_reg_bits(sensor, BANK_DSP, CTRL2, 5, 1, enable?1:0);
+}
+
+static int set_bpc_dsp(sensor_t *sensor, int enable)
+{
+    sensor->status.bpc = enable;
+    return set_reg_bits(sensor, BANK_DSP, CTRL3, 7, 1, enable?1:0);
+}
+
+static int set_wpc_dsp(sensor_t *sensor, int enable)
+{
+    sensor->status.wpc = enable;
+    return set_reg_bits(sensor, BANK_DSP, CTRL3, 6, 1, enable?1:0);
+}
+
+//unsupported
+static int set_sharpness(sensor_t *sensor, int level)
+{
+   return -1;
+}
+
+static int set_denoise(sensor_t *sensor, int level)
+{
+   return -1;
+}
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = read_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF);
+    if(ret > 0){
+        ret &= mask;
+    }
+    return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0;
+    ret = read_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF);
+    if(ret < 0){
+        return ret;
+    }
+    value = (ret & ~mask) | (value & mask);
+    ret = write_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF, value);
+    return ret;
+}
+
+static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning)
+{
+    return set_window(sensor, (ov2640_sensor_mode_t)startX, offsetX, offsetY, totalX, totalY, outputX, outputY);
+}
+
+static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div)
+{
+    return -1;
+}
+
+static int set_xclk(sensor_t *sensor, int timer, int xclk)
+{
+    int ret = 0;
+    sensor->xclk_freq_hz = xclk * 1000000U;
+    ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
+    return ret;
+}
+
+static int init_status(sensor_t *sensor){
+    sensor->status.brightness = 0;
+    sensor->status.contrast = 0;
+    sensor->status.saturation = 0;
+    sensor->status.ae_level = 0;
+    sensor->status.special_effect = 0;
+    sensor->status.wb_mode = 0;
+
+    sensor->status.agc_gain = 30;
+    int agc_gain = read_reg(sensor, BANK_SENSOR, GAIN);
+    for (int i=0; i<30; i++){
+        if(agc_gain >= agc_gain_tbl[i] && agc_gain < agc_gain_tbl[i+1]){
+            sensor->status.agc_gain = i;
+            break;
+        }
+    }
+
+    sensor->status.aec_value = ((uint16_t)get_reg_bits(sensor, BANK_SENSOR, REG45, 0, 0x3F) << 10)
+                             | ((uint16_t)read_reg(sensor, BANK_SENSOR, AEC) << 2)
+                             | get_reg_bits(sensor, BANK_SENSOR, REG04, 0, 3);//0 - 1200
+    sensor->status.quality = read_reg(sensor, BANK_DSP, QS);
+    sensor->status.gainceiling = get_reg_bits(sensor, BANK_SENSOR, COM9, 5, 7);
+
+    sensor->status.awb = get_reg_bits(sensor, BANK_DSP, CTRL1, 3, 1);
+    sensor->status.awb_gain = get_reg_bits(sensor, BANK_DSP, CTRL1, 2, 1);
+    sensor->status.aec = get_reg_bits(sensor, BANK_SENSOR, COM8, 0, 1);
+    sensor->status.aec2 = get_reg_bits(sensor, BANK_DSP, CTRL0, 6, 1);
+    sensor->status.agc = get_reg_bits(sensor, BANK_SENSOR, COM8, 2, 1);
+    sensor->status.bpc = get_reg_bits(sensor, BANK_DSP, CTRL3, 7, 1);
+    sensor->status.wpc = get_reg_bits(sensor, BANK_DSP, CTRL3, 6, 1);
+    sensor->status.raw_gma = get_reg_bits(sensor, BANK_DSP, CTRL1, 5, 1);
+    sensor->status.lenc = get_reg_bits(sensor, BANK_DSP, CTRL1, 1, 1);
+    sensor->status.hmirror = get_reg_bits(sensor, BANK_SENSOR, REG04, 7, 1);
+    sensor->status.vflip = get_reg_bits(sensor, BANK_SENSOR, REG04, 6, 1);
+    sensor->status.dcw = get_reg_bits(sensor, BANK_DSP, CTRL2, 5, 1);
+    sensor->status.colorbar = get_reg_bits(sensor, BANK_SENSOR, COM7, 1, 1);
+
+    sensor->status.sharpness = 0;//not supported
+    sensor->status.denoise = 0;
+    return 0;
+}
+
+int ov2640_detect(int slv_addr, sensor_id_t *id)
+{
+    if (OV2640_SCCB_ADDR == slv_addr) {
+        SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
+        uint16_t PID = SCCB_Read(slv_addr, 0x0A);
+        if (OV2640_PID == PID) {
+            id->PID = PID;
+            id->VER = SCCB_Read(slv_addr, REG_VER);
+            id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
+            id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int ov2640_init(sensor_t *sensor)
+{
+    sensor->reset = reset;
+    sensor->init_status = init_status;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_contrast  = set_contrast;
+    sensor->set_brightness= set_brightness;
+    sensor->set_saturation= set_saturation;
+
+    sensor->set_quality = set_quality;
+    sensor->set_colorbar = set_colorbar;
+
+    sensor->set_gainceiling = set_gainceiling_sensor;
+    sensor->set_gain_ctrl = set_agc_sensor;
+    sensor->set_exposure_ctrl = set_aec_sensor;
+    sensor->set_hmirror = set_hmirror_sensor;
+    sensor->set_vflip = set_vflip_sensor;
+
+    sensor->set_whitebal = set_awb_dsp;
+    sensor->set_aec2 = set_aec2;
+    sensor->set_aec_value = set_aec_value;
+    sensor->set_special_effect = set_special_effect;
+    sensor->set_wb_mode = set_wb_mode;
+    sensor->set_ae_level = set_ae_level;
+
+    sensor->set_dcw = set_dcw_dsp;
+    sensor->set_bpc = set_bpc_dsp;
+    sensor->set_wpc = set_wpc_dsp;
+    sensor->set_awb_gain = set_awb_gain_dsp;
+    sensor->set_agc_gain = set_agc_gain;
+
+    sensor->set_raw_gma = set_raw_gma_dsp;
+    sensor->set_lenc = set_lenc_dsp;
+
+    //not supported
+    sensor->set_sharpness = set_sharpness;
+    sensor->set_denoise = set_denoise;
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_res_raw = set_res_raw;
+    sensor->set_pll = _set_pll;
+    sensor->set_xclk = set_xclk;
+    ESP_LOGD(TAG, "OV2640 Attached");
+    return 0;
+}

+ 1053 - 0
std/camera/sensors/ov3660.c

@@ -0,0 +1,1053 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV3660 driver.
+ *
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sccb.h"
+#include "xclk.h"
+#include "ov3660.h"
+#include "ov3660_regs.h"
+#include "ov3660_settings.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char *TAG = "ov3660";
+#endif
+
+//#define REG_DEBUG_ON
+
+static int read_reg(uint8_t slv_addr, const uint16_t reg){
+    int ret = SCCB_Read16(slv_addr, reg);
+#ifdef REG_DEBUG_ON
+    if (ret < 0) {
+        ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask){
+    return (read_reg(slv_addr, reg) & mask) == mask;
+}
+
+static int read_reg16(uint8_t slv_addr, const uint16_t reg){
+    int ret = 0, ret2 = 0;
+    ret = read_reg(slv_addr, reg);
+    if (ret >= 0) {
+        ret = (ret & 0xFF) << 8;
+        ret2 = read_reg(slv_addr, reg+1);
+        if (ret2 < 0) {
+            ret = ret2;
+        } else {
+            ret |= ret2 & 0xFF;
+        }
+    }
+    return ret;
+}
+
+
+static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value){
+    int ret = 0;
+#ifndef REG_DEBUG_ON
+    ret = SCCB_Write16(slv_addr, reg, value);
+#else
+    int old_value = read_reg(slv_addr, reg);
+    if (old_value < 0) {
+        return old_value;
+    }
+    if ((uint8_t)old_value != value) {
+        ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
+        ret = SCCB_Write16(slv_addr, reg, value);
+    } else {
+        ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
+        ret = SCCB_Write16(slv_addr, reg, value);//maybe not?
+    }
+    if (ret < 0) {
+        ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
+{
+    int ret = 0;
+    uint8_t c_value, new_value;
+    ret = read_reg(slv_addr, reg);
+    if(ret < 0) {
+        return ret;
+    }
+    c_value = ret;
+    new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
+    ret = write_reg(slv_addr, reg, new_value);
+    return ret;
+}
+
+static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
+{
+    int i = 0, ret = 0;
+    while (!ret && regs[i][0] != REGLIST_TAIL) {
+        if (regs[i][0] == REG_DLY) {
+            vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+        } else {
+            ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
+        }
+        i++;
+    }
+    return ret;
+}
+
+static int write_reg16(uint8_t slv_addr, const uint16_t reg, uint16_t value)
+{
+    if (write_reg(slv_addr, reg, value >> 8) || write_reg(slv_addr, reg + 1, value)) {
+        return -1;
+    }
+    return 0;
+}
+
+static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value, uint16_t y_value)
+{
+    if (write_reg16(slv_addr, reg, x_value) || write_reg16(slv_addr, reg + 2, y_value)) {
+        return -1;
+    }
+    return 0;
+}
+
+#define write_reg_bits(slv_addr, reg, mask, enable) set_reg_bits(slv_addr, reg, 0, mask, enable?mask:0)
+
+static int calc_sysclk(int xclk, bool pll_bypass, int pll_multiplier, int pll_sys_div, int pll_pre_div, bool pll_root_2x, int pll_seld5, bool pclk_manual, int pclk_div)
+{
+    const int pll_pre_div2x_map[] = { 2, 3, 4, 6 };//values are multiplied by two to avoid floats
+    const int pll_seld52x_map[] = { 2, 2, 4, 5 };
+
+    if(!pll_sys_div) {
+        pll_sys_div = 1;
+    }
+
+    int pll_pre_div2x = pll_pre_div2x_map[pll_pre_div];
+    int pll_root_div = pll_root_2x?2:1;
+    int pll_seld52x = pll_seld52x_map[pll_seld5];
+
+    int VCO = (xclk / 1000) * pll_multiplier * pll_root_div * 2 / pll_pre_div2x;
+    int PLLCLK = pll_bypass?(xclk):(VCO * 1000 * 2 / pll_sys_div / pll_seld52x);
+    int PCLK = PLLCLK / 2 / ((pclk_manual && pclk_div)?pclk_div:1);
+    int SYSCLK = PLLCLK / 4;
+
+    ESP_LOGI(TAG, "Calculated VCO: %d Hz, PLLCLK: %d Hz, SYSCLK: %d Hz, PCLK: %d Hz", VCO*1000, PLLCLK, SYSCLK, PCLK);
+    return SYSCLK;
+}
+
+static int set_pll(sensor_t *sensor, bool bypass, uint8_t multiplier, uint8_t sys_div, uint8_t pre_div, bool root_2x, uint8_t seld5, bool pclk_manual, uint8_t pclk_div){
+    int ret = 0;
+    if(multiplier > 31 || sys_div > 15 || pre_div > 3 || pclk_div > 31 || seld5 > 3){
+        ESP_LOGE(TAG, "Invalid arguments");
+        return -1;
+    }
+
+    calc_sysclk(sensor->xclk_freq_hz, bypass, multiplier, sys_div, pre_div, root_2x, seld5, pclk_manual, pclk_div);
+
+    ret = write_reg(sensor->slv_addr, SC_PLLS_CTRL0, bypass?0x80:0x00);
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, SC_PLLS_CTRL1, multiplier & 0x1f);
+    }
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, SC_PLLS_CTRL2, 0x10 | (sys_div & 0x0f));
+    }
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, SC_PLLS_CTRL3, (pre_div & 0x3) << 4 | seld5 | (root_2x?0x40:0x00));
+    }
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, PCLK_RATIO, pclk_div & 0x1f);
+    }
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, VFIFO_CTRL0C, pclk_manual?0x22:0x20);
+    }
+    if(ret){
+        ESP_LOGE(TAG, "set_sensor_pll FAILED!");
+    }
+    return ret;
+}
+
+static int set_ae_level(sensor_t *sensor, int level);
+
+static int reset(sensor_t *sensor)
+{
+    int ret = 0;
+    // Software Reset: clear all registers and reset them to their default values
+    ret = write_reg(sensor->slv_addr, SYSTEM_CTROL0, 0x82);
+    if(ret){
+        ESP_LOGE(TAG, "Software Reset FAILED!");
+        return ret;
+    }
+    vTaskDelay(100 / portTICK_PERIOD_MS);
+    ret = write_regs(sensor->slv_addr, sensor_default_regs);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Camera defaults loaded");
+        ret = set_ae_level(sensor, 0);
+        vTaskDelay(100 / portTICK_PERIOD_MS);
+    }
+    return ret;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret = 0;
+    const uint16_t (*regs)[2];
+
+    switch (pixformat) {
+    case PIXFORMAT_YUV422:
+        regs = sensor_fmt_yuv422;
+        break;
+
+    case PIXFORMAT_GRAYSCALE:
+        regs = sensor_fmt_grayscale;
+        break;
+
+    case PIXFORMAT_RGB565:
+    case PIXFORMAT_RGB888:
+        regs = sensor_fmt_rgb565;
+        break;
+
+    case PIXFORMAT_JPEG:
+        regs = sensor_fmt_jpeg;
+        break;
+
+    case PIXFORMAT_RAW:
+        regs = sensor_fmt_raw;
+        break;
+
+    default:
+        ESP_LOGE(TAG, "Unsupported pixformat: %u", pixformat);
+        return -1;
+    }
+
+    ret = write_regs(sensor->slv_addr, regs);
+    if(ret == 0) {
+        sensor->pixformat = pixformat;
+        ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
+    }
+    return ret;
+}
+
+static int set_image_options(sensor_t *sensor)
+{
+    int ret = 0;
+    uint8_t reg20 = 0;
+    uint8_t reg21 = 0;
+    uint8_t reg4514 = 0;
+    uint8_t reg4514_test = 0;
+
+    // compression
+    if (sensor->pixformat == PIXFORMAT_JPEG) {
+        reg21 |= 0x20;
+    }
+
+    // binning
+    if (sensor->status.binning) {
+        reg20 |= 0x01;
+        reg21 |= 0x01;
+        reg4514_test |= 4;
+    } else {
+        reg20 |= 0x40;
+    }
+
+    // V-Flip
+    if (sensor->status.vflip) {
+        reg20 |= 0x06;
+        reg4514_test |= 1;
+    }
+
+    // H-Mirror
+    if (sensor->status.hmirror) {
+        reg21 |= 0x06;
+        reg4514_test |= 2;
+    }
+
+    switch (reg4514_test) {
+        //no binning
+        case 0: reg4514 = 0x88; break;//normal
+        case 1: reg4514 = 0x88; break;//v-flip
+        case 2: reg4514 = 0xbb; break;//h-mirror
+        case 3: reg4514 = 0xbb; break;//v-flip+h-mirror
+        //binning
+        case 4: reg4514 = 0xaa; break;//normal
+        case 5: reg4514 = 0xbb; break;//v-flip
+        case 6: reg4514 = 0xbb; break;//h-mirror
+        case 7: reg4514 = 0xaa; break;//v-flip+h-mirror
+    }
+
+    if(write_reg(sensor->slv_addr, TIMING_TC_REG20, reg20)
+        || write_reg(sensor->slv_addr, TIMING_TC_REG21, reg21)
+        || write_reg(sensor->slv_addr, 0x4514, reg4514)){
+        ESP_LOGE(TAG, "Setting Image Options Failed");
+        ret = -1;
+    }
+
+    if (sensor->status.binning) {
+        ret  = write_reg(sensor->slv_addr, 0x4520, 0x0b)
+            || write_reg(sensor->slv_addr, X_INCREMENT, 0x31)//odd:3, even: 1
+            || write_reg(sensor->slv_addr, Y_INCREMENT, 0x31);//odd:3, even: 1
+    } else {
+        ret  = write_reg(sensor->slv_addr, 0x4520, 0xb0)
+            || write_reg(sensor->slv_addr, X_INCREMENT, 0x11)//odd:1, even: 1
+            || write_reg(sensor->slv_addr, Y_INCREMENT, 0x11);//odd:1, even: 1
+    }
+
+    ESP_LOGD(TAG, "Set Image Options: Compression: %u, Binning: %u, V-Flip: %u, H-Mirror: %u, Reg-4514: 0x%02x",
+        sensor->pixformat == PIXFORMAT_JPEG, sensor->status.binning, sensor->status.vflip, sensor->status.hmirror, reg4514);
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    int ret = 0;
+
+    if(framesize > FRAMESIZE_QXGA){
+        ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
+        framesize = FRAMESIZE_QXGA;
+    }
+    framesize_t old_framesize = sensor->status.framesize;
+    sensor->status.framesize = framesize;
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+    aspect_ratio_t ratio = resolution[sensor->status.framesize].aspect_ratio;
+    ratio_settings_t settings = ratio_table[ratio];
+
+    sensor->status.binning = (w <= (settings.max_width / 2) && h <= (settings.max_height / 2));
+    sensor->status.scale = !((w == settings.max_width && h == settings.max_height)
+        || (w == (settings.max_width / 2) && h == (settings.max_height / 2)));
+
+    ret  = write_addr_reg(sensor->slv_addr, X_ADDR_ST_H, settings.start_x, settings.start_y)
+        || write_addr_reg(sensor->slv_addr, X_ADDR_END_H, settings.end_x, settings.end_y)
+        || write_addr_reg(sensor->slv_addr, X_OUTPUT_SIZE_H, w, h);
+
+    if (ret) {
+        goto fail;
+    }
+
+    if (sensor->status.binning) {
+        ret  = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, settings.total_x, (settings.total_y / 2) + 1)
+            || write_addr_reg(sensor->slv_addr, X_OFFSET_H, 8, 2);
+    } else {
+        ret  = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, settings.total_x, settings.total_y)
+            || write_addr_reg(sensor->slv_addr, X_OFFSET_H, 16, 6);
+    }
+
+    if (ret == 0) {
+        ret = write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x20, sensor->status.scale);
+    }
+
+    if (ret == 0) {
+        ret = set_image_options(sensor);
+    }
+
+    if (ret) {
+        goto fail;
+    }
+
+    if (sensor->pixformat == PIXFORMAT_JPEG) {
+        if (framesize == FRAMESIZE_QXGA || sensor->xclk_freq_hz == 16000000) {
+            //40MHz SYSCLK and 10MHz PCLK
+            ret = set_pll(sensor, false, 24, 1, 3, false, 0, true, 8);
+        } else {
+            //50MHz SYSCLK and 10MHz PCLK
+            ret = set_pll(sensor, false, 30, 1, 3, false, 0, true, 10);
+        }
+    } else {
+        //tuned for 16MHz XCLK and 8MHz PCLK
+        if (framesize > FRAMESIZE_HVGA) {
+            //8MHz SYSCLK and 8MHz PCLK (4.44 FPS)
+            ret = set_pll(sensor, false, 4, 1, 0, false, 2, true, 2);
+        } else if (framesize >= FRAMESIZE_QVGA) {
+            //16MHz SYSCLK and 8MHz PCLK (10.25 FPS)
+            ret = set_pll(sensor, false, 8, 1, 0, false, 2, true, 4);
+        } else {
+            //32MHz SYSCLK and 8MHz PCLK (17.77 FPS)
+            ret = set_pll(sensor, false, 8, 1, 0, false, 0, true, 8);
+        }
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
+    }
+    return ret;
+
+fail:
+    sensor->status.framesize = old_framesize;
+    ESP_LOGE(TAG, "Setting framesize to: %ux%u failed", w, h);
+    return ret;
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.hmirror = enable;
+    ret = set_image_options(sensor);
+    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 = set_image_options(sensor);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set v-flip to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_quality(sensor_t *sensor, int qs)
+{
+    int ret = 0;
+    ret = write_reg(sensor->slv_addr, COMPRESSION_CTRL07, qs & 0x3f);
+    if (ret == 0) {
+        sensor->status.quality = qs;
+        ESP_LOGD(TAG, "Set quality to: %d", qs);
+    }
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, PRE_ISP_TEST_SETTING_1, TEST_COLOR_BAR, enable);
+    if (ret == 0) {
+        sensor->status.colorbar = enable;
+        ESP_LOGD(TAG, "Set colorbar to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_gain_ctrl(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AGC_MANUALEN, !enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set gain_ctrl to: %d", enable);
+        sensor->status.agc = enable;
+    }
+    return ret;
+}
+
+static int set_exposure_ctrl(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AEC_MANUALEN, !enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set exposure_ctrl to: %d", enable);
+        sensor->status.aec = enable;
+    }
+    return ret;
+}
+
+static int set_whitebal(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x01, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set awb to: %d", enable);
+        sensor->status.awb = enable;
+    }
+    return ret;
+}
+
+//Advanced AWB
+static int set_dcw_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x5183, 0x80, !enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set dcw to: %d", enable);
+        sensor->status.dcw = enable;
+    }
+    return ret;
+}
+
+//night mode enable
+static int set_aec2(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x3a00, 0x04, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set aec2 to: %d", enable);
+        sensor->status.aec2 = enable;
+    }
+    return ret;
+}
+
+static int set_bpc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x04, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set bpc to: %d", enable);
+        sensor->status.bpc = enable;
+    }
+    return ret;
+}
+
+static int set_wpc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x02, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set wpc to: %d", enable);
+        sensor->status.wpc = enable;
+    }
+    return ret;
+}
+
+//Gamma enable
+static int set_raw_gma_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x20, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set raw_gma to: %d", enable);
+        sensor->status.raw_gma = enable;
+    }
+    return ret;
+}
+
+static int set_lenc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x80, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set lenc to: %d", enable);
+        sensor->status.lenc = enable;
+    }
+    return ret;
+}
+
+static int get_agc_gain(sensor_t *sensor)
+{
+    int ra = read_reg(sensor->slv_addr, 0x350a);
+    if (ra < 0) {
+        return 0;
+    }
+    int rb = read_reg(sensor->slv_addr, 0x350b);
+    if (rb < 0) {
+        return 0;
+    }
+    int res = (rb & 0xF0) >> 4 | (ra & 0x03) << 4;
+    if (rb & 0x0F) {
+        res += 1;
+    }
+    return res;
+}
+
+//real gain
+static int set_agc_gain(sensor_t *sensor, int gain)
+{
+    int ret = 0;
+    if(gain < 0) {
+        gain = 0;
+    } else if(gain > 64) {
+        gain = 64;
+    }
+
+    //gain value is 6.4 bits float
+    //in order to use the max range, we deduct 1/16
+    int gainv = gain << 4;
+    if(gainv){
+        gainv -= 1;
+    }
+
+    ret = write_reg(sensor->slv_addr, 0x350a, gainv >> 8) || write_reg(sensor->slv_addr, 0x350b, gainv & 0xff);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set agc_gain to: %d", gain);
+        sensor->status.agc_gain = gain;
+    }
+    return ret;
+}
+
+static int get_aec_value(sensor_t *sensor)
+{
+    int ra = read_reg(sensor->slv_addr, 0x3500);
+    if (ra < 0) {
+        return 0;
+    }
+    int rb = read_reg(sensor->slv_addr, 0x3501);
+    if (rb < 0) {
+        return 0;
+    }
+    int rc = read_reg(sensor->slv_addr, 0x3502);
+    if (rc < 0) {
+        return 0;
+    }
+    int res = (ra & 0x0F) << 12 | (rb & 0xFF) << 4 | (rc & 0xF0) >> 4;
+    return res;
+}
+
+static int set_aec_value(sensor_t *sensor, int value)
+{
+    int ret = 0, max_val = 0;
+    max_val = read_reg16(sensor->slv_addr, 0x380e);
+    if (max_val < 0) {
+        ESP_LOGE(TAG, "Could not read max aec_value");
+        return -1;
+    }
+    if (value > max_val) {
+        value =max_val;
+    }
+
+    ret =  write_reg(sensor->slv_addr, 0x3500, (value >> 12) & 0x0F)
+        || write_reg(sensor->slv_addr, 0x3501, (value >> 4) & 0xFF)
+        || write_reg(sensor->slv_addr, 0x3502, (value << 4) & 0xF0);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set aec_value to: %d / %d", value, max_val);
+        sensor->status.aec_value = value;
+    }
+    return ret;
+}
+
+static int set_ae_level(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    if (level < -5 || level > 5) {
+        return -1;
+    }
+    //good targets are between 5 and 115
+    int target_level = ((level + 5) * 10) + 5;
+
+    int level_high, level_low;
+    int fast_high, fast_low;
+
+    level_low = target_level * 23 / 25; //0.92 (0.46)
+    level_high = target_level * 27 / 25; //1.08 (2.08)
+
+    fast_low = level_low >> 1;
+    fast_high = level_high << 1;
+
+    if(fast_high>255) {
+        fast_high = 255;
+    }
+
+    ret =  write_reg(sensor->slv_addr, 0x3a0f, level_high)
+        || write_reg(sensor->slv_addr, 0x3a10, level_low)
+        || write_reg(sensor->slv_addr, 0x3a1b, level_high)
+        || write_reg(sensor->slv_addr, 0x3a1e, level_low)
+        || write_reg(sensor->slv_addr, 0x3a11, fast_high)
+        || write_reg(sensor->slv_addr, 0x3a1f, fast_low);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set ae_level to: %d", level);
+        sensor->status.ae_level = level;
+    }
+    return ret;
+}
+
+static int set_wb_mode(sensor_t *sensor, int mode)
+{
+    int ret = 0;
+    if (mode < 0 || mode > 4) {
+        return -1;
+    }
+
+    ret = write_reg(sensor->slv_addr, 0x3406, (mode != 0));
+    if (ret) {
+        return ret;
+    }
+    switch (mode) {
+        case 1://Sunny
+            ret  = write_reg16(sensor->slv_addr, 0x3400, 0x5e0) //AWB R GAIN
+                || write_reg16(sensor->slv_addr, 0x3402, 0x410) //AWB G GAIN
+                || write_reg16(sensor->slv_addr, 0x3404, 0x540);//AWB B GAIN
+            break;
+        case 2://Cloudy
+            ret  = write_reg16(sensor->slv_addr, 0x3400, 0x650) //AWB R GAIN
+                || write_reg16(sensor->slv_addr, 0x3402, 0x410) //AWB G GAIN
+                || write_reg16(sensor->slv_addr, 0x3404, 0x4f0);//AWB B GAIN
+            break;
+        case 3://Office
+            ret  = write_reg16(sensor->slv_addr, 0x3400, 0x520) //AWB R GAIN
+                || write_reg16(sensor->slv_addr, 0x3402, 0x410) //AWB G GAIN
+                || write_reg16(sensor->slv_addr, 0x3404, 0x660);//AWB B GAIN
+            break;
+        case 4://HOME
+            ret  = write_reg16(sensor->slv_addr, 0x3400, 0x420) //AWB R GAIN
+                || write_reg16(sensor->slv_addr, 0x3402, 0x3f0) //AWB G GAIN
+                || write_reg16(sensor->slv_addr, 0x3404, 0x710);//AWB B GAIN
+            break;
+        default://AUTO
+            break;
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set wb_mode to: %d", mode);
+        sensor->status.wb_mode = mode;
+    }
+    return ret;
+}
+
+static int set_awb_gain_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    int old_mode = sensor->status.wb_mode;
+    int mode = enable?old_mode:0;
+
+    ret = set_wb_mode(sensor, mode);
+
+    if (ret == 0) {
+        sensor->status.wb_mode = old_mode;
+        ESP_LOGD(TAG, "Set awb_gain to: %d", enable);
+        sensor->status.awb_gain = enable;
+    }
+    return ret;
+}
+
+static int set_special_effect(sensor_t *sensor, int effect)
+{
+    int ret=0;
+    if (effect < 0 || effect > 6) {
+        return -1;
+    }
+
+    uint8_t * regs = (uint8_t *)sensor_special_effects[effect];
+    ret =  write_reg(sensor->slv_addr, 0x5580, regs[0])
+        || write_reg(sensor->slv_addr, 0x5583, regs[1])
+        || write_reg(sensor->slv_addr, 0x5584, regs[2])
+        || write_reg(sensor->slv_addr, 0x5003, regs[3]);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set special_effect to: %d", effect);
+        sensor->status.special_effect = effect;
+    }
+    return ret;
+}
+
+static int set_brightness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    uint8_t value = 0;
+    bool negative = false;
+
+    switch (level) {
+        case 3:
+            value = 0x30;
+            break;
+        case 2:
+            value = 0x20;
+            break;
+        case 1:
+            value = 0x10;
+            break;
+        case -1:
+            value = 0x10;
+            negative = true;
+            break;
+        case -2:
+            value = 0x20;
+            negative = true;
+            break;
+        case -3:
+            value = 0x30;
+            negative = true;
+            break;
+        default: // 0
+            break;
+    }
+
+    ret = write_reg(sensor->slv_addr, 0x5587, value);
+    if (ret == 0) {
+        ret = write_reg_bits(sensor->slv_addr, 0x5588, 0x08, negative);
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set brightness to: %d", level);
+        sensor->status.brightness = level;
+    }
+    return ret;
+}
+
+static int set_contrast(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    if(level > 3 || level < -3) {
+        return -1;
+    }
+    ret = write_reg(sensor->slv_addr, 0x5586, (level + 4) << 3);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set contrast to: %d", level);
+        sensor->status.contrast = level;
+    }
+    return ret;
+}
+
+static int set_saturation(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    if(level > 4 || level < -4) {
+        return -1;
+    }
+
+    uint8_t * regs = (uint8_t *)sensor_saturation_levels[level+4];
+    for(int i=0; i<11; i++) {
+        ret = write_reg(sensor->slv_addr, 0x5381 + i, regs[i]);
+        if (ret) {
+            break;
+        }
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set saturation to: %d", level);
+        sensor->status.saturation = level;
+    }
+    return ret;
+}
+
+static int set_sharpness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    if(level > 3 || level < -3) {
+        return -1;
+    }
+
+    uint8_t mt_offset_2 = (level + 3) * 8;
+    uint8_t mt_offset_1 = mt_offset_2 + 1;
+
+    ret = write_reg_bits(sensor->slv_addr, 0x5308, 0x40, false)//0x40 means auto
+        || write_reg(sensor->slv_addr, 0x5300, 0x10)
+        || write_reg(sensor->slv_addr, 0x5301, 0x10)
+        || write_reg(sensor->slv_addr, 0x5302, mt_offset_1)
+        || write_reg(sensor->slv_addr, 0x5303, mt_offset_2)
+        || write_reg(sensor->slv_addr, 0x5309, 0x10)
+        || write_reg(sensor->slv_addr, 0x530a, 0x10)
+        || write_reg(sensor->slv_addr, 0x530b, 0x04)
+        || write_reg(sensor->slv_addr, 0x530c, 0x06);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set sharpness to: %d", level);
+        sensor->status.sharpness = level;
+    }
+    return ret;
+}
+
+static int set_gainceiling(sensor_t *sensor, gainceiling_t level)
+{
+    int ret = 0, l = (int)level;
+
+    ret = write_reg(sensor->slv_addr, 0x3A18, (l >> 8) & 3)
+       || write_reg(sensor->slv_addr, 0x3A19, l & 0xFF);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set gainceiling to: %d", l);
+        sensor->status.gainceiling = l;
+    }
+    return ret;
+}
+
+static int get_denoise(sensor_t *sensor)
+{
+    if (!check_reg_mask(sensor->slv_addr, 0x5308, 0x10)) {
+        return 0;
+    }
+    return (read_reg(sensor->slv_addr, 0x5306) / 4) + 1;
+}
+
+static int set_denoise(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    if (level < 0 || level > 8) {
+        return -1;
+    }
+
+    ret = write_reg_bits(sensor->slv_addr, 0x5308, 0x10, level > 0);
+    if (ret == 0 && level > 0) {
+        ret = write_reg(sensor->slv_addr, 0x5306, (level - 1) * 4);
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set denoise to: %d", level);
+        sensor->status.denoise = level;
+    }
+    return ret;
+}
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = 0, ret2 = 0;
+    if(mask > 0xFF){
+        ret = read_reg16(sensor->slv_addr, reg);
+        if(ret >= 0 && mask > 0xFFFF){
+            ret2 = read_reg(sensor->slv_addr, reg+2);
+            if(ret2 >= 0){
+                ret = (ret << 8) | ret2 ;
+            } else {
+                ret = ret2;
+            }
+        }
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if(ret > 0){
+        ret &= mask;
+    }
+    return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0, ret2 = 0;
+    if(mask > 0xFF){
+        ret = read_reg16(sensor->slv_addr, reg);
+        if(ret >= 0 && mask > 0xFFFF){
+            ret2 = read_reg(sensor->slv_addr, reg+2);
+            if(ret2 >= 0){
+                ret = (ret << 8) | ret2 ;
+            } else {
+                ret = ret2;
+            }
+        }
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if(ret < 0){
+        return ret;
+    }
+    value = (ret & ~mask) | (value & mask);
+    if(mask > 0xFFFF){
+        ret = write_reg16(sensor->slv_addr, reg, value >> 8);
+        if(ret >= 0){
+            ret = write_reg(sensor->slv_addr, reg+2, value & 0xFF);
+        }
+    } else if(mask > 0xFF){
+        ret = write_reg16(sensor->slv_addr, reg, value);
+    } else {
+        ret = write_reg(sensor->slv_addr, reg, value);
+    }
+    return ret;
+}
+
+static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning)
+{
+    int ret = 0;
+    ret  = write_addr_reg(sensor->slv_addr, X_ADDR_ST_H, startX, startY)
+        || write_addr_reg(sensor->slv_addr, X_ADDR_END_H, endX, endY)
+        || write_addr_reg(sensor->slv_addr, X_OFFSET_H, offsetX, offsetY)
+        || write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, totalX, totalY)
+        || write_addr_reg(sensor->slv_addr, X_OUTPUT_SIZE_H, outputX, outputY)
+        || write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x20, scale);
+    if(!ret){
+        sensor->status.scale = scale;
+        sensor->status.binning = binning;
+        ret = set_image_options(sensor);
+    }
+    return ret;
+}
+
+static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div)
+{
+    return set_pll(sensor, bypass > 0, multiplier, sys_div, pre_div, root_2x > 0, seld5, pclk_manual > 0, pclk_div);
+}
+
+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;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    sensor->status.brightness = 0;
+    sensor->status.contrast = 0;
+    sensor->status.saturation = 0;
+    sensor->status.sharpness = (read_reg(sensor->slv_addr, 0x5303) / 8) - 3;
+    sensor->status.denoise = get_denoise(sensor);
+    sensor->status.ae_level = 0;
+    sensor->status.gainceiling = read_reg16(sensor->slv_addr, 0x3A18) & 0x3FF;
+    sensor->status.awb = check_reg_mask(sensor->slv_addr, ISP_CONTROL_01, 0x01);
+    sensor->status.dcw = !check_reg_mask(sensor->slv_addr, 0x5183, 0x80);
+    sensor->status.agc = !check_reg_mask(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AGC_MANUALEN);
+    sensor->status.aec = !check_reg_mask(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AEC_MANUALEN);
+    sensor->status.hmirror = check_reg_mask(sensor->slv_addr, TIMING_TC_REG21, TIMING_TC_REG21_HMIRROR);
+    sensor->status.vflip = check_reg_mask(sensor->slv_addr, TIMING_TC_REG20, TIMING_TC_REG20_VFLIP);
+    sensor->status.colorbar = check_reg_mask(sensor->slv_addr, PRE_ISP_TEST_SETTING_1, TEST_COLOR_BAR);
+    sensor->status.bpc = check_reg_mask(sensor->slv_addr, 0x5000, 0x04);
+    sensor->status.wpc = check_reg_mask(sensor->slv_addr, 0x5000, 0x02);
+    sensor->status.raw_gma = check_reg_mask(sensor->slv_addr, 0x5000, 0x20);
+    sensor->status.lenc = check_reg_mask(sensor->slv_addr, 0x5000, 0x80);
+    sensor->status.quality = read_reg(sensor->slv_addr, COMPRESSION_CTRL07) & 0x3f;
+    sensor->status.special_effect = 0;
+    sensor->status.wb_mode = 0;
+    sensor->status.awb_gain = check_reg_mask(sensor->slv_addr, 0x3406, 0x01);
+    sensor->status.agc_gain = get_agc_gain(sensor);
+    sensor->status.aec_value = get_aec_value(sensor);
+    sensor->status.aec2 = check_reg_mask(sensor->slv_addr, 0x3a00, 0x04);
+    return 0;
+}
+
+int ov3660_detect(int slv_addr, sensor_id_t *id)
+{
+    if (OV3660_SCCB_ADDR == slv_addr) {
+        uint8_t h = SCCB_Read16(slv_addr, 0x300A);
+        uint8_t l = SCCB_Read16(slv_addr, 0x300B);
+        uint16_t PID = (h<<8) | l;
+        if (OV3660_PID == PID) {
+            id->PID = PID;
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int ov3660_init(sensor_t *sensor)
+{
+    sensor->reset = reset;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_contrast = set_contrast;
+    sensor->set_brightness = set_brightness;
+    sensor->set_saturation = set_saturation;
+    sensor->set_sharpness = set_sharpness;
+    sensor->set_gainceiling = set_gainceiling;
+    sensor->set_quality = set_quality;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_gain_ctrl = set_gain_ctrl;
+    sensor->set_exposure_ctrl = set_exposure_ctrl;
+    sensor->set_whitebal = set_whitebal;
+    sensor->set_hmirror = set_hmirror;
+    sensor->set_vflip = set_vflip;
+    sensor->init_status = init_status;
+    sensor->set_aec2 = set_aec2;
+    sensor->set_aec_value = set_aec_value;
+    sensor->set_special_effect = set_special_effect;
+    sensor->set_wb_mode = set_wb_mode;
+    sensor->set_ae_level = set_ae_level;
+    sensor->set_dcw = set_dcw_dsp;
+    sensor->set_bpc = set_bpc_dsp;
+    sensor->set_wpc = set_wpc_dsp;
+    sensor->set_awb_gain = set_awb_gain_dsp;
+    sensor->set_agc_gain = set_agc_gain;
+    sensor->set_raw_gma = set_raw_gma_dsp;
+    sensor->set_lenc = set_lenc_dsp;
+    sensor->set_denoise = set_denoise;
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_res_raw = set_res_raw;
+    sensor->set_pll = _set_pll;
+    sensor->set_xclk = set_xclk;
+    return 0;
+}

+ 1130 - 0
std/camera/sensors/ov5640.c

@@ -0,0 +1,1130 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV3660 driver.
+ *
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sccb.h"
+#include "xclk.h"
+#include "ov5640.h"
+#include "ov5640_regs.h"
+#include "ov5640_settings.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char *TAG = "ov5640";
+#endif
+
+//#define REG_DEBUG_ON
+
+static int read_reg(uint8_t slv_addr, const uint16_t reg){
+    int ret = SCCB_Read16(slv_addr, reg);
+#ifdef REG_DEBUG_ON
+    if (ret < 0) {
+        ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask){
+    return (read_reg(slv_addr, reg) & mask) == mask;
+}
+
+static int read_reg16(uint8_t slv_addr, const uint16_t reg){
+    int ret = 0, ret2 = 0;
+    ret = read_reg(slv_addr, reg);
+    if (ret >= 0) {
+        ret = (ret & 0xFF) << 8;
+        ret2 = read_reg(slv_addr, reg+1);
+        if (ret2 < 0) {
+            ret = ret2;
+        } else {
+            ret |= ret2 & 0xFF;
+        }
+    }
+    return ret;
+}
+
+//static void dump_reg(sensor_t *sensor, const uint16_t reg){
+//    int v = SCCB_Read16(sensor->slv_addr, reg);
+//    if(v < 0){
+//        ets_printf("  0x%04x: FAIL[%d]\n", reg, v);
+//    } else {
+//        ets_printf("  0x%04x: 0x%02X\n", reg, v);
+//    }
+//}
+//
+//static void dump_range(sensor_t *sensor, const char * name, const uint16_t start_reg, const uint16_t end_reg){
+//    ets_printf("%s: 0x%04x - 0x%04X\n", name, start_reg, end_reg);
+//    for(uint16_t reg = start_reg; reg <= end_reg; reg++){
+//        dump_reg(sensor, reg);
+//    }
+//}
+//
+//static void dump_regs(sensor_t *sensor){
+////    dump_range(sensor, "All Regs", 0x3000, 0x6100);
+////    dump_range(sensor, "system and IO pad control", 0x3000, 0x3052);
+////    dump_range(sensor, "SCCB control", 0x3100, 0x3108);
+////    dump_range(sensor, "SRB control", 0x3200, 0x3211);
+////    dump_range(sensor, "AWB gain control", 0x3400, 0x3406);
+////    dump_range(sensor, "AEC/AGC control", 0x3500, 0x350D);
+////    dump_range(sensor, "VCM control", 0x3600, 0x3606);
+////    dump_range(sensor, "timing control", 0x3800, 0x3821);
+////    dump_range(sensor, "AEC/AGC power down domain control", 0x3A00, 0x3A25);
+////    dump_range(sensor, "strobe control", 0x3B00, 0x3B0C);
+////    dump_range(sensor, "50/60Hz detector control", 0x3C00, 0x3C1E);
+////    dump_range(sensor, "OTP control", 0x3D00, 0x3D21);
+////    dump_range(sensor, "MC control", 0x3F00, 0x3F0D);
+////    dump_range(sensor, "BLC control", 0x4000, 0x4033);
+////    dump_range(sensor, "frame control", 0x4201, 0x4202);
+////    dump_range(sensor, "format control", 0x4300, 0x430D);
+////    dump_range(sensor, "JPEG control", 0x4400, 0x4431);
+////    dump_range(sensor, "VFIFO control", 0x4600, 0x460D);
+////    dump_range(sensor, "DVP control", 0x4709, 0x4745);
+////    dump_range(sensor, "MIPI control", 0x4800, 0x4837);
+////    dump_range(sensor, "ISP frame control", 0x4901, 0x4902);
+////    dump_range(sensor, "ISP top control", 0x5000, 0x5063);
+////    dump_range(sensor, "AWB control", 0x5180, 0x51D0);
+////    dump_range(sensor, "CIP control", 0x5300, 0x530F);
+////    dump_range(sensor, "CMX control", 0x5380, 0x538B);
+////    dump_range(sensor, "gamma control", 0x5480, 0x5490);
+////    dump_range(sensor, "SDE control", 0x5580, 0x558C);
+////    dump_range(sensor, "scale control", 0x5600, 0x5606);
+////    dump_range(sensor, "AVG control", 0x5680, 0x56A2);
+////    dump_range(sensor, "LENC control", 0x5800, 0x5849);
+////    dump_range(sensor, "AFC control", 0x6000, 0x603F);
+//}
+
+static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value){
+    int ret = 0;
+#ifndef REG_DEBUG_ON
+    ret = SCCB_Write16(slv_addr, reg, value);
+#else
+    int old_value = read_reg(slv_addr, reg);
+    if (old_value < 0) {
+        return old_value;
+    }
+    if ((uint8_t)old_value != value) {
+        ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
+        ret = SCCB_Write16(slv_addr, reg, value);
+    } else {
+        ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
+        ret = SCCB_Write16(slv_addr, reg, value);//maybe not?
+    }
+    if (ret < 0) {
+        ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
+{
+    int ret = 0;
+    uint8_t c_value, new_value;
+    ret = read_reg(slv_addr, reg);
+    if(ret < 0) {
+        return ret;
+    }
+    c_value = ret;
+    new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
+    ret = write_reg(slv_addr, reg, new_value);
+    return ret;
+}
+
+static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
+{
+    int i = 0, ret = 0;
+    while (!ret && regs[i][0] != REGLIST_TAIL) {
+        if (regs[i][0] == REG_DLY) {
+            vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+        } else {
+            ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
+        }
+        i++;
+    }
+    return ret;
+}
+
+static int write_reg16(uint8_t slv_addr, const uint16_t reg, uint16_t value)
+{
+    if (write_reg(slv_addr, reg, value >> 8) || write_reg(slv_addr, reg + 1, value)) {
+        return -1;
+    }
+    return 0;
+}
+
+static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value, uint16_t y_value)
+{
+    if (write_reg16(slv_addr, reg, x_value) || write_reg16(slv_addr, reg + 2, y_value)) {
+        return -1;
+    }
+    return 0;
+}
+
+#define write_reg_bits(slv_addr, reg, mask, enable) set_reg_bits(slv_addr, reg, 0, mask, (enable)?(mask):0)
+
+static int calc_sysclk(int xclk, bool pll_bypass, int pll_multiplier, int pll_sys_div, int pre_div, bool root_2x, int pclk_root_div, bool pclk_manual, int pclk_div)
+{
+    const float pll_pre_div2x_map[] = { 1, 1, 2, 3, 4, 1.5, 6, 2.5, 8};
+    const int pll_pclk_root_div_map[] = { 1, 2, 4, 8 };
+
+    if(!pll_sys_div) {
+        pll_sys_div = 1;
+    }
+
+    float pll_pre_div = pll_pre_div2x_map[pre_div];
+    unsigned int root_2x_div = root_2x?2:1;
+    unsigned int pll_pclk_root_div = pll_pclk_root_div_map[pclk_root_div];
+
+    unsigned int REFIN = xclk / pll_pre_div;
+
+    unsigned int VCO = REFIN * pll_multiplier / root_2x_div;
+
+    unsigned int PLL_CLK = pll_bypass?(xclk):(VCO / pll_sys_div * 2 / 5);//5 here is 10bit mode / 2, for 8bit it should be 4 (reg 0x3034)
+
+    unsigned int PCLK = PLL_CLK / pll_pclk_root_div / ((pclk_manual && pclk_div)?pclk_div:2);
+
+    unsigned int SYSCLK = PLL_CLK / 4;
+
+    ESP_LOGI(TAG, "Calculated XVCLK: %d Hz, REFIN: %u Hz, VCO: %u Hz, PLL_CLK: %u Hz, SYSCLK: %u Hz, PCLK: %u Hz", xclk, REFIN, VCO, PLL_CLK, SYSCLK, PCLK);
+    return SYSCLK;
+}
+
+static int set_pll(sensor_t *sensor, bool bypass, uint8_t multiplier, uint8_t sys_div, uint8_t pre_div, bool root_2x, uint8_t pclk_root_div, bool pclk_manual, uint8_t pclk_div){
+    int ret = 0;
+    if(multiplier > 252 || multiplier < 4 || sys_div > 15 || pre_div > 8 || pclk_div > 31 || pclk_root_div > 3){
+        ESP_LOGE(TAG, "Invalid arguments");
+        return -1;
+    }
+    if(multiplier > 127){
+        multiplier &= 0xFE;//only even integers above 127
+    }
+    ESP_LOGI(TAG, "Set PLL: bypass: %u, multiplier: %u, sys_div: %u, pre_div: %u, root_2x: %u, pclk_root_div: %u, pclk_manual: %u, pclk_div: %u", bypass, multiplier, sys_div, pre_div, root_2x, pclk_root_div, pclk_manual, pclk_div);
+
+    calc_sysclk(sensor->xclk_freq_hz, bypass, multiplier, sys_div, pre_div, root_2x, pclk_root_div, pclk_manual, pclk_div);
+
+    ret = write_reg(sensor->slv_addr, 0x3039, bypass?0x80:0x00);
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, 0x3034, 0x1A);//10bit mode
+    }
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, 0x3035, 0x01 | ((sys_div & 0x0f) << 4));
+    }
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, 0x3036, multiplier & 0xff);
+    }
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, 0x3037, (pre_div & 0xf) | (root_2x?0x10:0x00));
+    }
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, 0x3108, (pclk_root_div & 0x3) << 4 | 0x06);
+    }
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, 0x3824, pclk_div & 0x1f);
+    }
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, 0x460C, pclk_manual?0x22:0x20);
+    }
+    if (ret == 0) {
+        ret = write_reg(sensor->slv_addr, 0x3103, 0x13);// system clock from pll, bit[1]
+    }
+    if(ret){
+        ESP_LOGE(TAG, "set_sensor_pll FAILED!");
+    }
+    return ret;
+}
+
+static int set_ae_level(sensor_t *sensor, int level);
+
+static int reset(sensor_t *sensor)
+{
+    //dump_regs(sensor);
+    vTaskDelay(100 / portTICK_PERIOD_MS);
+    int ret = 0;
+    // Software Reset: clear all registers and reset them to their default values
+    ret = write_reg(sensor->slv_addr, SYSTEM_CTROL0, 0x82);
+    if(ret){
+        ESP_LOGE(TAG, "Software Reset FAILED!");
+        return ret;
+    }
+    vTaskDelay(100 / portTICK_PERIOD_MS);
+    ret = write_regs(sensor->slv_addr, sensor_default_regs);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Camera defaults loaded");
+        vTaskDelay(100 / portTICK_PERIOD_MS);
+        //write_regs(sensor->slv_addr, sensor_regs_awb0);
+        //write_regs(sensor->slv_addr, sensor_regs_gamma1);
+    }
+    return ret;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret = 0;
+    const uint16_t (*regs)[2];
+
+    switch (pixformat) {
+    case PIXFORMAT_YUV422:
+        regs = sensor_fmt_yuv422;
+        break;
+
+    case PIXFORMAT_GRAYSCALE:
+        regs = sensor_fmt_grayscale;
+        break;
+
+    case PIXFORMAT_RGB565:
+    case PIXFORMAT_RGB888:
+        regs = sensor_fmt_rgb565;
+        break;
+
+    case PIXFORMAT_JPEG:
+        regs = sensor_fmt_jpeg;
+        break;
+
+    case PIXFORMAT_RAW:
+        regs = sensor_fmt_raw;
+        break;
+
+    default:
+        ESP_LOGE(TAG, "Unsupported pixformat: %u", pixformat);
+        return -1;
+    }
+
+    ret = write_regs(sensor->slv_addr, regs);
+    if(ret == 0) {
+        sensor->pixformat = pixformat;
+        ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
+    }
+    return ret;
+}
+
+static int set_image_options(sensor_t *sensor)
+{
+    int ret = 0;
+    uint8_t reg20 = 0;
+    uint8_t reg21 = 0;
+    uint8_t reg4514 = 0;
+    uint8_t reg4514_test = 0;
+
+    // compression
+    if (sensor->pixformat == PIXFORMAT_JPEG) {
+        reg21 |= 0x20;
+    }
+
+    // binning
+    if (!sensor->status.binning) {
+        reg20 |= 0x40;
+    } else {
+        reg20 |= 0x01;
+        reg21 |= 0x01;
+        reg4514_test |= 4;
+    }
+
+    // V-Flip
+    if (sensor->status.vflip) {
+        reg20 |= 0x06;
+        reg4514_test |= 1;
+    }
+
+    // H-Mirror
+    if (sensor->status.hmirror) {
+        reg21 |= 0x06;
+        reg4514_test |= 2;
+    }
+
+    switch (reg4514_test) {
+        //no binning
+        case 0: reg4514 = 0x88; break;//normal
+        case 1: reg4514 = 0x00; break;//v-flip
+        case 2: reg4514 = 0xbb; break;//h-mirror
+        case 3: reg4514 = 0x00; break;//v-flip+h-mirror
+        //binning
+        case 4: reg4514 = 0xaa; break;//normal
+        case 5: reg4514 = 0xbb; break;//v-flip
+        case 6: reg4514 = 0xbb; break;//h-mirror
+        case 7: reg4514 = 0xaa; break;//v-flip+h-mirror
+    }
+
+    if(write_reg(sensor->slv_addr, TIMING_TC_REG20, reg20)
+        || write_reg(sensor->slv_addr, TIMING_TC_REG21, reg21)
+        || write_reg(sensor->slv_addr, 0x4514, reg4514)){
+        ESP_LOGE(TAG, "Setting Image Options Failed");
+        return -1;
+    }
+
+    if (!sensor->status.binning) {
+        ret  = write_reg(sensor->slv_addr, 0x4520, 0x10)
+            || write_reg(sensor->slv_addr, X_INCREMENT, 0x11)//odd:1, even: 1
+            || write_reg(sensor->slv_addr, Y_INCREMENT, 0x11);//odd:1, even: 1
+    } else {
+        ret  = write_reg(sensor->slv_addr, 0x4520, 0x0b)
+            || write_reg(sensor->slv_addr, X_INCREMENT, 0x31)//odd:3, even: 1
+            || write_reg(sensor->slv_addr, Y_INCREMENT, 0x31);//odd:3, even: 1
+    }
+
+    ESP_LOGD(TAG, "Set Image Options: Compression: %u, Binning: %u, V-Flip: %u, H-Mirror: %u, Reg-4514: 0x%02x",
+        sensor->pixformat == PIXFORMAT_JPEG, sensor->status.binning, sensor->status.vflip, sensor->status.hmirror, reg4514);
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    int ret = 0;
+    framesize_t old_framesize = sensor->status.framesize;
+    sensor->status.framesize = framesize;
+
+    if(framesize > FRAMESIZE_QSXGA){
+        ESP_LOGE(TAG, "Invalid framesize: %u", framesize);
+        return -1;
+    }
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+    aspect_ratio_t ratio = resolution[framesize].aspect_ratio;
+    ratio_settings_t settings = ratio_table[ratio];
+
+    sensor->status.binning = (w <= (settings.max_width / 2) && h <= (settings.max_height / 2));
+    sensor->status.scale = !((w == settings.max_width && h == settings.max_height)
+        || (w == (settings.max_width / 2) && h == (settings.max_height / 2)));
+
+    ret  = write_addr_reg(sensor->slv_addr, X_ADDR_ST_H, settings.start_x, settings.start_y)
+        || write_addr_reg(sensor->slv_addr, X_ADDR_END_H, settings.end_x, settings.end_y)
+        || write_addr_reg(sensor->slv_addr, X_OUTPUT_SIZE_H, w, h);
+
+    if (ret) {
+        goto fail;
+    }
+
+    if (!sensor->status.binning) {
+        ret  = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, settings.total_x, settings.total_y)
+            || write_addr_reg(sensor->slv_addr, X_OFFSET_H, settings.offset_x, settings.offset_y);
+    } else {
+        if (w > 920) {
+            ret = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, settings.total_x - 200, settings.total_y / 2);
+        } else {
+            ret = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, 2060, settings.total_y / 2);
+        }
+        if (ret == 0) {
+            ret = write_addr_reg(sensor->slv_addr, X_OFFSET_H, settings.offset_x / 2, settings.offset_y / 2);
+        }
+    }
+
+    if (ret == 0) {
+        ret = write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x20, sensor->status.scale);
+    }
+
+    if (ret == 0) {
+        ret = set_image_options(sensor);
+    }
+
+    if (ret) {
+        goto fail;
+    }
+
+    if (sensor->pixformat == PIXFORMAT_JPEG) {
+        //10MHz PCLK
+        uint8_t sys_mul = 200;
+        if(framesize < FRAMESIZE_QVGA || sensor->xclk_freq_hz == 16000000){
+            sys_mul = 160;
+        } else if(framesize < FRAMESIZE_XGA){
+            sys_mul = 180;
+        }
+        ret = set_pll(sensor, false, sys_mul, 4, 2, false, 2, true, 4);
+        //Set PLL: bypass: 0, multiplier: sys_mul, sys_div: 4, pre_div: 2, root_2x: 0, pclk_root_div: 2, pclk_manual: 1, pclk_div: 4
+    } else {
+        //ret = set_pll(sensor, false, 8, 1, 1, false, 1, true, 4);
+        if (framesize > FRAMESIZE_HVGA) {
+            ret = set_pll(sensor, false, 10, 1, 2, false, 1, true, 2);
+        } else if (framesize >= FRAMESIZE_QVGA) {
+            ret = set_pll(sensor, false, 8, 1, 1, false, 1, true, 4);
+        } else {
+            ret = set_pll(sensor, false, 20, 1, 1, false, 1, true, 8);
+        }
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
+    }
+    return ret;
+
+fail:
+    sensor->status.framesize = old_framesize;
+    ESP_LOGE(TAG, "Setting framesize to: %ux%u failed", w, h);
+    return ret;
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.hmirror = enable;
+    ret = set_image_options(sensor);
+    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 = set_image_options(sensor);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set v-flip to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_quality(sensor_t *sensor, int qs)
+{
+    int ret = 0;
+    ret = write_reg(sensor->slv_addr, COMPRESSION_CTRL07, qs & 0x3f);
+    if (ret == 0) {
+        sensor->status.quality = qs;
+        ESP_LOGD(TAG, "Set quality to: %d", qs);
+    }
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, PRE_ISP_TEST_SETTING_1, TEST_COLOR_BAR, enable);
+    if (ret == 0) {
+        sensor->status.colorbar = enable;
+        ESP_LOGD(TAG, "Set colorbar to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_gain_ctrl(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AGC_MANUALEN, !enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set gain_ctrl to: %d", enable);
+        sensor->status.agc = enable;
+    }
+    return ret;
+}
+
+static int set_exposure_ctrl(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AEC_MANUALEN, !enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set exposure_ctrl to: %d", enable);
+        sensor->status.aec = enable;
+    }
+    return ret;
+}
+
+static int set_whitebal(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x01, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set awb to: %d", enable);
+        sensor->status.awb = enable;
+    }
+    return ret;
+}
+
+//Advanced AWB
+static int set_dcw_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x5183, 0x80, !enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set dcw to: %d", enable);
+        sensor->status.dcw = enable;
+    }
+    return ret;
+}
+
+//night mode enable
+static int set_aec2(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x3a00, 0x04, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set aec2 to: %d", enable);
+        sensor->status.aec2 = enable;
+    }
+    return ret;
+}
+
+static int set_bpc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x04, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set bpc to: %d", enable);
+        sensor->status.bpc = enable;
+    }
+    return ret;
+}
+
+static int set_wpc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x02, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set wpc to: %d", enable);
+        sensor->status.wpc = enable;
+    }
+    return ret;
+}
+
+//Gamma enable
+static int set_raw_gma_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x20, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set raw_gma to: %d", enable);
+        sensor->status.raw_gma = enable;
+    }
+    return ret;
+}
+
+static int set_lenc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x80, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set lenc to: %d", enable);
+        sensor->status.lenc = enable;
+    }
+    return ret;
+}
+
+static int get_agc_gain(sensor_t *sensor)
+{
+    int ra = read_reg(sensor->slv_addr, 0x350a);
+    if (ra < 0) {
+        return 0;
+    }
+    int rb = read_reg(sensor->slv_addr, 0x350b);
+    if (rb < 0) {
+        return 0;
+    }
+    int res = (rb & 0xF0) >> 4 | (ra & 0x03) << 4;
+    if (rb & 0x0F) {
+        res += 1;
+    }
+    return res;
+}
+
+//real gain
+static int set_agc_gain(sensor_t *sensor, int gain)
+{
+    int ret = 0;
+    if(gain < 0) {
+        gain = 0;
+    } else if(gain > 64) {
+        gain = 64;
+    }
+
+    //gain value is 6.4 bits float
+    //in order to use the max range, we deduct 1/16
+    int gainv = gain << 4;
+    if(gainv){
+        gainv -= 1;
+    }
+
+    ret = write_reg(sensor->slv_addr, 0x350a, gainv >> 8) || write_reg(sensor->slv_addr, 0x350b, gainv & 0xff);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set agc_gain to: %d", gain);
+        sensor->status.agc_gain = gain;
+    }
+    return ret;
+}
+
+static int get_aec_value(sensor_t *sensor)
+{
+    int ra = read_reg(sensor->slv_addr, 0x3500);
+    if (ra < 0) {
+        return 0;
+    }
+    int rb = read_reg(sensor->slv_addr, 0x3501);
+    if (rb < 0) {
+        return 0;
+    }
+    int rc = read_reg(sensor->slv_addr, 0x3502);
+    if (rc < 0) {
+        return 0;
+    }
+    int res = (ra & 0x0F) << 12 | (rb & 0xFF) << 4 | (rc & 0xF0) >> 4;
+    return res;
+}
+
+static int set_aec_value(sensor_t *sensor, int value)
+{
+    int ret = 0, max_val = 0;
+    max_val = read_reg16(sensor->slv_addr, 0x380e);
+    if (max_val < 0) {
+        ESP_LOGE(TAG, "Could not read max aec_value");
+        return -1;
+    }
+    if (value > max_val) {
+        value =max_val;
+    }
+
+    ret =  write_reg(sensor->slv_addr, 0x3500, (value >> 12) & 0x0F)
+        || write_reg(sensor->slv_addr, 0x3501, (value >> 4) & 0xFF)
+        || write_reg(sensor->slv_addr, 0x3502, (value << 4) & 0xF0);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set aec_value to: %d / %d", value, max_val);
+        sensor->status.aec_value = value;
+    }
+    return ret;
+}
+
+static int set_ae_level(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    if (level < -5 || level > 5) {
+        return -1;
+    }
+    //good targets are between 5 and 115
+    int target_level = ((level + 5) * 10) + 5;
+
+    int level_high, level_low;
+    int fast_high, fast_low;
+
+    level_low = target_level * 23 / 25; //0.92 (0.46)
+    level_high = target_level * 27 / 25; //1.08 (2.08)
+
+    fast_low = level_low >> 1;
+    fast_high = level_high << 1;
+
+    if(fast_high>255) {
+        fast_high = 255;
+    }
+
+    ret =  write_reg(sensor->slv_addr, 0x3a0f, level_high)
+        || write_reg(sensor->slv_addr, 0x3a10, level_low)
+        || write_reg(sensor->slv_addr, 0x3a1b, level_high)
+        || write_reg(sensor->slv_addr, 0x3a1e, level_low)
+        || write_reg(sensor->slv_addr, 0x3a11, fast_high)
+        || write_reg(sensor->slv_addr, 0x3a1f, fast_low);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set ae_level to: %d", level);
+        sensor->status.ae_level = level;
+    }
+    return ret;
+}
+
+static int set_wb_mode(sensor_t *sensor, int mode)
+{
+    int ret = 0;
+    if (mode < 0 || mode > 4) {
+        return -1;
+    }
+
+    ret = write_reg(sensor->slv_addr, 0x3406, (mode != 0));
+    if (ret) {
+        return ret;
+    }
+    switch (mode) {
+        case 1://Sunny
+            ret  = write_reg16(sensor->slv_addr, 0x3400, 0x5e0) //AWB R GAIN
+                || write_reg16(sensor->slv_addr, 0x3402, 0x410) //AWB G GAIN
+                || write_reg16(sensor->slv_addr, 0x3404, 0x540);//AWB B GAIN
+            break;
+        case 2://Cloudy
+            ret  = write_reg16(sensor->slv_addr, 0x3400, 0x650) //AWB R GAIN
+                || write_reg16(sensor->slv_addr, 0x3402, 0x410) //AWB G GAIN
+                || write_reg16(sensor->slv_addr, 0x3404, 0x4f0);//AWB B GAIN
+            break;
+        case 3://Office
+            ret  = write_reg16(sensor->slv_addr, 0x3400, 0x520) //AWB R GAIN
+                || write_reg16(sensor->slv_addr, 0x3402, 0x410) //AWB G GAIN
+                || write_reg16(sensor->slv_addr, 0x3404, 0x660);//AWB B GAIN
+            break;
+        case 4://HOME
+            ret  = write_reg16(sensor->slv_addr, 0x3400, 0x420) //AWB R GAIN
+                || write_reg16(sensor->slv_addr, 0x3402, 0x3f0) //AWB G GAIN
+                || write_reg16(sensor->slv_addr, 0x3404, 0x710);//AWB B GAIN
+            break;
+        default://AUTO
+            break;
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set wb_mode to: %d", mode);
+        sensor->status.wb_mode = mode;
+    }
+    return ret;
+}
+
+static int set_awb_gain_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    int old_mode = sensor->status.wb_mode;
+    int mode = enable?old_mode:0;
+
+    ret = set_wb_mode(sensor, mode);
+
+    if (ret == 0) {
+        sensor->status.wb_mode = old_mode;
+        ESP_LOGD(TAG, "Set awb_gain to: %d", enable);
+        sensor->status.awb_gain = enable;
+    }
+    return ret;
+}
+
+static int set_special_effect(sensor_t *sensor, int effect)
+{
+    int ret=0;
+    if (effect < 0 || effect > 6) {
+        return -1;
+    }
+
+    uint8_t * regs = (uint8_t *)sensor_special_effects[effect];
+    ret =  write_reg(sensor->slv_addr, 0x5580, regs[0])
+        || write_reg(sensor->slv_addr, 0x5583, regs[1])
+        || write_reg(sensor->slv_addr, 0x5584, regs[2])
+        || write_reg(sensor->slv_addr, 0x5003, regs[3]);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set special_effect to: %d", effect);
+        sensor->status.special_effect = effect;
+    }
+    return ret;
+}
+
+static int set_brightness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    uint8_t value = 0;
+    bool negative = false;
+
+    switch (level) {
+        case 3:
+            value = 0x30;
+            break;
+        case 2:
+            value = 0x20;
+            break;
+        case 1:
+            value = 0x10;
+            break;
+        case -1:
+            value = 0x10;
+            negative = true;
+            break;
+        case -2:
+            value = 0x20;
+            negative = true;
+            break;
+        case -3:
+            value = 0x30;
+            negative = true;
+            break;
+        default: // 0
+            break;
+    }
+
+    ret = write_reg(sensor->slv_addr, 0x5587, value);
+    if (ret == 0) {
+        ret = write_reg_bits(sensor->slv_addr, 0x5588, 0x08, negative);
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set brightness to: %d", level);
+        sensor->status.brightness = level;
+    }
+    return ret;
+}
+
+static int set_contrast(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    if(level > 3 || level < -3) {
+        return -1;
+    }
+    ret = write_reg(sensor->slv_addr, 0x5586, (level + 4) << 3);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set contrast to: %d", level);
+        sensor->status.contrast = level;
+    }
+    return ret;
+}
+
+static int set_saturation(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    if(level > 4 || level < -4) {
+        return -1;
+    }
+
+    uint8_t * regs = (uint8_t *)sensor_saturation_levels[level+4];
+    for(int i=0; i<11; i++) {
+        ret = write_reg(sensor->slv_addr, 0x5381 + i, regs[i]);
+        if (ret) {
+            break;
+        }
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set saturation to: %d", level);
+        sensor->status.saturation = level;
+    }
+    return ret;
+}
+
+static int set_sharpness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    if(level > 3 || level < -3) {
+        return -1;
+    }
+
+    uint8_t mt_offset_2 = (level + 3) * 8;
+    uint8_t mt_offset_1 = mt_offset_2 + 1;
+
+    ret = write_reg_bits(sensor->slv_addr, 0x5308, 0x40, false)//0x40 means auto
+        || write_reg(sensor->slv_addr, 0x5300, 0x10)
+        || write_reg(sensor->slv_addr, 0x5301, 0x10)
+        || write_reg(sensor->slv_addr, 0x5302, mt_offset_1)
+        || write_reg(sensor->slv_addr, 0x5303, mt_offset_2)
+        || write_reg(sensor->slv_addr, 0x5309, 0x10)
+        || write_reg(sensor->slv_addr, 0x530a, 0x10)
+        || write_reg(sensor->slv_addr, 0x530b, 0x04)
+        || write_reg(sensor->slv_addr, 0x530c, 0x06);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set sharpness to: %d", level);
+        sensor->status.sharpness = level;
+    }
+    return ret;
+}
+
+static int set_gainceiling(sensor_t *sensor, gainceiling_t level)
+{
+    int ret = 0, l = (int)level;
+
+    ret = write_reg(sensor->slv_addr, 0x3A18, (l >> 8) & 3)
+       || write_reg(sensor->slv_addr, 0x3A19, l & 0xFF);
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set gainceiling to: %d", l);
+        sensor->status.gainceiling = l;
+    }
+    return ret;
+}
+
+static int get_denoise(sensor_t *sensor)
+{
+    if (!check_reg_mask(sensor->slv_addr, 0x5308, 0x10)) {
+        return 0;
+    }
+    return (read_reg(sensor->slv_addr, 0x5306) / 4) + 1;
+}
+
+static int set_denoise(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    if (level < 0 || level > 8) {
+        return -1;
+    }
+
+    ret = write_reg_bits(sensor->slv_addr, 0x5308, 0x10, level > 0);
+    if (ret == 0 && level > 0) {
+        ret = write_reg(sensor->slv_addr, 0x5306, (level - 1) * 4);
+    }
+
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set denoise to: %d", level);
+        sensor->status.denoise = level;
+    }
+    return ret;
+}
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = 0, ret2 = 0;
+    if(mask > 0xFF){
+        ret = read_reg16(sensor->slv_addr, reg);
+        if(ret >= 0 && mask > 0xFFFF){
+            ret2 = read_reg(sensor->slv_addr, reg+2);
+            if(ret2 >= 0){
+                ret = (ret << 8) | ret2 ;
+            } else {
+                ret = ret2;
+            }
+        }
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if(ret > 0){
+        ret &= mask;
+    }
+    return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0, ret2 = 0;
+    if(mask > 0xFF){
+        ret = read_reg16(sensor->slv_addr, reg);
+        if(ret >= 0 && mask > 0xFFFF){
+            ret2 = read_reg(sensor->slv_addr, reg+2);
+            if(ret2 >= 0){
+                ret = (ret << 8) | ret2 ;
+            } else {
+                ret = ret2;
+            }
+        }
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if(ret < 0){
+        return ret;
+    }
+    value = (ret & ~mask) | (value & mask);
+    if(mask > 0xFFFF){
+        ret = write_reg16(sensor->slv_addr, reg, value >> 8);
+        if(ret >= 0){
+            ret = write_reg(sensor->slv_addr, reg+2, value & 0xFF);
+        }
+    } else if(mask > 0xFF){
+        ret = write_reg16(sensor->slv_addr, reg, value);
+    } else {
+        ret = write_reg(sensor->slv_addr, reg, value);
+    }
+    return ret;
+}
+
+static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning)
+{
+    int ret = 0;
+    ret  = write_addr_reg(sensor->slv_addr, X_ADDR_ST_H, startX, startY)
+        || write_addr_reg(sensor->slv_addr, X_ADDR_END_H, endX, endY)
+        || write_addr_reg(sensor->slv_addr, X_OFFSET_H, offsetX, offsetY)
+        || write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, totalX, totalY)
+        || write_addr_reg(sensor->slv_addr, X_OUTPUT_SIZE_H, outputX, outputY)
+        || write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x20, scale);
+    if(!ret){
+        sensor->status.scale = scale;
+        sensor->status.binning = binning;
+        ret = set_image_options(sensor);
+    }
+    return ret;
+}
+
+static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div)
+{
+    int ret = 0;
+    ret = set_pll(sensor, bypass > 0, multiplier, sys_div, pre_div, root_2x > 0, seld5, pclk_manual > 0, pclk_div);
+    return ret;
+}
+
+static int set_xclk(sensor_t *sensor, int timer, int xclk)
+{
+    int ret = 0;
+    sensor->xclk_freq_hz = xclk * 1000000U;
+    ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
+    return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    sensor->status.brightness = 0;
+    sensor->status.contrast = 0;
+    sensor->status.saturation = 0;
+    sensor->status.sharpness = (read_reg(sensor->slv_addr, 0x5303) / 8) - 3;
+    sensor->status.denoise = get_denoise(sensor);
+    sensor->status.ae_level = 0;
+    sensor->status.gainceiling = read_reg16(sensor->slv_addr, 0x3A18) & 0x3FF;
+    sensor->status.awb = check_reg_mask(sensor->slv_addr, ISP_CONTROL_01, 0x01);
+    sensor->status.dcw = !check_reg_mask(sensor->slv_addr, 0x5183, 0x80);
+    sensor->status.agc = !check_reg_mask(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AGC_MANUALEN);
+    sensor->status.aec = !check_reg_mask(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AEC_MANUALEN);
+    sensor->status.hmirror = check_reg_mask(sensor->slv_addr, TIMING_TC_REG21, TIMING_TC_REG21_HMIRROR);
+    sensor->status.vflip = check_reg_mask(sensor->slv_addr, TIMING_TC_REG20, TIMING_TC_REG20_VFLIP);
+    sensor->status.colorbar = check_reg_mask(sensor->slv_addr, PRE_ISP_TEST_SETTING_1, TEST_COLOR_BAR);
+    sensor->status.bpc = check_reg_mask(sensor->slv_addr, 0x5000, 0x04);
+    sensor->status.wpc = check_reg_mask(sensor->slv_addr, 0x5000, 0x02);
+    sensor->status.raw_gma = check_reg_mask(sensor->slv_addr, 0x5000, 0x20);
+    sensor->status.lenc = check_reg_mask(sensor->slv_addr, 0x5000, 0x80);
+    sensor->status.quality = read_reg(sensor->slv_addr, COMPRESSION_CTRL07) & 0x3f;
+    sensor->status.special_effect = 0;
+    sensor->status.wb_mode = 0;
+    sensor->status.awb_gain = check_reg_mask(sensor->slv_addr, 0x3406, 0x01);
+    sensor->status.agc_gain = get_agc_gain(sensor);
+    sensor->status.aec_value = get_aec_value(sensor);
+    sensor->status.aec2 = check_reg_mask(sensor->slv_addr, 0x3a00, 0x04);
+    return 0;
+}
+
+int ov5640_detect(int slv_addr, sensor_id_t *id)
+{
+    if (OV5640_SCCB_ADDR == slv_addr) {
+        uint8_t h = SCCB_Read16(slv_addr, 0x300A);
+        uint8_t l = SCCB_Read16(slv_addr, 0x300B);
+        uint16_t PID = (h<<8) | l;
+        if (OV5640_PID == PID) {
+            id->PID = PID;
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int ov5640_init(sensor_t *sensor)
+{
+    sensor->reset = reset;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_contrast = set_contrast;
+    sensor->set_brightness = set_brightness;
+    sensor->set_saturation = set_saturation;
+    sensor->set_sharpness = set_sharpness;
+    sensor->set_gainceiling = set_gainceiling;
+    sensor->set_quality = set_quality;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_gain_ctrl = set_gain_ctrl;
+    sensor->set_exposure_ctrl = set_exposure_ctrl;
+    sensor->set_whitebal = set_whitebal;
+    sensor->set_hmirror = set_hmirror;
+    sensor->set_vflip = set_vflip;
+    sensor->init_status = init_status;
+    sensor->set_aec2 = set_aec2;
+    sensor->set_aec_value = set_aec_value;
+    sensor->set_special_effect = set_special_effect;
+    sensor->set_wb_mode = set_wb_mode;
+    sensor->set_ae_level = set_ae_level;
+    sensor->set_dcw = set_dcw_dsp;
+    sensor->set_bpc = set_bpc_dsp;
+    sensor->set_wpc = set_wpc_dsp;
+    sensor->set_awb_gain = set_awb_gain_dsp;
+    sensor->set_agc_gain = set_agc_gain;
+    sensor->set_raw_gma = set_raw_gma_dsp;
+    sensor->set_lenc = set_lenc_dsp;
+    sensor->set_denoise = set_denoise;
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_res_raw = set_res_raw;
+    sensor->set_pll = _set_pll;
+    sensor->set_xclk = set_xclk;
+    return 0;
+}

+ 457 - 0
std/camera/sensors/ov7670.c

@@ -0,0 +1,457 @@
+/*
+ * This file is part of the OpenMV project.
+ * author: Juan Schiavoni <juanjoseschiavoni@hotmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV7725 driver.
+ *
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sccb.h"
+#include "ov7670.h"
+#include "ov7670_regs.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include <stdio.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 = "ov7760";
+#endif
+
+static int ov7670_clkrc = 0x01;
+
+/*
+ * The default register settings, as obtained from OmniVision.  There
+ * is really no making sense of most of these - lots of "reserved" values
+ * and such.
+ *
+ * These settings give VGA YUYV.
+ */
+struct regval_list {
+	uint8_t reg_num;
+	uint8_t value;
+};
+
+static struct regval_list ov7670_default_regs[] = {
+    /* Sensor automatically sets output window when resolution changes. */    
+    {TSLB, 0x04}, 
+    
+    /* Frame rate 30 fps at 12 Mhz clock */    
+	{CLKRC, 0x00},  
+	{DBLV,  0x4A},  
+
+    {COM10, COM10_VSYNC_NEG | COM10_PCLK_FREE},
+
+    /* Improve white balance */ 
+	{COM4, 0x40},  
+    
+    /* Improve color */   
+    {RSVD_B0, 0x84},  
+
+    /* Enable 50/60 Hz auto detection */
+    {COM11, COM11_EXP|COM11_HZAUTO}, 
+
+    /* Disable some delays */
+	{HSYST, 0},
+    {HSYEN, 0},   
+
+    {MVFP, MVFP_SUN}, 
+
+	/* More reserved magic, some of which tweaks white balance */
+	{AWBC1, 0x0a},		
+    {AWBC2, 0xf0},
+	{AWBC3, 0x34},		
+    {AWBC4, 0x58},
+	{AWBC5, 0x28},		
+    {AWBC6, 0x3a},
+	
+    {AWBCTR3, 0x0a},		
+    {AWBCTR2, 0x55},
+	{AWBCTR1, 0x11},		
+    {AWBCTR0, 0x9e}, 
+
+    {COM8, COM8_FAST_AUTO|COM8_STEP_UNLIMIT|COM8_AGC_EN|COM8_AEC_EN|COM8_AWB_EN},
+
+    /* End marker is FF because in ov7670 the address of GAIN 0 and default value too. */
+    {0xFF, 0xFF},  
+};
+
+static struct regval_list ov7670_fmt_yuv422[] = {
+	{ COM7,     0x0                         },  /* Selects YUV mode */
+	{ RGB444,   0                           },  /* No RGB444 please */
+	{ COM1,     0                           },  /* CCIR601 */
+	{ COM15,    COM15_R00FF                 },
+    { MVFP,     MVFP_SUN                    }, 
+	{ COM9,     0x6A                        },  /* 128x gain ceiling; 0x8 is reserved bit */
+	{ MTX1,     0x80                        },  /* "matrix coefficient 1" */
+	{ MTX2,     0x80                        }, 	/* "matrix coefficient 2" */
+	{ MTX3,     0                           },  /* vb */
+	{ MTX4,     0x22                        }, 	/* "matrix coefficient 4" */
+	{ MTX5,     0x5e                        },  /* "matrix coefficient 5" */
+	{ MTX6,     0x80                        },  /* "matrix coefficient 6" */
+	{ COM13,    COM13_UVSAT                 },
+	{ 0xff,     0xff                        },  /* END MARKER */
+};
+
+static struct regval_list ov7670_fmt_rgb565[] = {
+	{ COM7,     COM7_FMT_RGB565             },	/* Selects RGB mode */
+	{ RGB444,   0                           },	/* No RGB444 please */
+	{ COM1,     0x0                         },	/* CCIR601 */
+	{ COM15,    COM15_RGB565 |COM15_R00FF   },
+    { MVFP,     MVFP_SUN                    },   
+	{ COM9,     0x6A                        }, 	/* 128x gain ceiling; 0x8 is reserved bit */
+	{ MTX1,     0xb3                        }, 	/* "matrix coefficient 1" */
+	{ MTX2,     0xb3                        }, 	/* "matrix coefficient 2" */
+	{ MTX3,     0                           },	/* vb */
+	{ MTX4,     0x3d                        }, 	/* "matrix coefficient 4" */
+	{ MTX5,     0xa7                        }, 	/* "matrix coefficient 5" */
+	{ MTX6,     0xe4                        }, 	/* "matrix coefficient 6" */
+	{ COM13,    COM13_UVSAT                 },
+	{ 0xff,     0xff                        },  /* END MARKER */
+};
+
+
+static struct regval_list ov7670_vga[] = {
+    { COM3,                 0x00 },
+    { COM14,                0x00 },
+    { SCALING_XSC,          0x3A },
+    { SCALING_YSC,          0x35 },
+    { SCALING_DCWCTR,       0x11 },
+    { SCALING_PCLK_DIV,     0xF0 },
+    { SCALING_PCLK_DELAY,   0x02 },
+    { 0xff, 0xff },
+};
+
+static struct regval_list ov7670_qvga[] = {
+    { COM3,                 0x04 },
+    { COM14,                0x19 },
+    { SCALING_XSC,          0x3A },
+    { SCALING_YSC,          0x35 },
+    { SCALING_DCWCTR,       0x11 },
+    { SCALING_PCLK_DIV,     0xF1 },
+    { SCALING_PCLK_DELAY,   0x02 },
+    { 0xff, 0xff },
+};
+
+static struct regval_list ov7670_qqvga[] = {
+	{ COM3,                 0x04 }, //DCW enable	
+	{ COM14,                0x1a }, //pixel clock divided by 4, manual scaling enable, DCW and PCLK controlled by register	
+	{ SCALING_XSC,          0x3a },	
+	{ SCALING_YSC,          0x35 },
+	{ SCALING_DCWCTR,       0x22 }, //downsample by 4	
+	{ SCALING_PCLK_DIV,     0xf2 }, //pixel clock divided by 4	
+	{ SCALING_PCLK_DELAY,   0x02 },
+    { 0xff, 0xff },
+};
+
+/*
+ * Write a list of register settings; ff/ff stops the process.
+ */
+static int ov7670_write_array(sensor_t *sensor, struct regval_list *vals)
+{
+int ret = 0;
+	
+	while ( (vals->reg_num != 0xff || vals->value != 0xff) && (ret == 0) ) {
+        ret = SCCB_Write(sensor->slv_addr, vals->reg_num, vals->value);
+
+	    ESP_LOGD(TAG, "reset reg %02X, W(%02X) R(%02X)", vals->reg_num, 
+                        vals->value, SCCB_Read(sensor->slv_addr, vals->reg_num) );
+		
+		vals++;
+	}
+
+    return ret;
+}
+
+/*
+ * Calculate the frame control registers.
+ */
+static int ov7670_frame_control(sensor_t *sensor, int hstart, int hstop, int vstart, int vstop)
+{
+struct regval_list frame[7];
+
+    frame[0].reg_num = HSTART;
+    frame[0].value = (hstart >> 3);
+
+    frame[1].reg_num = HSTOP;
+    frame[1].value = (hstop >> 3);
+
+    frame[2].reg_num = HREF;
+    frame[2].value = (((hstop & 0x07) << 3) | (hstart & 0x07));
+    
+    frame[3].reg_num = VSTART;
+    frame[3].value = (vstart >> 2);
+    
+    frame[4].reg_num = VSTOP;
+    frame[4].value = (vstop >> 2);
+
+    frame[5].reg_num = VREF;
+    frame[5].value = (((vstop & 0x02) << 2) | (vstart & 0x02));
+
+    /* End mark */
+    frame[5].reg_num = 0xFF;
+    frame[5].value = 0xFF;
+
+    return ov7670_write_array(sensor, frame);
+}
+
+static int reset(sensor_t *sensor)
+{
+    int ret;
+
+    // Reset all registers
+    SCCB_Write(sensor->slv_addr, COM7, COM7_RESET);
+
+    // Delay 10 ms
+    vTaskDelay(10 / portTICK_PERIOD_MS);
+
+    ret = ov7670_write_array(sensor, ov7670_default_regs);
+
+    // Delay
+    vTaskDelay(30 / portTICK_PERIOD_MS);
+
+    return ret;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+int ret;
+
+    switch (pixformat) {
+        case PIXFORMAT_RGB565:
+        case PIXFORMAT_RGB888:
+            ret = ov7670_write_array(sensor, ov7670_fmt_rgb565);
+        break;
+ 
+        case PIXFORMAT_YUV422:
+        case PIXFORMAT_GRAYSCALE:
+	    default:
+            ret = ov7670_write_array(sensor, ov7670_fmt_yuv422);
+        break;
+    }
+
+    vTaskDelay(30 / portTICK_PERIOD_MS);
+
+    /*
+	 * If we're running RGB565, we must rewrite clkrc after setting
+	 * the other parameters or the image looks poor.  If we're *not*
+	 * doing RGB565, we must not rewrite clkrc or the image looks
+	 * *really* poor.
+	 *
+	 * (Update) Now that we retain clkrc state, we should be able
+	 * to write it unconditionally, and that will make the frame
+	 * rate persistent too.
+	 */
+    if (pixformat == PIXFORMAT_RGB565) {
+        ret = SCCB_Write(sensor->slv_addr, CLKRC, ov7670_clkrc); 
+    }
+
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+   int ret;
+
+    // store clkrc before changing window settings...
+    ov7670_clkrc =  SCCB_Read(sensor->slv_addr, CLKRC);
+     
+	switch (framesize){
+        case FRAMESIZE_VGA:
+            if( (ret = ov7670_write_array(sensor, ov7670_vga)) == 0 ) {
+                /* These values from Omnivision */
+                ret = ov7670_frame_control(sensor, 158, 14, 10, 490);
+            }
+        break;
+	    case FRAMESIZE_QVGA:
+            if( (ret = ov7670_write_array(sensor, ov7670_qvga)) == 0 ) {
+                /* These values from Omnivision */
+                ret = ov7670_frame_control(sensor, 158, 14, 10, 490);
+            }
+        break;
+	    case FRAMESIZE_QQVGA:
+            if( (ret = ov7670_write_array(sensor, ov7670_qqvga)) == 0 ) {
+                /* These values from Omnivision */
+                ret = ov7670_frame_control(sensor, 158, 14, 10, 490);
+            }
+        break; 
+
+        default:
+            ret = -1;   
+    }
+
+    vTaskDelay(30 / portTICK_PERIOD_MS);
+
+    if (ret == 0) {
+        sensor->status.framesize = framesize;
+    }
+
+	return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+    uint8_t ret = 0;
+    // Read register scaling_xsc
+    uint8_t reg = SCCB_Read(sensor->slv_addr, SCALING_XSC);
+
+    // Pattern to set color bar bit[0]=0 in every case
+    reg = SCALING_XSC_CBAR(reg);
+
+    // Write pattern to SCALING_XSC
+    ret = SCCB_Write(sensor->slv_addr, SCALING_XSC, reg);
+
+    // Read register scaling_ysc
+    reg = SCCB_Read(sensor->slv_addr, SCALING_YSC);
+
+    // Pattern to set color bar bit[0]=0 in every case
+    reg = SCALING_YSC_CBAR(reg, enable);
+
+    // Write pattern to SCALING_YSC
+    ret = ret | SCCB_Write(sensor->slv_addr, SCALING_YSC, reg);
+
+    // return 0 or 0xFF
+    return ret;
+}
+
+static int set_whitebal(sensor_t *sensor, int enable)
+{
+    // Read register COM8
+    uint8_t reg = SCCB_Read(sensor->slv_addr, COM8);
+
+    // Set white bal on/off
+    reg = COM8_SET_AWB(reg, enable);
+
+    // Write back register COM8
+    return SCCB_Write(sensor->slv_addr, COM8, reg);
+}
+
+static int set_gain_ctrl(sensor_t *sensor, int enable)
+{
+    // Read register COM8
+    uint8_t reg = SCCB_Read(sensor->slv_addr, COM8);
+
+    // Set white bal on/off
+    reg = COM8_SET_AGC(reg, enable);
+
+    // Write back register COM8
+    return SCCB_Write(sensor->slv_addr, COM8, reg);
+}
+
+static int set_exposure_ctrl(sensor_t *sensor, int enable)
+{
+    // Read register COM8
+    uint8_t reg = SCCB_Read(sensor->slv_addr, COM8);
+
+    // Set white bal on/off
+    reg = COM8_SET_AEC(reg, enable);
+
+    // Write back register COM8
+    return SCCB_Write(sensor->slv_addr, COM8, reg);
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    // Read register MVFP
+    uint8_t reg = SCCB_Read(sensor->slv_addr, MVFP);
+
+    // Set mirror on/off
+    reg = MVFP_SET_MIRROR(reg, enable);
+
+    // Write back register MVFP
+    return SCCB_Write(sensor->slv_addr, MVFP, reg);
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+    // Read register MVFP
+    uint8_t reg = SCCB_Read(sensor->slv_addr, MVFP);
+
+    // Set mirror on/off
+    reg = MVFP_SET_FLIP(reg, enable);
+
+    // Write back register MVFP
+    return SCCB_Write(sensor->slv_addr, MVFP, reg);
+}
+
+static int init_status(sensor_t *sensor)
+{
+    sensor->status.awb = 0;
+    sensor->status.aec = 0;
+    sensor->status.agc = 0;
+    sensor->status.hmirror = 0;
+    sensor->status.vflip = 0;
+    sensor->status.colorbar = 0;
+    return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val){ return -1; }
+static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
+
+int ov7670_detect(int slv_addr, sensor_id_t *id)
+{
+    if (OV7670_SCCB_ADDR == slv_addr) {
+        SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
+        uint16_t PID = SCCB_Read(slv_addr, 0x0A);
+        if (OV7670_PID == PID) {
+            id->PID = PID;
+            id->VER = SCCB_Read(slv_addr, REG_VER);
+            id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
+            id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int ov7670_init(sensor_t *sensor)
+{
+    // Set function pointers
+    sensor->reset = reset;
+    sensor->init_status = init_status;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_whitebal = set_whitebal;
+    sensor->set_gain_ctrl = set_gain_ctrl;
+    sensor->set_exposure_ctrl = set_exposure_ctrl;
+    sensor->set_hmirror = set_hmirror;
+    sensor->set_vflip = set_vflip;
+
+    //not supported
+    sensor->set_brightness= set_dummy;
+    sensor->set_saturation= set_dummy;
+    sensor->set_quality = set_dummy;
+    sensor->set_gainceiling = set_gainceiling_dummy;
+    sensor->set_aec2 = set_dummy;
+    sensor->set_aec_value = set_dummy;
+    sensor->set_special_effect = set_dummy;
+    sensor->set_wb_mode = set_dummy;
+    sensor->set_ae_level = set_dummy;
+    sensor->set_dcw = set_dummy;
+    sensor->set_bpc = set_dummy;
+    sensor->set_wpc = set_dummy;
+    sensor->set_awb_gain = set_dummy;
+    sensor->set_agc_gain = set_dummy;
+    sensor->set_raw_gma = set_dummy;
+    sensor->set_lenc = set_dummy;
+    sensor->set_sharpness = set_dummy;
+    sensor->set_denoise = set_dummy;
+
+    // Retrieve sensor's signature
+    sensor->id.MIDH = SCCB_Read(sensor->slv_addr, REG_MIDH);
+    sensor->id.MIDL = SCCB_Read(sensor->slv_addr, REG_MIDL);
+    sensor->id.PID = SCCB_Read(sensor->slv_addr, REG_PID);
+    sensor->id.VER = SCCB_Read(sensor->slv_addr, REG_VER);
+    
+    ESP_LOGD(TAG, "OV7670 Attached");
+    
+    return 0;
+}

+ 575 - 0
std/camera/sensors/ov7725.c

@@ -0,0 +1,575 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV7725 driver.
+ *
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "sccb.h"
+#include "xclk.h"
+#include "ov7725.h"
+#include "ov7725_regs.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char* TAG = "ov7725";
+#endif
+
+
+static const uint8_t default_regs[][2] = {
+    {COM3,          COM3_SWAP_YUV},
+    {COM7,          COM7_RES_QVGA | COM7_FMT_YUV},
+
+    {COM4,          0x01 | 0x00}, /* bypass PLL (0x00:off, 0x40:4x, 0x80:6x, 0xC0:8x) */
+    {CLKRC,         0x80 | 0x03}, /* Res/Bypass pre-scalar (0x40:bypass, 0x00-0x3F:prescaler PCLK=XCLK/(prescaler + 1)/2 ) */
+
+    // QVGA Window Size
+    {HSTART,        0x3F},
+    {HSIZE,         0x50},
+    {VSTART,        0x03},
+    {VSIZE,         0x78},
+    {HREF,          0x00},
+
+    // Scale down to QVGA Resolution
+    {HOUTSIZE,      0x50},
+    {VOUTSIZE,      0x78},
+    {EXHCH,         0x00},
+
+    {COM12,         0x03},
+    {TGT_B,         0x7F},
+    {FIXGAIN,       0x09},
+    {AWB_CTRL0,     0xE0},
+    {DSP_CTRL1,     0xFF},
+
+    {DSP_CTRL2,     DSP_CTRL2_VDCW_EN | DSP_CTRL2_HDCW_EN | DSP_CTRL2_HZOOM_EN | DSP_CTRL2_VZOOM_EN},
+
+    {DSP_CTRL3,     0x00},
+    {DSP_CTRL4,     0x00},
+    {DSPAUTO,       0xFF},
+
+    {COM8,          0xF0},
+    {COM6,          0xC5},
+    {COM9,          0x11},
+    {COM10,         COM10_VSYNC_NEG | COM10_PCLK_FREE}, //Invert VSYNC and MASK PCLK
+    {BDBASE,        0x7F},
+    {DBSTEP,        0x03},
+    {AEW,           0x75},
+    {AEB,           0x64},
+    {VPT,           0xA1},
+    {EXHCL,         0x00},
+    {AWB_CTRL3,     0xAA},
+    {COM8,          0xFF},
+
+    //Gamma
+    {GAM1,          0x0C},
+    {GAM2,          0x16},
+    {GAM3,          0x2A},
+    {GAM4,          0x4E},
+    {GAM5,          0x61},
+    {GAM6,          0x6F},
+    {GAM7,          0x7B},
+    {GAM8,          0x86},
+    {GAM9,          0x8E},
+    {GAM10,         0x97},
+    {GAM11,         0xA4},
+    {GAM12,         0xAF},
+    {GAM13,         0xC5},
+    {GAM14,         0xD7},
+    {GAM15,         0xE8},
+
+    {SLOP,          0x20},
+    {EDGE1,         0x05},
+    {EDGE2,         0x03},
+    {EDGE3,         0x00},
+    {DNSOFF,        0x01},
+
+    {MTX1,          0xB0},
+    {MTX2,          0x9D},
+    {MTX3,          0x13},
+    {MTX4,          0x16},
+    {MTX5,          0x7B},
+    {MTX6,          0x91},
+    {MTX_CTRL,      0x1E},
+
+    {BRIGHTNESS,    0x08},
+    {CONTRAST,      0x30},
+    {UVADJ0,        0x81},
+    {SDE,           (SDE_CONT_BRIGHT_EN | SDE_SATURATION_EN)},
+
+    // For 30 fps/60Hz
+    {DM_LNL,        0x00},
+    {DM_LNH,        0x00},
+    {BDBASE,        0x7F},
+    {DBSTEP,        0x03},
+
+    // Lens Correction, should be tuned with real camera module
+    {LC_RADI,       0x10},
+    {LC_COEF,       0x10},
+    {LC_COEFB,      0x14},
+    {LC_COEFR,      0x17},
+    {LC_CTR,        0x05},
+    {COM5,          0xF5}, //0x65
+
+    {0x00,          0x00},
+};
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
+    if(ret > 0){
+        ret &= mask;
+    }
+    return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0;
+    ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
+    if(ret < 0){
+        return ret;
+    }
+    value = (ret & ~mask) | (value & mask);
+    ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
+    return ret;
+}
+
+static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
+{
+    int ret = 0;
+    ret = SCCB_Read(sensor->slv_addr, reg);
+    if(ret < 0){
+        return ret;
+    }
+    uint8_t mask = ((1 << length) - 1) << offset;
+    value = (ret & ~mask) | ((value << offset) & mask);
+    ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
+    return ret;
+}
+
+static int get_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length)
+{
+    int ret = 0;
+    ret = SCCB_Read(sensor->slv_addr, reg);
+    if(ret < 0){
+        return ret;
+    }
+    uint8_t mask = ((1 << length) - 1) << offset;
+    return (ret & mask) >> offset;
+}
+
+
+static int reset(sensor_t *sensor)
+{
+    int i=0;
+    const uint8_t (*regs)[2];
+
+    // Reset all registers
+    SCCB_Write(sensor->slv_addr, COM7, COM7_RESET);
+
+    // Delay 10 ms
+    vTaskDelay(10 / portTICK_PERIOD_MS);
+
+    // Write default regsiters
+    for (i=0, regs = default_regs; regs[i][0]; i++) {
+        SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
+    }
+
+    // Delay
+    vTaskDelay(30 / portTICK_PERIOD_MS);
+
+    return 0;
+}
+
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret=0;
+    sensor->pixformat = pixformat;
+    // Read register COM7
+    uint8_t reg = SCCB_Read(sensor->slv_addr, COM7);
+
+    switch (pixformat) {
+    case PIXFORMAT_RGB565:
+        reg =  COM7_SET_RGB(reg, COM7_FMT_RGB565);
+        break;
+    case PIXFORMAT_YUV422:
+    case PIXFORMAT_GRAYSCALE:
+        reg =  COM7_SET_FMT(reg, COM7_FMT_YUV);
+        break;
+    default:
+        return -1;
+    }
+
+    // Write back register COM7
+    ret = SCCB_Write(sensor->slv_addr, COM7, reg);
+
+    // Delay
+    vTaskDelay(30 / portTICK_PERIOD_MS);
+
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    int ret=0;
+    if (framesize > FRAMESIZE_VGA) {
+        return -1;
+    }
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+    uint8_t reg = SCCB_Read(sensor->slv_addr, COM7);
+
+    sensor->status.framesize = framesize;
+
+    // Write MSBs
+    ret |= SCCB_Write(sensor->slv_addr, HOUTSIZE, w>>2);
+    ret |= SCCB_Write(sensor->slv_addr, VOUTSIZE, h>>1);
+
+    ret |= SCCB_Write(sensor->slv_addr, HSIZE, w>>2);
+    ret |= SCCB_Write(sensor->slv_addr, VSIZE, h>>1);
+
+    // Write LSBs
+    ret |= SCCB_Write(sensor->slv_addr, HREF, ((w&0x3) | ((h&0x1) << 2)));
+
+    if (framesize < FRAMESIZE_VGA) {
+        // Enable auto-scaling/zooming factors
+        ret |= SCCB_Write(sensor->slv_addr, DSPAUTO, 0xFF);
+
+        ret |= SCCB_Write(sensor->slv_addr, HSTART, 0x3F);
+        ret |= SCCB_Write(sensor->slv_addr, VSTART, 0x03);
+
+        ret |= SCCB_Write(sensor->slv_addr, COM7, reg | COM7_RES_QVGA);
+
+        ret |= SCCB_Write(sensor->slv_addr, CLKRC, 0x80 | 0x01);
+
+    } else {
+        // Disable auto-scaling/zooming factors
+        ret |= SCCB_Write(sensor->slv_addr, DSPAUTO, 0xF3);
+
+        // Clear auto-scaling/zooming factors
+        ret |= SCCB_Write(sensor->slv_addr, SCAL0, 0x00);
+        ret |= SCCB_Write(sensor->slv_addr, SCAL1, 0x00);
+        ret |= SCCB_Write(sensor->slv_addr, SCAL2, 0x00);
+
+        ret |= SCCB_Write(sensor->slv_addr, HSTART, 0x23);
+        ret |= SCCB_Write(sensor->slv_addr, VSTART, 0x07);
+
+        ret |= SCCB_Write(sensor->slv_addr, COM7, reg & ~COM7_RES_QVGA);
+
+        ret |= SCCB_Write(sensor->slv_addr, CLKRC, 0x80 | 0x03);
+    }
+
+    // Delay
+    vTaskDelay(30 / portTICK_PERIOD_MS);
+
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+    int ret=0;
+    uint8_t reg;
+    sensor->status.colorbar = enable;
+
+    // Read reg COM3
+    reg = SCCB_Read(sensor->slv_addr, COM3);
+    // Enable colorbar test pattern output
+    reg = COM3_SET_CBAR(reg, enable);
+    // Write back COM3
+    ret |= SCCB_Write(sensor->slv_addr, COM3, reg);
+
+    // Read reg DSP_CTRL3
+    reg = SCCB_Read(sensor->slv_addr, DSP_CTRL3);
+    // Enable DSP colorbar output
+    reg = DSP_CTRL3_SET_CBAR(reg, enable);
+    // Write back DSP_CTRL3
+    ret |= SCCB_Write(sensor->slv_addr, DSP_CTRL3, reg);
+
+    return ret;
+}
+
+static int set_whitebal(sensor_t *sensor, int enable)
+{
+    if(set_reg_bits(sensor, COM8, 1, 1, enable) >= 0){
+        sensor->status.awb = !!enable;
+    }
+    return sensor->status.awb;
+}
+
+static int set_gain_ctrl(sensor_t *sensor, int enable)
+{
+    if(set_reg_bits(sensor, COM8, 2, 1, enable) >= 0){
+        sensor->status.agc = !!enable;
+    }
+    return sensor->status.agc;
+}
+
+static int set_exposure_ctrl(sensor_t *sensor, int enable)
+{
+    if(set_reg_bits(sensor, COM8, 0, 1, enable) >= 0){
+        sensor->status.aec = !!enable;
+    }
+    return sensor->status.aec;
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    if(set_reg_bits(sensor, COM3, 6, 1, enable) >= 0){
+        sensor->status.hmirror = !!enable;
+    }
+    return sensor->status.hmirror;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+    if(set_reg_bits(sensor, COM3, 7, 1, enable) >= 0){
+        sensor->status.vflip = !!enable;
+    }
+    return sensor->status.vflip;
+}
+
+static int set_dcw_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = set_reg_bits(sensor, 0x65, 2, 1, !enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set dcw to: %d", enable);
+        sensor->status.dcw = enable;
+    }
+    return ret;
+}
+
+static int set_aec2(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = set_reg_bits(sensor, COM8, 7, 1, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set aec2 to: %d", enable);
+        sensor->status.aec2 = enable;
+    }
+    return ret;
+}
+
+static int set_bpc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = set_reg_bits(sensor, 0x64, 1, 1, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set bpc to: %d", enable);
+        sensor->status.bpc = enable;
+    }
+    return ret;
+}
+
+static int set_wpc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = set_reg_bits(sensor, 0x64, 0, 1, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set wpc to: %d", enable);
+        sensor->status.wpc = enable;
+    }
+    return ret;
+}
+
+static int set_raw_gma_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = set_reg_bits(sensor, 0x64, 2, 1, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set raw_gma to: %d", enable);
+        sensor->status.raw_gma = enable;
+    }
+    return ret;
+}
+
+static int set_lenc_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = set_reg_bits(sensor, LC_CTR, 0, 1, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set lenc to: %d", enable);
+        sensor->status.lenc = enable;
+    }
+    return ret;
+}
+
+//real gain
+static int set_agc_gain(sensor_t *sensor, int gain)
+{
+    int ret = 0;
+    ret = set_reg_bits(sensor, COM9, 4, 3, gain % 5);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set gain to: %d", gain);
+        sensor->status.agc_gain = gain;
+    }
+    return ret;
+}
+
+static int set_aec_value(sensor_t *sensor, int value)
+{
+    int ret = 0;
+    ret =  SCCB_Write(sensor->slv_addr, AEC, value & 0xff) | SCCB_Write(sensor->slv_addr, AECH, value >> 8);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set aec_value to: %d", value);
+        sensor->status.aec_value = value;
+    }
+    return ret;
+}
+
+static int set_awb_gain_dsp(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    ret = set_reg_bits(sensor, 0x63, 7, 1, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set awb_gain to: %d", enable);
+        sensor->status.awb_gain = enable;
+    }
+    return ret;
+}
+
+static int set_brightness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    ret = SCCB_Write(sensor->slv_addr, 0x9B, level);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set brightness to: %d", level);
+        sensor->status.brightness = level;
+    }
+    return ret;
+}
+
+static int set_contrast(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    ret = SCCB_Write(sensor->slv_addr, 0x9C, level);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set contrast to: %d", level);
+        sensor->status.contrast = level;
+    }
+    return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x9B);
+    sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0x9C);
+    sensor->status.saturation = 0;
+    sensor->status.ae_level = 0;
+    sensor->status.special_effect = get_reg_bits(sensor, 0x64, 5, 1);
+    sensor->status.wb_mode = get_reg_bits(sensor, 0x6B, 7, 1);
+    sensor->status.agc_gain = get_reg_bits(sensor, COM9, 4, 3);
+    sensor->status.aec_value = SCCB_Read(sensor->slv_addr, AEC) | (SCCB_Read(sensor->slv_addr, AECH) << 8);
+    sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x00);
+    sensor->status.awb = get_reg_bits(sensor, COM8, 1, 1);
+    sensor->status.awb_gain = get_reg_bits(sensor, 0x63, 7, 1);
+    sensor->status.aec = get_reg_bits(sensor, COM8, 0, 1);
+    sensor->status.aec2 = get_reg_bits(sensor, COM8, 7, 1);
+    sensor->status.agc = get_reg_bits(sensor, COM8, 2, 1);
+    sensor->status.bpc = get_reg_bits(sensor, 0x64, 1, 1);
+    sensor->status.wpc = get_reg_bits(sensor, 0x64, 0, 1);
+    sensor->status.raw_gma = get_reg_bits(sensor, 0x64, 2, 1);
+    sensor->status.lenc = get_reg_bits(sensor, LC_CTR, 0, 1);
+    sensor->status.hmirror = get_reg_bits(sensor, COM3, 6, 1);
+    sensor->status.vflip = get_reg_bits(sensor, COM3, 7, 1);
+    sensor->status.dcw = get_reg_bits(sensor, 0x65, 2, 1);
+    sensor->status.colorbar = get_reg_bits(sensor, COM3, 0, 1);
+    sensor->status.sharpness = get_reg_bits(sensor, EDGE0, 0, 5);
+    sensor->status.denoise = SCCB_Read(sensor->slv_addr, 0x8E);
+    return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val){ return -1; }
+static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
+static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning){return -1;}
+static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div){return -1;}
+
+static int set_xclk(sensor_t *sensor, int timer, int xclk)
+{
+    int ret = 0;
+    sensor->xclk_freq_hz = xclk * 1000000U;
+    ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
+    return ret;
+}
+
+int ov7725_detect(int slv_addr, sensor_id_t *id)
+{
+    if (OV7725_SCCB_ADDR == slv_addr) {
+        SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
+        uint16_t PID = SCCB_Read(slv_addr, 0x0A);
+        if (OV7725_PID == PID) {
+            id->PID = PID;
+            id->VER = SCCB_Read(slv_addr, REG_VER);
+            id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
+            id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int ov7725_init(sensor_t *sensor)
+{
+    // Set function pointers
+    sensor->reset = reset;
+    sensor->init_status = init_status;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_whitebal = set_whitebal;
+    sensor->set_gain_ctrl = set_gain_ctrl;
+    sensor->set_exposure_ctrl = set_exposure_ctrl;
+    sensor->set_hmirror = set_hmirror;
+    sensor->set_vflip = set_vflip;
+
+    sensor->set_brightness = set_brightness;
+    sensor->set_contrast = set_contrast;
+    sensor->set_aec2 = set_aec2;
+    sensor->set_aec_value = set_aec_value;
+    sensor->set_awb_gain = set_awb_gain_dsp;
+    sensor->set_agc_gain = set_agc_gain;
+    sensor->set_dcw = set_dcw_dsp;
+    sensor->set_bpc = set_bpc_dsp;
+    sensor->set_wpc = set_wpc_dsp;
+    sensor->set_raw_gma = set_raw_gma_dsp;
+    sensor->set_lenc = set_lenc_dsp;
+
+    //not supported
+    sensor->set_saturation= set_dummy;
+    sensor->set_sharpness = set_dummy;
+    sensor->set_denoise = set_dummy;
+    sensor->set_quality = set_dummy;
+    sensor->set_special_effect = set_dummy;
+    sensor->set_wb_mode = set_dummy;
+    sensor->set_ae_level = set_dummy;
+    sensor->set_gainceiling = set_gainceiling_dummy;
+
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_res_raw = set_res_raw;
+    sensor->set_pll = _set_pll;
+    sensor->set_xclk = set_xclk;
+    
+    // Retrieve sensor's signature
+    sensor->id.MIDH = SCCB_Read(sensor->slv_addr, REG_MIDH);
+    sensor->id.MIDL = SCCB_Read(sensor->slv_addr, REG_MIDL);
+    sensor->id.PID = SCCB_Read(sensor->slv_addr, REG_PID);
+    sensor->id.VER = SCCB_Read(sensor->slv_addr, REG_VER);
+    
+    ESP_LOGD(TAG, "OV7725 Attached");
+
+    return 0;
+}

+ 27 - 0
std/camera/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__

+ 12 - 0
std/camera/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__

+ 158 - 0
std/camera/sensors/private_include/bf20a6_settings.h

@@ -0,0 +1,158 @@
+
+#include <stdint.h>
+
+#define REG_DLY 0xffff
+#define REGLIST_TAIL 0xffff /* Array end token */
+
+static const uint16_t bf20a6_default_init_regs[][2] = {
+    {0xf2,0x01},
+    {0x12,0x20},
+    {0x3a,0x00},
+    {0xe1,0x92},
+    {0xe3,0x12},// PLL Control, important for framerate(choice: 0x02\0x12\0x22\0x32\0x82)
+    {0xe0,0x00},
+    {0x2a,0x98},
+    {0xcd,0x17},
+    {0xc0,0x10},
+    {0xc6,0x1d},
+    {0x10,0x35},
+    {0xe2,0x09},
+    {0xe4,0x72},
+    {0xe5,0x22},
+    {0xe6,0x24},
+    {0xe7,0x64},
+    {0xe8,0xa2}, // DVP:a2},  SPI:f2        VDDIO=1.8V,E8[2]=1},VDDIO=2.8V,E8[2]=0},
+    {0x4a,0x00},
+    {0x00,0x03},
+    {0x1f,0x02},
+    {0x22,0x02},
+    {0x0c,0x31},
+
+    {0x00,0x00},
+    {0x60,0x81},
+    {0x61,0x81},
+
+    {0xa0,0x08},
+    {0x01,0x1a},
+    // {0x01,0x1a},
+    // {0x01,0x1a},
+    // {0x02,0x15},
+    // {0x02,0x15},
+    {0x02,0x15},
+    {0x13,0x08},
+    {0x8a,0x96},
+    {0x8b,0x06},
+    {0x87,0x18},
+
+
+    {0x34,0x48}, // lens
+    {0x35,0x40},
+    {0x36,0x40},
+
+    {0x71,0x44},
+    {0x72,0x48},
+    {0x74,0xa2},
+    {0x75,0xa9},
+    {0x78,0x12},
+    {0x79,0xa0},
+    {0x7a,0x94},
+    {0x7c,0x97},
+    {0x40,0x30},
+    {0x41,0x30},
+    {0x42,0x28},
+    {0x43,0x1f},
+    {0x44,0x1c},
+    {0x45,0x16},
+    {0x46,0x13},
+    {0x47,0x10},
+    {0x48,0x0D},
+    {0x49,0x0C},
+    {0x4B,0x0A},
+    {0x4C,0x0B},
+    {0x4E,0x09},
+    {0x4F,0x08},
+    {0x50,0x08},
+
+
+    {0x5f,0x29},
+    {0x23,0x33},
+    {0xa1,0x10}, // AWB
+    {0xa2,0x0d},
+    {0xa3,0x30},
+    {0xa4,0x06},
+    {0xa5,0x22},
+    {0xa6,0x56},
+    {0xa7,0x18},
+    {0xa8,0x1a},
+    {0xa9,0x12},
+    {0xaa,0x12},
+    {0xab,0x16},
+    {0xac,0xb1},
+    {0xba,0x12},
+    {0xbb,0x12},
+    {0xad,0x12},
+    {0xae,0x56},
+    {0xaf,0x0a},
+    {0x3b,0x30},
+    {0x3c,0x12},
+    {0x3d,0x22},
+    {0x3e,0x3f},
+    {0x3f,0x28},
+    {0xb8,0xc3},
+    {0xb9,0xa3},
+    {0x39,0x47}, // pure color threshold
+    {0x26,0x13},
+    {0x27,0x16},
+    {0x28,0x14},
+    {0x29,0x18},
+    {0xee,0x0d},
+
+        
+    {0x13,0x05},
+    {0x24,0x3C},
+    {0x81,0x20},
+    {0x82,0x40},
+    {0x83,0x30},
+    {0x84,0x58},
+    {0x85,0x30},
+    {0x92,0x08},
+    {0x86,0x80},
+    {0x8a,0x96},
+    {0x91,0xff},
+    {0x94,0x62},
+    {0x9a,0x18}, // outdoor threshold
+    {0xf0,0x45}, // integral time control, important for framerate(choice: 0x46\0x45\0x44..)
+    {0x51,0x17}, // color normal
+    {0x52,0x03},
+    {0x53,0x5F},
+    {0x54,0x47},
+    {0x55,0x66},
+    {0x56,0x0F},
+    {0x7e,0x14},
+    {0x57,0x36}, // color
+    {0x58,0x2A},
+    {0x59,0xAA},
+    {0x5a,0xA8},
+    {0x5b,0x43},
+    {0x5c,0x10},
+    {0x5d,0x00},
+    {0x7d,0x36},
+    {0x5e,0x10},
+
+    {0xd6,0x88}, // contrast
+    {0xd5,0x20}, // bright
+    {0xb0,0x84}, // low light ctrl in gray section
+    {0xb5,0x08}, // the threshold of GLB_GAIN
+    {0xb1,0xc8}, // saturation
+    {0xb2,0xc0},
+    {0xb3,0xd0},
+    {0xb4,0xB0},
+
+    {0x32,0x10},
+    // {0x8a,0x00},
+    // {0x8b,0x10},
+    {0xa0,0x09},
+    {0x00,0x03},
+    {0x0b,0x02},
+    {REGLIST_TAIL, 0x00},
+};

+ 33 - 0
std/camera/sensors/private_include/bf3005.h

@@ -0,0 +1,33 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * BF3005 driver.
+ *
+ */
+#ifndef __BF3005_H__
+#define __BF3005_H__
+#include "sensor.h"
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ *     0:       Can't detect this sensor
+ *     Nonzero: This sensor has been detected
+ */
+int bf3005_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int bf3005_init(sensor_t *sensor);
+
+#endif // __BF3005_H__

+ 337 - 0
std/camera/sensors/private_include/bf3005_regs.h

@@ -0,0 +1,337 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * BF3005 register definitions.
+ */
+#ifndef __REG_REGS_H__
+#define __REG_REGS_H__
+#if 0
+#define GAIN                    0x00 /* AGC ¨C Gain control gain setting  */
+#define BLUE                    0x01 /* AWB ¨C Blue channel gain setting  */
+#define RED                     0x02 /* AWB ¨C Red channel gain setting   */
+#define GREEN                   0x03 /* AWB ¨C Green channel gain setting */
+#define BAVG                    0x05 /* U/B Average Level   */
+#define GAVG                    0x06 /* Y/Gb Average Level  */
+#define RAVG                    0x07 /* V/R Average Level   */
+#define AECH                    0x08 /* Exposure Value ¨C AEC MSBs */
+
+#define COM2                    0x09 /* Common Control 2 */
+#define COM2_SOFT_SLEEP         0x10 /* Soft sleep mode  */
+#define COM2_OUT_DRIVE_1x       0x00 /* Output drive capability 1x */
+#define COM2_OUT_DRIVE_2x       0x01 /* Output drive capability 2x */
+#define COM2_OUT_DRIVE_3x       0x02 /* Output drive capability 3x */
+#define COM2_OUT_DRIVE_4x       0x03 /* Output drive capability 4x */
+
+#define REG_PID                     0x0A /* Product ID Number MSB */
+#define REG_VER                     0x0B /* Product ID Number LSB */
+
+#define COM3                    0x0C /* Common Control 3                                        */
+#define COM3_VFLIP              0x80 /* Vertical flip image ON/OFF selection                    */
+#define COM3_MIRROR             0x40 /* Horizontal mirror image ON/OFF selection                */
+#define COM3_SWAP_BR            0x20 /* Swap B/R output sequence in RGB output mode             */
+#define COM3_SWAP_YUV           0x10 /* Swap Y/UV output sequence in YUV output mode            */
+#define COM3_SWAP_MSB           0x08 /* Swap output MSB/LSB                                     */
+#define COM3_TRI_CLOCK          0x04 /* Tri-state option for output clock at power-down period  */
+#define COM3_TRI_DATA           0x02 /* Tri-state option for output data at power-down period   */
+#define COM3_COLOR_BAR          0x01 /* Sensor color bar test pattern output enable             */
+#define COM3_SET_CBAR(r, x)     ((r&0xFE)|((x&1)<<0))
+#define COM3_SET_MIRROR(r, x)   ((r&0xBF)|((x&1)<<6))
+#define COM3_SET_FLIP(r, x)     ((r&0x7F)|((x&1)<<7))
+
+#define COM4                    0x0D /* Common Control 4         */
+#define COM4_PLL_BYPASS         0x00 /* Bypass PLL               */
+#define COM4_PLL_4x             0x40 /* PLL frequency 4x         */
+#define COM4_PLL_6x             0x80 /* PLL frequency 6x         */
+#define COM4_PLL_8x             0xc0 /* PLL frequency 8x         */
+#define COM4_AEC_FULL           0x00 /* AEC evaluate full window */
+#define COM4_AEC_1_2            0x10 /* AEC evaluate 1/2 window  */
+#define COM4_AEC_1_4            0x20 /* AEC evaluate 1/4 window  */
+#define COM4_AEC_2_3            0x30 /* AEC evaluate 2/3 window  */
+
+#define COM5                    0x0E /* Common Control 5 */
+#define COM5_AFR                0x80 /* Auto frame rate control ON/OFF selection (night mode) */
+#define COM5_AFR_SPEED          0x40 /* Auto frame rate control speed selection */
+#define COM5_AFR_0              0x00 /* No reduction of frame rate          */
+#define COM5_AFR_1_2            0x10 /* Max reduction to 1/2 frame rate     */
+#define COM5_AFR_1_4            0x20 /* Max reduction to 1/4 frame rate     */
+#define COM5_AFR_1_8            0x30 /* Max reduction to 1/8 frame rate     */
+#define COM5_AFR_4x             0x04 /* Add frame when AGC reaches 4x gain  */
+#define COM5_AFR_8x             0x08 /* Add frame when AGC reaches 8x gain  */
+#define COM5_AFR_16x            0x0c /* Add frame when AGC reaches 16x gain */
+#define COM5_AEC_NO_LIMIT       0x01 /* No limit to AEC increase step       */
+
+#define COM6                    0x0F /* Common Control 6 */
+#define COM6_AUTO_WINDOW        0x01 /* Auto window setting ON/OFF selection when format changes */
+
+#define AEC                     0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */
+#define CLKRC                   0x11 /* Internal Clock */
+
+#define COM7                    0x12 /* Common Control 7         */
+#define COM7_RESET              0x80 /* SCCB Register Reset      */
+#define COM7_RES_VGA            0x00 /* Resolution VGA           */
+#define COM7_RES_QVGA           0x40 /* Resolution QVGA          */
+#define COM7_BT656              0x20 /* BT.656 protocol ON/OFF   */
+#define COM7_SENSOR_RAW         0x10 /* Sensor RAW               */
+#define COM7_FMT_GBR422         0x00 /* RGB output format GBR422 */
+#define COM7_FMT_RGB565         0x04 /* RGB output format RGB565 */
+#define COM7_FMT_RGB555         0x08 /* RGB output format RGB555 */
+#define COM7_FMT_RGB444         0x0C /* RGB output format RGB444 */
+#define COM7_FMT_YUV            0x00 /* Output format YUV        */
+#define COM7_FMT_P_BAYER        0x01 /* Output format Processed Bayer RAW */
+#define COM7_FMT_RGB            0x02 /* Output format RGB        */
+#define COM7_FMT_R_BAYER        0x03 /* Output format Bayer RAW  */
+#define COM7_SET_FMT(r, x)      ((r&0xFC)|((x&0x3)<<0))
+#define COM7_SET_RGB(r, x)      ((r&0xF0)|(x&0x0C)|COM7_FMT_RGB)
+
+#define COM8                    0x13 /* Common Control 8                */
+#define COM8_FAST_AUTO          0x80 /* Enable fast AGC/AEC algorithm   */
+#define COM8_STEP_VSYNC         0x00 /* AEC - Step size limited to vertical blank */
+#define COM8_STEP_UNLIMIT       0x40 /* AEC - Step size unlimited step size       */
+#define COM8_BANDF_EN           0x20 /* Banding filter ON/OFF */
+#define COM8_AEC_BANDF          0x10 /* Enable AEC below banding value */
+#define COM8_AEC_FINE_EN        0x08 /* Fine AEC ON/OFF control */
+#define COM8_AGC_EN             0x04 /* AGC Enable */
+#define COM8_AWB_EN             0x02 /* AWB Enable */
+#define COM8_AEC_EN             0x01 /* AEC Enable */
+#define COM8_SET_AGC(r, x)      ((r&0xFB)|((x&0x1)<<2))
+#define COM8_SET_AWB(r, x)      ((r&0xFD)|((x&0x1)<<1))
+#define COM8_SET_AEC(r, x)      ((r&0xFE)|((x&0x1)<<0))
+
+#define COM9                    0x14 /* Common Control 9 */
+#define COM9_HISTO_AVG          0x80 /* Histogram or average based AEC/AGC selection */
+#define COM9_AGC_GAIN_2x        0x00 /* Automatic Gain Ceiling 2x  */
+#define COM9_AGC_GAIN_4x        0x10 /* Automatic Gain Ceiling 4x  */
+#define COM9_AGC_GAIN_8x        0x20 /* Automatic Gain Ceiling 8x  */
+#define COM9_AGC_GAIN_16x       0x30 /* Automatic Gain Ceiling 16x */
+#define COM9_AGC_GAIN_32x       0x40 /* Automatic Gain Ceiling 32x */
+#define COM9_DROP_VSYNC         0x04 /* Drop VSYNC output of corrupt frame */
+#define COM9_DROP_HREF          0x02 /* Drop HREF output of corrupt frame  */
+#define COM9_SET_AGC(r, x)      ((r&0x8F)|((x&0x07)<<4))
+
+#define COM10                   0x15 /* Common Control 10 */
+#define COM10_NEGATIVE          0x80 /* Output negative data */
+#define COM10_HSYNC_EN          0x40 /* HREF changes to HSYNC */
+#define COM10_PCLK_FREE         0x00 /* PCLK output option: free running PCLK */
+#define COM10_PCLK_MASK         0x20 /* PCLK output option: masked during horizontal blank  */
+#define COM10_PCLK_REV          0x10 /* PCLK reverse */
+#define COM10_HREF_REV          0x08 /* HREF reverse */
+#define COM10_VSYNC_FALLING     0x00 /* VSYNC changes on falling edge of PCLK */
+#define COM10_VSYNC_RISING      0x04 /* VSYNC changes on rising edge of PCLK */
+#define COM10_VSYNC_NEG         0x02 /* VSYNC negative */
+#define COM10_OUT_RANGE_8       0x01 /* Output data range: Full range */
+#define COM10_OUT_RANGE_10      0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */
+
+#define REG16                   0x16 /* Register 16 */
+#define REG16_BIT_SHIFT         0x80 /* Bit shift test pattern options */
+#define HSTART                  0x17 /* Horizontal Frame (HREF column) Start 8 MSBs (2 LSBs are at HREF[5:4]) */
+#define HSIZE                   0x18 /* Horizontal Sensor Size (2 LSBs are at HREF[1:0]) */
+#define VSTART                  0x19 /* Vertical Frame (row) Start 8 MSBs (1 LSB is at HREF[6]) */
+#define VSIZE                   0x1A /* Vertical Sensor Size (1 LSB is at HREF[2]) */
+#define PSHFT                   0x1B /* Data Format - Pixel Delay Select */
+#define REG_MIDH                    0x1C /* Manufacturer ID Byte ¨C High */
+#define REG_MIDL                    0x1D /* Manufacturer ID Byte ¨C Low */
+#define LAEC                    0x1F /* Fine AEC Value - defines exposure value less than one row period */
+
+#define COM11                   0x20 /* Common Control 11 */
+#define COM11_SNGL_FRAME_EN     0x02 /* Single frame ON/OFF selection */
+#define COM11_SNGL_XFR_TRIG     0x01 /* Single frame transfer trigger */
+
+#define BDBASE                  0x22 /* Banding Filter Minimum AEC Value */
+#define DBSTEP                  0x23 /* Banding Filter Maximum Step */
+#define AEW                     0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */
+#define AEB                     0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */
+#define VPT                     0x26 /* AGC/AEC Fast Mode Operating Region */
+#define REG28                   0x28 /* Selection on the number of dummy rows, N */
+#define HOUTSIZE                0x29 /* Horizontal Data Output Size MSBs (2 LSBs at register EXHCH[1:0]) */
+#define EXHCH                   0x2A /* Dummy Pixel Insert MSB */
+#define EXHCL                   0x2B /* Dummy Pixel Insert LSB */
+#define VOUTSIZE                0x2C /* Vertical Data Output Size MSBs (LSB at register EXHCH[2])       */
+#define ADVFL                   0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row)  */
+#define ADVFH                   0x2E /* MSB of Insert Dummy Rows in Vertical Sync */
+#define YAVE                    0x2F /* Y/G Channel Average Value */
+#define LUMHTH                  0x30 /* Histogram AEC/AGC Luminance High Level Threshold */
+#define LUMLTH                  0x31 /* Histogram AEC/AGC Luminance Low Level Threshold  */
+#define HREF                    0x32 /* Image Start and Size Control */
+#define DM_LNL                  0x33 /* Dummy Row Low 8 Bits  */
+#define DM_LNH                  0x34 /* Dummy Row High 8 Bits */
+#define ADOFF_B                 0x35 /* AD Offset Compensation Value for B Channel  */
+#define ADOFF_R                 0x36 /* AD Offset Compensation Value for R Channel  */
+#define ADOFF_GB                0x37 /* AD Offset Compensation Value for GB Channel */
+#define ADOFF_GR                0x38 /* AD Offset Compensation Value for GR Channel */
+#define OFF_B                   0x39 /* AD Offset Compensation Value for B Channel  */
+#define OFF_R                   0x3A /* AD Offset Compensation Value for R Channel  */
+#define OFF_GB                  0x3B /* AD Offset Compensation Value for GB Channel */
+#define OFF_GR                  0x3C /* AD Offset Compensation Value for GR Channel */
+#define COM12                   0x3D /* DC offset compensation for analog process */
+
+#define COM13                   0x3E /* Common Control 13 */
+#define COM13_BLC_EN            0x80 /* BLC enable */
+#define COM13_ADC_EN            0x40 /* ADC channel BLC ON/OFF control */
+#define COM13_ANALOG_BLC        0x20 /* Analog processing channel BLC ON/OFF control */
+#define COM13_ABLC_GAIN_EN      0x04 /* ABLC gain trigger enable */
+
+#define COM14                   0x3F /* Common Control 14 */
+#define COM15                   0x40 /* Common Control 15 */
+#define COM16                   0x41 /* Common Control 16 */
+#define TGT_B                   0x42 /* BLC Blue Channel Target Value   */
+#define TGT_R                   0x43 /* BLC Red Channel Target Value    */
+#define TGT_GB                  0x44 /* BLC Gb Channel Target Value     */
+#define TGT_GR                  0x45 /* BLC Gr Channel Target Value     */
+
+#define LC_CTR                  0x46 /* Lens Correction Control */
+#define LC_CTR_RGB_COMP_1       0x00 /* R, G, and B channel compensation coefficient is set by LC_COEF (0x49) */
+#define LC_CTR_RGB_COMP_3       0x04 /* R, G, and B channel compensation coefficient is set by registers
+                                        LC_COEFB (0x4B), LC_COEF (0x49), and LC_COEFR (0x4C), respectively */
+#define LC_CTR_EN               0x01 /* Lens correction enable */
+#define LC_XC                   0x47 /* X Coordinate of Lens Correction Center Relative to Array Center */
+#define LC_YC                   0x48 /* Y Coordinate of Lens Correction Center Relative to Array Center */
+#define LC_COEF                 0x49 /* Lens Correction Coefficient */
+#define LC_RADI                 0x4A /* Lens Correction Radius */
+#define LC_COEFB                0x4B /* Lens Correction B Channel Compensation Coefficient */
+#define LC_COEFR                0x4C /* Lens Correction R Channel Compensation Coefficient */
+
+#define FIXGAIN                 0x4D /* Analog Fix Gain Amplifier */
+#define AREF0                   0x4E /* Sensor Reference Control */
+#define AREF1                   0x4F /* Sensor Reference Current Control */
+#define AREF2                   0x50 /* Analog Reference Control */
+#define AREF3                   0x51 /* ADC Reference Control */
+#define AREF4                   0x52 /* ADC Reference Control */
+#define AREF5                   0x53 /* ADC Reference Control */
+#define AREF6                   0x54 /* Analog Reference Control */
+#define AREF7                   0x55 /* Analog Reference Control */
+#define UFIX                    0x60 /* U Channel Fixed Value Output */
+#define VFIX                    0x61 /* V Channel Fixed Value Output */
+#define AWBB_BLK                0x62 /* AWB Option for Advanced AWB  */
+
+#define AWB_CTRL0               0x63 /* AWB Control Byte 0   */
+#define AWB_CTRL0_GAIN_EN       0x80 /* AWB gain enable      */
+#define AWB_CTRL0_CALC_EN       0x40 /* AWB calculate enable */
+#define AWB_CTRL0_WBC_MASK      0x0F /* WBC threshold 2      */
+
+#define DSP_CTRL1               0x64 /* DSP Control Byte 1                  */
+#define DSP_CTRL1_FIFO_EN       0x80 /* FIFO enable/disable selection       */
+#define DSP_CTRL1_UV_EN         0x40 /* UV adjust function ON/OFF selection */
+#define DSP_CTRL1_SDE_EN        0x20 /* SDE enable                          */
+#define DSP_CTRL1_MTRX_EN       0x10 /* Color matrix ON/OFF selection       */
+#define DSP_CTRL1_INTRP_EN      0x08 /* Interpolation ON/OFF selection      */
+#define DSP_CTRL1_GAMMA_EN      0x04 /* Gamma function ON/OFF selection     */
+#define DSP_CTRL1_BLACK_EN      0x02 /* Black defect auto correction ON/OFF */
+#define DSP_CTRL1_WHITE_EN      0x01 /* White defect auto correction ON/OFF */
+
+#define DSP_CTRL2               0x65 /* DSP Control Byte 2          */
+#define DSP_CTRL2_VDCW_EN       0x08 /* Vertical DCW enable         */
+#define DSP_CTRL2_HDCW_EN       0x04 /* Horizontal DCW enable       */
+#define DSP_CTRL2_VZOOM_EN      0x02 /* Vertical zoom out enable    */
+#define DSP_CTRL2_HZOOM_EN      0x01 /* Horizontal zoom out enable  */
+
+#define DSP_CTRL3               0x66 /* DSP Control Byte 3                      */
+#define DSP_CTRL3_UV_EN         0x80 /* UV output sequence option               */
+#define DSP_CTRL3_CBAR_EN       0x20 /* DSP color bar ON/OFF selection          */
+#define DSP_CTRL3_FIFO_EN       0x08 /* FIFO power down ON/OFF selection        */
+#define DSP_CTRL3_SCAL1_PWDN    0x04 /* Scaling module power down control 1     */
+#define DSP_CTRL3_SCAL2_PWDN    0x02 /* Scaling module power down control 2     */
+#define DSP_CTRL3_INTRP_PWDN    0x01 /* Interpolation module power down control */
+#define DSP_CTRL3_SET_CBAR(r, x)    ((r&0xDF)|((x&1)<<5))
+
+
+#define DSP_CTRL4               0x67 /* DSP Control Byte 4          */
+#define DSP_CTRL4_YUV_RGB       0x00 /* Output selection YUV or RGB */
+#define DSP_CTRL4_RAW8          0x02 /* Output selection RAW8       */
+#define DSP_CTRL4_RAW10         0x03 /* Output selection RAW10      */
+
+
+#define AWB_BIAS                0x68 /* AWB BLC Level Clip */
+#define AWB_CTRL1               0x69 /* AWB Control 1 */
+#define AWB_CTRL2               0x6A /* AWB Control 2 */
+
+#define AWB_CTRL3               0x6B /* AWB Control 3 */
+#define AWB_CTRL3_ADVANCED      0x80 /* AWB mode select - Advanced AWB */
+#define AWB_CTRL3_SIMPLE        0x00 /* AWB mode select - Simple AWB */
+
+#define AWB_CTRL4               0x6C /* AWB Control 4  */
+#define AWB_CTRL5               0x6D /* AWB Control 5  */
+#define AWB_CTRL6               0x6E /* AWB Control 6  */
+#define AWB_CTRL7               0x6F /* AWB Control 7  */
+#define AWB_CTRL8               0x70 /* AWB Control 8  */
+#define AWB_CTRL9               0x71 /* AWB Control 9  */
+#define AWB_CTRL10              0x72 /* AWB Control 10 */
+#define AWB_CTRL11              0x73 /* AWB Control 11 */
+#define AWB_CTRL12              0x74 /* AWB Control 12 */
+#define AWB_CTRL13              0x75 /* AWB Control 13 */
+#define AWB_CTRL14              0x76 /* AWB Control 14 */
+#define AWB_CTRL15              0x77 /* AWB Control 15 */
+#define AWB_CTRL16              0x78 /* AWB Control 16 */
+#define AWB_CTRL17              0x79 /* AWB Control 17 */
+#define AWB_CTRL18              0x7A /* AWB Control 18 */
+#define AWB_CTRL19              0x7B /* AWB Control 19 */
+#define AWB_CTRL20              0x7C /* AWB Control 20 */
+#define AWB_CTRL21              0x7D /* AWB Control 21 */
+#define GAM1                    0x7E /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */
+#define GAM2                    0x7F /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */
+#define GAM3                    0x80 /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */
+#define GAM4                    0x81 /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */
+#define GAM5                    0x82 /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */
+#define GAM6                    0x83 /* Gamma Curve 6th Segment Input End Point 0x30 Output Value */
+#define GAM7                    0x84 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */
+#define GAM8                    0x85 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */
+#define GAM9                    0x86 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */
+#define GAM10                   0x87 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */
+#define GAM11                   0x88 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */
+#define GAM12                   0x89 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */
+#define GAM13                   0x8A /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */
+#define GAM14                   0x8B /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */
+#define GAM15                   0x8C /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */
+#define SLOP                    0x8D /* Gamma Curve Highest Segment Slope */
+#define DNSTH                   0x8E /* De-noise Threshold */
+#define EDGE0                   0x8F /* Edge Enhancement Strength Control */
+#define EDGE1                   0x90 /* Edge Enhancement Threshold Control */
+#define DNSOFF                  0x91 /* Auto De-noise Threshold Control */
+#define EDGE2                   0x92 /* Edge Enhancement Strength Upper Limit */
+#define EDGE3                   0x93 /* Edge Enhancement Strength Upper Limit */
+#define MTX1                    0x94 /* Matrix Coefficient 1 */
+#define MTX2                    0x95 /* Matrix Coefficient 2 */
+#define MTX3                    0x96 /* Matrix Coefficient 3 */
+#define MTX4                    0x97 /* Matrix Coefficient 4 */
+#define MTX5                    0x98 /* Matrix Coefficient 5 */
+#define MTX6                    0x99 /* Matrix Coefficient 6 */
+
+#define MTX_CTRL                0x9A /* Matrix Control */
+#define MTX_CTRL_DBL_EN         0x80 /* Matrix double ON/OFF selection */
+
+#define BRIGHTNESS              0x9B /* Brightness Control */
+#define CONTRAST                0x9C /* Contrast Gain */
+#define UVADJ0                  0x9E /* Auto UV Adjust Control 0 */
+#define UVADJ1                  0x9F /* Auto UV Adjust Control 1 */
+#define SCAL0                   0xA0 /* DCW Ratio Control */
+#define SCAL1                   0xA1 /* Horizontal Zoom Out Control */
+#define SCAL2                   0xA2 /* Vertical Zoom Out Control */
+#define FIFODLYM                0xA3 /* FIFO Manual Mode Delay Control */
+#define FIFODLYA                0xA4 /* FIFO Auto Mode Delay Control */
+
+#define SDE                     0xA6 /* Special Digital Effect Control  */
+#define SDE_NEGATIVE_EN         0x40 /* Negative image enable           */
+#define SDE_GRAYSCALE_EN        0x20 /* Gray scale image enable         */
+#define SDE_V_FIXED_EN          0x10 /* V fixed value enable            */
+#define SDE_U_FIXED_EN          0x08 /* U fixed value enable            */
+#define SDE_CONT_BRIGHT_EN      0x04 /* Contrast/Brightness enable      */
+#define SDE_SATURATION_EN       0x02 /* Saturation enable               */
+#define SDE_HUE_EN              0x01 /* Hue enable                      */
+
+#define USAT                    0xA7 /* U Component Saturation Gain     */
+#define VSAT                    0xA8 /* V Component Saturation Gain     */
+#define HUECOS                  0xA9 /* Cosine value ¡Á 0x80             */
+#define HUESIN                  0xAA /* Sine value ¡Á 0x80               */
+#define SIGN_BIT                0xAB /* Sign Bit for Hue and Brightness */
+
+#define DSPAUTO                 0xAC /* DSP Auto Function ON/OFF Control */
+#define DSPAUTO_AWB_EN          0x80 /* AWB auto threshold control */
+#define DSPAUTO_DENOISE_EN      0x40 /* De-noise auto threshold control */
+#define DSPAUTO_EDGE_EN         0x20 /* Sharpness (edge enhancement) auto strength control */
+#define DSPAUTO_UV_EN           0x10 /* UV adjust auto slope control */
+#define DSPAUTO_SCAL0_EN        0x08 /* Auto scaling factor control (register SCAL0 (0xA0)) */
+#define DSPAUTO_SCAL1_EN        0x04 /* Auto scaling factor control (registers SCAL1 (0xA1 and SCAL2 (0xA2))*/
+#define SET_REG(reg, x)         (##reg_DEFAULT|x)
+#endif //__REG_REGS_H__
+#endif

+ 31 - 0
std/camera/sensors/private_include/gc0308.h

@@ -0,0 +1,31 @@
+#pragma once
+
+#include "sensor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ *     0:       Can't detect this sensor
+ *     Nonzero: This sensor has been detected
+ */
+int gc0308_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int gc0308_init(sensor_t *sensor);
+
+#ifdef __cplusplus
+}
+#endif

+ 25 - 0
std/camera/sensors/private_include/gc0308_regs.h

@@ -0,0 +1,25 @@
+/*
+ * GC0308 register definitions.
+ */
+#ifndef __GC0308_REG_REGS_H__
+#define __GC0308_REG_REGS_H__
+
+#define RESET_RELATED   0xfe    // Bit[7]: Software reset 
+                                // Bit[6:5]: NA
+                                // Bit[4]: CISCTL_restart_n
+                                // Bit[3:1]: NA
+                                // Bit[0]: page select
+                                //  0:page0
+                                //  1:page1
+
+
+// page0:
+
+
+
+/**
+ * @brief register value
+ */
+
+
+#endif // __GC0308_REG_REGS_H__

+ 245 - 0
std/camera/sensors/private_include/gc0308_settings.h

@@ -0,0 +1,245 @@
+#ifndef _GC0308_SETTINGS_H_
+#define _GC0308_SETTINGS_H_
+
+#include <stdint.h>
+
+#define REG_DLY 0xffff
+#define REGLIST_TAIL 0x0000 /* Array end token */
+
+static const uint16_t gc0308_sensor_default_regs[][2] = {
+    {0xfe, 0x00},
+    {0xec, 0x20},
+    {0x05, 0x00},
+    {0x06, 0x00},
+    {0x07, 0x00},
+    {0x08, 0x00},
+    {0x09, 0x01},
+    {0x0a, 0xe8},
+    {0x0b, 0x02},
+    {0x0c, 0x88},
+    {0x0d, 0x02},
+    {0x0e, 0x02},
+    {0x10, 0x26},
+    {0x11, 0x0d},
+    {0x12, 0x2a},
+    {0x13, 0x00},
+    {0x14, 0x11},
+    {0x15, 0x0a},
+    {0x16, 0x05},
+    {0x17, 0x01},
+    {0x18, 0x44},
+    {0x19, 0x44},
+    {0x1a, 0x2a},
+    {0x1b, 0x00},
+    {0x1c, 0x49},
+    {0x1d, 0x9a},
+    {0x1e, 0x61},
+    {0x1f, 0x00}, //pad drv <=24MHz, use 0x00 is ok
+    {0x20, 0x7f},
+    {0x21, 0xfa},
+    {0x22, 0x57},
+    {0x24, 0xa2},   //YCbYCr
+    {0x25, 0x0f},
+    {0x26, 0x03}, // 0x01
+    {0x28, 0x00},
+    {0x2d, 0x0a},
+    {0x2f, 0x01},
+    {0x30, 0xf7},
+    {0x31, 0x50},
+    {0x32, 0x00},
+    {0x33, 0x28},
+    {0x34, 0x2a},
+    {0x35, 0x28},
+    {0x39, 0x04},
+    {0x3a, 0x20},
+    {0x3b, 0x20},
+    {0x3c, 0x00},
+    {0x3d, 0x00},
+    {0x3e, 0x00},
+    {0x3f, 0x00},
+    {0x50, 0x14}, // 0x14
+    {0x52, 0x41},
+    {0x53, 0x80},
+    {0x54, 0x80},
+    {0x55, 0x80},
+    {0x56, 0x80},
+    {0x8b, 0x20},
+    {0x8c, 0x20},
+    {0x8d, 0x20},
+    {0x8e, 0x14},
+    {0x8f, 0x10},
+    {0x90, 0x14},
+    {0x91, 0x3c},
+    {0x92, 0x50},
+//{0x8b,0x10},
+//{0x8c,0x10},
+//{0x8d,0x10},
+//{0x8e,0x10},
+//{0x8f,0x10},
+//{0x90,0x10},
+//{0x91,0x3c},
+//{0x92,0x50},
+    {0x5d, 0x12},
+    {0x5e, 0x1a},
+    {0x5f, 0x24},
+    {0x60, 0x07},
+    {0x61, 0x15},
+    {0x62, 0x08}, // 0x08
+    {0x64, 0x03}, // 0x03
+    {0x66, 0xe8},
+    {0x67, 0x86},
+    {0x68, 0x82},
+    {0x69, 0x18},
+    {0x6a, 0x0f},
+    {0x6b, 0x00},
+    {0x6c, 0x5f},
+    {0x6d, 0x8f},
+    {0x6e, 0x55},
+    {0x6f, 0x38},
+    {0x70, 0x15},
+    {0x71, 0x33},
+    {0x72, 0xdc},
+    {0x73, 0x00},
+    {0x74, 0x02},
+    {0x75, 0x3f},
+    {0x76, 0x02},
+    {0x77, 0x38}, // 0x47
+    {0x78, 0x88},
+    {0x79, 0x81},
+    {0x7a, 0x81},
+    {0x7b, 0x22},
+    {0x7c, 0xff},
+    {0x93, 0x48}, //color matrix default
+    {0x94, 0x02},
+    {0x95, 0x07},
+    {0x96, 0xe0},
+    {0x97, 0x40},
+    {0x98, 0xf0},
+    {0xb1, 0x40},
+    {0xb2, 0x40},
+    {0xb3, 0x40}, //0x40
+    {0xb6, 0xe0},
+    {0xbd, 0x38},
+    {0xbe, 0x36},
+    {0xd0, 0xCB},
+    {0xd1, 0x10},
+    {0xd2, 0x90},
+    {0xd3, 0x48},
+    {0xd5, 0xF2},
+    {0xd6, 0x16},
+    {0xdb, 0x92},
+    {0xdc, 0xA5},
+    {0xdf, 0x23},
+    {0xd9, 0x00},
+    {0xda, 0x00},
+    {0xe0, 0x09},
+    {0xed, 0x04},
+    {0xee, 0xa0},
+    {0xef, 0x40},
+    {0x80, 0x03},
+
+    {0x9F, 0x10},
+    {0xA0, 0x20},
+    {0xA1, 0x38},
+    {0xA2, 0x4e},
+    {0xA3, 0x63},
+    {0xA4, 0x76},
+    {0xA5, 0x87},
+    {0xA6, 0xa2},
+    {0xA7, 0xb8},
+    {0xA8, 0xca},
+    {0xA9, 0xd8},
+    {0xAA, 0xe3},
+    {0xAB, 0xeb},
+    {0xAC, 0xf0},
+    {0xAD, 0xF8},
+    {0xAE, 0xFd},
+    {0xAF, 0xFF},
+
+    {0xc0, 0x00},
+    {0xc1, 0x10},
+    {0xc2, 0x1c},
+    {0xc3, 0x30},
+    {0xc4, 0x43},
+    {0xc5, 0x54},
+    {0xc6, 0x65},
+    {0xc7, 0x75},
+    {0xc8, 0x93},
+    {0xc9, 0xB0},
+    {0xca, 0xCB},
+    {0xcb, 0xE6},
+    {0xcc, 0xFF},
+    {0xf0, 0x02},
+    {0xf1, 0x01},
+    {0xf2, 0x02},
+    {0xf3, 0x30},
+    {0xf7, 0x04},
+    {0xf8, 0x02},
+    {0xf9, 0x9f},
+    {0xfa, 0x78},
+    {0xfe, 0x01},
+    {0x00, 0xf5},
+    {0x02, 0x20},
+    {0x04, 0x10},
+    {0x05, 0x08},
+    {0x06, 0x20},
+    {0x08, 0x0a},
+    {0x0a, 0xa0},
+    {0x0b, 0x60},
+    {0x0c, 0x08},
+    {0x0e, 0x44},
+    {0x0f, 0x32},
+    {0x10, 0x41},
+    {0x11, 0x37},
+    {0x12, 0x22},
+    {0x13, 0x19},
+    {0x14, 0x44},
+    {0x15, 0x44},
+    {0x16, 0xc2},
+    {0x17, 0xA8},
+    {0x18, 0x18},
+    {0x19, 0x50},
+    {0x1a, 0xd8},
+    {0x1b, 0xf5},
+    {0x70, 0x40},
+    {0x71, 0x58},
+    {0x72, 0x30},
+    {0x73, 0x48},
+    {0x74, 0x20},
+    {0x75, 0x60},
+    {0x77, 0x20},
+    {0x78, 0x32},
+    {0x30, 0x03},
+    {0x31, 0x40},
+    {0x32, 0x10},
+    {0x33, 0xe0},
+    {0x34, 0xe0},
+    {0x35, 0x00},
+    {0x36, 0x80},
+    {0x37, 0x00},
+    {0x38, 0x04},
+    {0x39, 0x09},
+    {0x3a, 0x12},
+    {0x3b, 0x1C},
+    {0x3c, 0x28},
+    {0x3d, 0x31},
+    {0x3e, 0x44},
+    {0x3f, 0x57},
+    {0x40, 0x6C},
+    {0x41, 0x81},
+    {0x42, 0x94},
+    {0x43, 0xA7},
+    {0x44, 0xB8},
+    {0x45, 0xD6},
+    {0x46, 0xEE},
+    {0x47, 0x0d},
+    {0x62, 0xf7},
+    {0x63, 0x68},
+    {0x64, 0xd3},
+    {0x65, 0xd3},
+    {0x66, 0x60},
+    {0xfe, 0x00},
+    {REGLIST_TAIL, 0x00},
+};
+
+#endif

+ 31 - 0
std/camera/sensors/private_include/gc032a.h

@@ -0,0 +1,31 @@
+/*
+ *
+ * GC032A driver.
+ *
+ */
+#ifndef __GC032A_H__
+#define __GC032A_H__
+
+#include "sensor.h"
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ *     0:       Can't detect this sensor
+ *     Nonzero: This sensor has been detected
+ */
+int gc032a_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int gc032a_init(sensor_t *sensor);
+
+#endif // __GC032A_H__

+ 82 - 0
std/camera/sensors/private_include/gc032a_regs.h

@@ -0,0 +1,82 @@
+/*
+ * GC032A register definitions.
+ */
+#ifndef __GC032A_REG_REGS_H__
+#define __GC032A_REG_REGS_H__
+
+#define SENSOR_ID_HIGH 0XF0
+#define SENSOR_ID_LOW 0XF1
+#define PAD_VB_HIZ_MODE 0XF2
+#define SYNC_OUTPUT 0XF3
+#define I2C_CONFIG 0XF4
+#define PLL_MODE1 0XF7
+#define PLL_MODE2 0XF8
+#define CM_MODE 0XF9
+#define ISP_DIV_MODE 0XFA
+#define I2C_DEVICE_ID 0XFB
+#define ANALOG_PWC 0XFC
+#define ISP_DIV_MODE2 0XFD
+#define RESET_RELATED   0XFE    // Bit[7]: Software reset 
+                                // Bit[6]: cm reset 
+                                // Bit[5]: spi reset 
+                                // Bit[4]: CISCTL_restart_n
+                                // Bit[3]: PLL_rst
+                                // Bit[2:0]: page select
+                                //  000:page0
+                                //  001:page1
+                                //  010:page2
+                                //  011:page3
+
+//----page0-----------------------------
+#define P0_EXPOSURE_HIGH 0X03
+#define P0_EXPOSURE_LOW 0X04
+#define P0_HB_HIGH 0X05
+#define P0_HB_LOW 0X06
+#define P0_VB_HIGH 0X07
+#define P0_VB_LOW 0X08
+#define P0_ROW_START_HIGH 0X09
+#define P0_ROW_START_LOW 0X0A
+#define P0_COLUMN_START_HIGH 0X0B
+#define P0_COLUMN_START_LOW 0X0C
+#define P0_WINDOW_HEIGHT_HIGH 0X0D
+#define P0_WINDOW_HEIGHT_LOW 0X0E
+#define P0_WINDOW_WIDTH_HIGH 0X0F
+#define P0_WINDOW_WIDTH_LOW 0X10
+#define P0_SH_DELAY 0X11
+#define P0_VS_ST 0X12
+#define P0_VS_ET 0X13
+#define P0_CISCTL_MODE1 0X17
+
+#define P0_BLOCK_ENABLE_1 0X40
+#define P0_AAAA_ENABLE 0X42
+#define P0_SPECIAL_EFFECT 0X43
+#define P0_SYNC_MODE 0X46
+#define P0_GAIN_CODE 0X48
+#define P0_DEBUG_MODE2 0X4C
+#define P0_WIN_MODE 0X50
+#define P0_OUT_WIN_Y1_HIGH 0X51
+#define P0_OUT_WIN_Y1_LOW 0X52
+#define P0_OUT_WIN_X1_HIGH 0X53
+#define P0_OUT_WIN_X1_LOW 0X54
+#define P0_OUT_WIN_HEIGHT_HIGH 0X55
+#define P0_OUT_WIN_HEIGHT_LOW 0X56
+#define P0_OUT_WIN_WIDTH_HIGH 0X57
+#define P0_OUT_WIN_WIDTH_LOW 0X58
+
+#define P0_GLOBAL_SATURATION 0XD0
+#define P0_SATURATION_CB 0XD1
+#define P0_SATURATION_CR 0XD2
+#define P0_LUMA_CONTRAST 0XD3
+#define P0_CONTRAST_CENTER 0XD4
+#define P0_LUMA_OFFSET 0XD5
+#define P0_FIXED_CB 0XDA
+#define P0_FIXED_CR 0XDB
+
+//----page3-----------------------------
+#define P3_IMAGE_WIDTH_LOW 0X5B
+#define P3_IMAGE_WIDTH_HIGH 0X5C
+#define P3_IMAGE_HEIGHT_LOW 0X5D
+#define P3_IMAGE_HEIGHT_HIGH 0X5E
+
+
+#endif //__GC032A_REG_REGS_H__

+ 401 - 0
std/camera/sensors/private_include/gc032a_settings.h

@@ -0,0 +1,401 @@
+#ifndef _GC032A_SETTINGS_H_
+#define _GC032A_SETTINGS_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp_attr.h"
+#include "gc032a_regs.h"
+
+
+#define REG_DLY 0xffff
+#define REGLIST_TAIL 0x0000
+
+
+/*
+ * The default register settings, as obtained from OmniVision.  There
+ * is really no making sense of most of these - lots of "reserved" values
+ * and such.
+ *
+ */
+static const uint16_t gc032a_default_regs[][2] = {
+    /*System*/
+    {0xf3, 0xff},
+    {0xf5, 0x06},
+    {0xf7, 0x01},
+    {0xf8, 0x03},
+    {0xf9, 0xce},
+    {0xfa, 0x00},
+    {0xfc, 0x02},
+    {0xfe, 0x02},
+    {0x81, 0x03},
+
+    {0xfe, 0x00},
+    {0x77, 0x64},
+    {0x78, 0x40},
+    {0x79, 0x60},
+    /*ANALOG & CISCTL*/
+    {0xfe, 0x00},
+    {0x03, 0x01},
+    {0x04, 0xce},
+    {0x05, 0x01},
+    {0x06, 0xad},
+    {0x07, 0x00},
+    {0x08, 0x10},
+    {0x0a, 0x00},
+    {0x0c, 0x00},
+    {0x0d, 0x01},
+    {0x0e, 0xe8}, // height 488
+    {0x0f, 0x02},
+    {0x10, 0x88}, // width 648
+    {0x17, 0x54},
+    {0x19, 0x08},
+    {0x1a, 0x0a},
+    {0x1f, 0x40},
+    {0x20, 0x30},
+    {0x2e, 0x80},
+    {0x2f, 0x2b},
+    {0x30, 0x1a},
+    {0xfe, 0x02},
+    {0x03, 0x02},
+    {0x05, 0xd7},
+    {0x06, 0x60},
+    {0x08, 0x80},
+    {0x12, 0x89},
+
+    /*blk*/
+    {0xfe, 0x00},
+    {0x18, 0x02},
+    {0xfe, 0x02},
+    {0x40, 0x22},
+    {0x45, 0x00},
+    {0x46, 0x00},
+    {0x49, 0x20},
+    {0x4b, 0x3c},
+    {0x50, 0x20},
+    {0x42, 0x10},
+
+    /*isp*/
+    {0xfe, 0x01},
+    {0x0a, 0xc5},
+    {0x45, 0x00},
+    {0xfe, 0x00},
+    {0x40, 0xff},
+    {0x41, 0x25},
+    {0x42, 0xcf},
+    {0x43, 0x10},
+    {0x44, 0x83},
+    {0x46, 0x23},
+    {0x49, 0x03},
+    {0x52, 0x02},
+    {0x54, 0x00},
+    {0xfe, 0x02},
+    {0x22, 0xf6},
+
+    /*Shading*/
+    {0xfe, 0x01},
+    {0xc1, 0x38},
+    {0xc2, 0x4c},
+    {0xc3, 0x00},
+    {0xc4, 0x32},
+    {0xc5, 0x24},
+    {0xc6, 0x16},
+    {0xc7, 0x08},
+    {0xc8, 0x08},
+    {0xc9, 0x00},
+    {0xca, 0x20},
+    {0xdc, 0x8a},
+    {0xdd, 0xa0},
+    {0xde, 0xa6},
+    {0xdf, 0x75},
+
+    /*AWB*/
+    {0xfe, 0x01},
+    {0x7c, 0x09},
+    {0x65, 0x06},
+    {0x7c, 0x08},
+    {0x56, 0xf4},
+    {0x66, 0x0f},
+    {0x67, 0x84},
+    {0x6b, 0x80},
+    {0x6d, 0x12},
+    {0x6e, 0xb0},
+    {0x86, 0x00},
+    {0x87, 0x00},
+    {0x88, 0x00},
+    {0x89, 0x00},
+    {0x8a, 0x00},
+    {0x8b, 0x00},
+    {0x8c, 0x00},
+    {0x8d, 0x00},
+    {0x8e, 0x00},
+    {0x8f, 0x00},
+    {0x90, 0x00},
+    {0x91, 0x00},
+    {0x92, 0xf4},
+    {0x93, 0xd5},
+    {0x94, 0x50},
+    {0x95, 0x0f},
+    {0x96, 0xf4},
+    {0x97, 0x2d},
+    {0x98, 0x0f},
+    {0x99, 0xa6},
+    {0x9a, 0x2d},
+    {0x9b, 0x0f},
+    {0x9c, 0x59},
+    {0x9d, 0x2d},
+    {0x9e, 0xaa},
+    {0x9f, 0x67},
+    {0xa0, 0x59},
+    {0xa1, 0x00},
+    {0xa2, 0x00},
+    {0xa3, 0x0a},
+    {0xa4, 0x00},
+    {0xa5, 0x00},
+    {0xa6, 0xd4},
+    {0xa7, 0x9f},
+    {0xa8, 0x55},
+    {0xa9, 0xd4},
+    {0xaa, 0x9f},
+    {0xab, 0xac},
+    {0xac, 0x9f},
+    {0xad, 0x55},
+    {0xae, 0xd4},
+    {0xaf, 0xac},
+    {0xb0, 0xd4},
+    {0xb1, 0xa3},
+    {0xb2, 0x55},
+    {0xb3, 0xd4},
+    {0xb4, 0xac},
+    {0xb5, 0x00},
+    {0xb6, 0x00},
+    {0xb7, 0x05},
+    {0xb8, 0xd6},
+    {0xb9, 0x8c},
+
+    /*CC*/
+    {0xfe, 0x01},
+    {0xd0, 0x40},
+    {0xd1, 0xf8},
+    {0xd2, 0x00},
+    {0xd3, 0xfa},
+    {0xd4, 0x45},
+    {0xd5, 0x02},
+
+    {0xd6, 0x30},
+    {0xd7, 0xfa},
+    {0xd8, 0x08},
+    {0xd9, 0x08},
+    {0xda, 0x58},
+    {0xdb, 0x02},
+    {0xfe, 0x00},
+
+    /*Gamma*/
+    {0xfe, 0x00},
+    {0xba, 0x00},
+    {0xbb, 0x04},
+    {0xbc, 0x0a},
+    {0xbd, 0x0e},
+    {0xbe, 0x22},
+    {0xbf, 0x30},
+    {0xc0, 0x3d},
+    {0xc1, 0x4a},
+    {0xc2, 0x5d},
+    {0xc3, 0x6b},
+    {0xc4, 0x7a},
+    {0xc5, 0x85},
+    {0xc6, 0x90},
+    {0xc7, 0xa5},
+    {0xc8, 0xb5},
+    {0xc9, 0xc2},
+    {0xca, 0xcc},
+    {0xcb, 0xd5},
+    {0xcc, 0xde},
+    {0xcd, 0xea},
+    {0xce, 0xf5},
+    {0xcf, 0xff},
+
+    /*Auto Gamma*/
+    {0xfe, 0x00},
+    {0x5a, 0x08},
+    {0x5b, 0x0f},
+    {0x5c, 0x15},
+    {0x5d, 0x1c},
+    {0x5e, 0x28},
+    {0x5f, 0x36},
+    {0x60, 0x45},
+    {0x61, 0x51},
+    {0x62, 0x6a},
+    {0x63, 0x7d},
+    {0x64, 0x8d},
+    {0x65, 0x98},
+    {0x66, 0xa2},
+    {0x67, 0xb5},
+    {0x68, 0xc3},
+    {0x69, 0xcd},
+    {0x6a, 0xd4},
+    {0x6b, 0xdc},
+    {0x6c, 0xe3},
+    {0x6d, 0xf0},
+    {0x6e, 0xf9},
+    {0x6f, 0xff},
+
+    /*Gain*/
+    {0xfe, 0x00},
+    {0x70, 0x50},
+
+    /*AEC*/
+    {0xfe, 0x00},
+    {0x4f, 0x01},
+    {0xfe, 0x01},
+    {0x0d, 0x00},
+    {0x12, 0xa0},
+    {0x13, 0x3a},
+    {0x44, 0x04},
+    {0x1f, 0x30},
+    {0x20, 0x40},
+    {0x26, 0x9a},
+    {0x3e, 0x20},
+    {0x3f, 0x2d},
+    {0x40, 0x40},
+    {0x41, 0x5b},
+    {0x42, 0x82},
+    {0x43, 0xb7},
+    {0x04, 0x0a},
+    {0x02, 0x79},
+    {0x03, 0xc0},
+
+    /*measure window*/
+    {0xfe, 0x01},
+    {0xcc, 0x08},
+    {0xcd, 0x08},
+    {0xce, 0xa4},
+    {0xcf, 0xec},
+
+    /*DNDD*/
+    {0xfe, 0x00},
+    {0x81, 0xb8},
+    {0x82, 0x12},
+    {0x83, 0x0a},
+    {0x84, 0x01},
+    {0x86, 0x50},
+    {0x87, 0x18},
+    {0x88, 0x10},
+    {0x89, 0x70},
+    {0x8a, 0x20},
+    {0x8b, 0x10},
+    {0x8c, 0x08},
+    {0x8d, 0x0a},
+
+    /*Intpee*/
+    {0xfe, 0x00},
+    {0x8f, 0xaa},
+    {0x90, 0x9c},
+    {0x91, 0x52},
+    {0x92, 0x03},
+    {0x93, 0x03},
+    {0x94, 0x08},
+    {0x95, 0x44},
+    {0x97, 0x00},
+    {0x98, 0x00},
+
+    /*ASDE*/
+    {0xfe, 0x00},
+    {0xa1, 0x30},
+    {0xa2, 0x41},
+    {0xa4, 0x30},
+    {0xa5, 0x20},
+    {0xaa, 0x30},
+    {0xac, 0x32},
+
+    /*YCP*/
+    {0xfe, 0x00},
+    {0xd1, 0x3c},
+    {0xd2, 0x3c},
+    {0xd3, 0x38},
+    {0xd6, 0xf4},
+    {0xd7, 0x1d},
+    {0xdd, 0x73},
+    {0xde, 0x84},
+
+    /*Banding*/
+    {0xfe, 0x00},
+    {0x05, 0x01},
+    {0x06, 0xad},
+    {0x07, 0x00},
+    {0x08, 0x10},
+
+    {0xfe, 0x01},
+    {0x25, 0x00},
+    {0x26, 0x9a},
+
+    {0x27, 0x01},
+    {0x28, 0xce},
+    {0x29, 0x02},
+    {0x2a, 0x68},
+    {0x2b, 0x02},
+    {0x2c, 0x68},
+    {0x2d, 0x07},
+    {0x2e, 0xd2},
+    {0x2f, 0x0b},
+    {0x30, 0x6e},
+    {0x31, 0x0e},
+    {0x32, 0x70},
+    {0x33, 0x12},
+    {0x34, 0x0c},
+    {0x3c, 0x30},
+
+    /*Analog&Cisctl*/
+    {0xfe, 0x00},
+    {0x05, 0x01},
+    {0x06, 0xa0},
+    {0x07, 0x00},
+    {0x08, 0x20},
+    {0x0a, 0x78},
+    {0x0c, 0xa0},
+    {0x0d, 0x00}, //window_height [8]
+    {0x0e, 0xf8}, //window_height [7:0] 248
+    {0x0f, 0x01}, //window_width [9:8]
+    {0x10, 0x48}, //window_width [7:0]  328
+
+    {0x55, 0x00},
+    {0x56, 0xf0}, // 240
+    {0x57, 0x01},
+    {0x58, 0x40}, // 320
+
+    /*SPI*/
+    {0xfe, 0x03},
+    {0x5b, 0x40},
+    {0x5c, 0x01},
+    {0x5d, 0xf0},
+    {0x5e, 0x00},
+
+    /*AEC*/
+    {0xfe, 0x01},
+    {0x25, 0x00}, //step
+    {0x26, 0x63},
+    {0x27, 0x01},
+    {0x28, 0x29},
+    {0x29, 0x01},
+    {0x2a, 0x29},
+    {0x2b, 0x01},
+    {0x2c, 0x29},
+    {0x2d, 0x01},
+    {0x2e, 0x29},
+    {0x2f, 0x01},
+    {0x30, 0x29},
+    {0x31, 0x01},
+    {0x32, 0x29},
+    {0x33, 0x01},
+    {0x34, 0x29},
+    {0x3c, 0x00},
+
+    /*measure window*/
+    {0xfe, 0x01},
+    {0xcc, 0x04},
+    {0xcd, 0x04},
+    {0xce, 0x72},
+    {0xcf, 0x52},
+    {REGLIST_TAIL, 0x00},
+};
+
+#endif

+ 27 - 0
std/camera/sensors/private_include/gc2145.h

@@ -0,0 +1,27 @@
+
+#ifndef __GC2145_H__
+#define __GC2145_H__
+
+#include "sensor.h"
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ *     0:       Can't detect this sensor
+ *     Nonzero: This sensor has been detected
+ */
+int gc2145_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int gc2145_init(sensor_t *sensor);
+
+#endif // __GC2145_H__

+ 85 - 0
std/camera/sensors/private_include/gc2145_regs.h

@@ -0,0 +1,85 @@
+/*
+ * GC2145 register definitions.
+ */
+#ifndef __GC2145_REG_REGS_H__
+#define __GC2145_REG_REGS_H__
+
+#define CHIP_ID_HIGH 0XF0
+#define CHIP_ID_LOW 0XF1
+#define PLL_MODE1 0XF7
+#define PLL_MODE2 0XF8
+#define CM_MODE 0XF9
+#define CLK_DIV_MODE 0XFA
+#define RESET_RELATED   0xfe    // Bit[7]: Software reset 
+                                // Bit[6]: cm reset 
+                                // Bit[5]: mipi reset 
+                                // Bit[4]: CISCTL_restart_n
+                                // Bit[3]: NA
+                                // Bit[2:0]: page select
+                                //  000:page0
+                                //  001:page1
+                                //  010:page2
+                                //  011:page3
+
+//-page0----------------
+
+#define P0_EXPOSURE_HIGH 0X03
+#define P0_EXPOSURE_LOW 0X04
+#define P0_HB_HIGH 0X05
+#define P0_HB_LOW 0X06
+#define P0_VB_HIGH 0X07
+#define P0_VB_LOW 0X08
+#define P0_ROW_START_HIGH 0X09
+#define P0_ROW_START_LOW 0X0A
+#define P0_COL_START_HIGH 0X0B
+#define P0_COL_START_LOW 0X0C
+
+#define P0_WIN_HEIGHT_HIGH 0X0D
+#define P0_WIN_HEIGHT_LOW 0X0E
+#define P0_WIN_WIDTH_HIGH 0X0F
+#define P0_WIN_WIDTH_LOW 0X10
+#define P0_ANALOG_MODE1 0X17
+#define P0_ANALOG_MODE2 0X18
+
+#define P0_SPECIAL_EFFECT 0X83
+#define P0_OUTPUT_FORMAT 0x84 // Format select
+                                // Bit[7]:YUV420 row switch
+                                // Bit[6]:YUV420 col switch
+                                // Bit[7]:YUV420_legacy
+                                // Bit[4:0]:output data mode
+                                //  5’h00 Cb Y Cr Y
+                                //  5’h01 Cr Y Cb Y
+                                //  5’h02 Y Cb Y Cr
+                                //  5’h03 Y Cr Y Cb
+                                //  5’h04 LSC bypass, C/Y
+                                //  5’h05 LSC bypass, Y/C
+                                //  5’h06 RGB 565
+                                //  5’h0f bypass 10bits
+                                //  5’h17 switch odd/even column /row to controls output Bayer pattern
+                                //    00 RGBG
+                                //    01 RGGB
+                                //    10 BGGR
+                                //    11 GBRG
+                                //  5'h18 DNDD out mode
+                                //  5'h19 LSC out mode
+                                //  5;h1b EEINTP out mode
+#define P0_FRAME_START 0X85
+#define P0_SYNC_MODE 0X86
+#define P0_MODULE_GATING 0X88
+#define P0_BYPASS_MODE 0X89
+#define P0_DEBUG_MODE2 0X8C
+#define P0_DEBUG_MODE3 0X8D
+#define P0_CROP_ENABLE 0X90
+#define P0_OUT_WIN_Y1_HIGH 0X91
+#define P0_OUT_WIN_Y1_LOW 0X92
+#define P0_OUT_WIN_X1_HIGH 0X93
+#define P0_OUT_WIN_X1_LOW 0X94
+#define P0_OUT_WIN_HEIGHT_HIGH 0X95
+#define P0_OUT_WIN_HEIGHT_LOW 0X96
+#define P0_OUT_WIN_WIDTH_HIGH 0X97
+#define P0_OUT_WIN_WIDTH_LOW 0X98
+#define P0_SUBSAMPLE 0X99
+#define P0_SUBSAMPLE_MODE 0X9A
+
+
+#endif // __GC2145_REG_REGS_H__

+ 719 - 0
std/camera/sensors/private_include/gc2145_settings.h

@@ -0,0 +1,719 @@
+
+#include <stdint.h>
+
+#define REG_DLY 0xffff
+#define REGLIST_TAIL 0x0000 /* Array end token */
+
+static const uint16_t gc2145_default_init_regs[][2] = {
+    {0xfe, 0xf0},
+    {0xfe, 0xf0},
+    {0xfe, 0xf0},
+
+    {0xfc, 0x06},
+    {0xf6, 0x00},
+
+    {0xf7, 0x1d}, //37 //17 //37 //1d//05
+    {0xf8, 0x83}, //87 //83 //82
+    {0xfa, 0x00},
+    {0xf9, 0xfe}, //ff
+    {0xfd, 0x00},
+    {0xc2, 0x00},
+    {0xf2, 0x0f},
+//////////////////////////////////////////////////////
+////////////////////  Analog & Cisctl ////////////////
+//////////////////////////////////////////////////////
+    {0xfe, 0x00},
+
+    {0x03, 0x04}, //exp time
+    {0x04, 0x62}, //exp time
+
+    {0x05, 0x01}, //00 //hb[11:8]
+    {0x06, 0x3b}, //0b //hb
+
+    {0x09, 0x00}, //row start
+    {0x0a, 0x00}, //
+    {0x0b, 0x00}, //col start
+    {0x0c, 0x00},
+    {0x0d, 0x04}, //height
+    {0x0e, 0xc0},
+    {0x0f, 0x06}, //width
+    {0x10, 0x52},
+
+    {0x12, 0x2e}, //sh_delay 太短 YUV出图异常
+    {0x17, 0x14}, //CISCTL Mode1 [1:0]mirror flip
+    {0x18, 0x22}, //sdark mode
+    {0x19, 0x0f}, // AD pipe number
+    {0x1a, 0x01}, //AD manual switch mode
+
+    {0x1b, 0x4b}, //48 restg Width,SH width
+    {0x1c, 0x07}, //06  帧率快后,横条纹 //12 //TX Width,Space Width
+    {0x1d, 0x10}, //double reset
+    {0x1e, 0x88}, //90//98 //fix  竖线//Analog Mode1,TX high,Coln_r
+    {0x1f, 0x78}, //78 //38 //18 //Analog Mode2,txlow
+    {0x20, 0x03}, //07 //Analog Mode3,comv,ad_clk mode
+    {0x21, 0x40}, //10//20//40 //fix 灯管横条纹
+    {0x22, 0xa0}, //d0//f0 //a2 //Vref vpix  FPN严重
+    {0x24, 0x1e},
+    {0x25, 0x01}, //col sel
+    {0x26, 0x10}, //Analog PGA gain1
+    {0x2d, 0x60}, //40//40 //txl drv mode
+    {0x30, 0x01}, //Analog Mode4
+    {0x31, 0x90}, //b0//70 // Analog Mode7 [7:5]rsgh_r灯管横条纹[4:3]isp_g
+    {0x33, 0x06}, //03//02//01 //EQ_hstart_width
+    {0x34, 0x01},
+//
+///////////////////////////////////////////////////
+////////////////////  ISP reg  //////////////////////
+//////////////////////////////////////////////////////
+    {0x80, 0xff}, //outdoor gamma_en, GAMMA_en, CC_en, EE_en, INTP_en, DN_en, DD_en,LSC_en
+    {0x81, 0x24}, //26//24 //BLK dither mode, ll_y_en ,skin_en, edge SA, new_skin_mode, autogray_en,ll_gamma_en,BFF test image
+    {0x82, 0xfa}, //FA //auto_SA, auto_EE, auto_DN, auto_DD, auto_LSC, ABS_en, AWB_en, NA
+    {0x83, 0x00}, //special_effect
+    {0x84, 0x02}, //output format
+    {0x86, 0x03}, //c2 //46 //c2 //sync mode
+    {0x88, 0x03}, //[1]ctl_auto_gating [0]out_auto_gating
+    {0x89, 0x03}, //bypass disable
+    {0x85, 0x30}, //60//frame start cut
+    {0x8a, 0x00}, //ISP_quiet_mode,close aaa pclk,BLK gate mode,exception,close first pipe clock,close dndd clock,close intp clock,DIV_gatedclk_en
+    {0x8b, 0x00}, //[7:6]BFF_gate_mode,[5]BLK switch gain,[4]protect exp,[3:2]pipe gate mode,[1]not split sram,[0]dark current update
+
+    {0xb0, 0x55}, //60 //global gain
+    {0xc3, 0x00}, //[7:4]auto_exp_gamma_th1[11:8],[3:0]auto_exp_gamma_th2[11:8]
+    {0xc4, 0x80}, //auto_exp_gamma_th1[7:0] into
+    {0xc5, 0x90}, //auto_exp_gamma_th2[7:0] out //outdoor gamma
+    {0xc6, 0x38}, //auto_gamma_th1
+    {0xc7, 0x40}, //auto_gamma_th2
+
+    {0xec, 0x06}, //measure window
+    {0xed, 0x04},
+    {0xee, 0x60}, //16  col
+    {0xef, 0x90}, //8  row
+
+    {0xb6, 0x01}, //[0]aec en
+
+    {0x90, 0x01}, //crop
+    {0x91, 0x00},
+    {0x92, 0x00},
+    {0x93, 0x00},
+    {0x94, 0x00}, //08
+    {0x95, 0x04},
+    {0x96, 0xb0},
+    {0x97, 0x06},
+    {0x98, 0x40},
+
+///////////////////////////////////////////////
+///////////  BLK ////////////////////////
+///////////////////////////////////////////////
+    {0x18, 0x02},
+    {0x40, 0x42}, //2b //27
+    {0x41, 0x00}, //80 //dark row sel
+    {0x43, 0x54}, //[7:4]BLK start not smooth  [3:0]output start frame
+
+    {0x5e, 0x00}, //00//10 //18
+    {0x5f, 0x00}, //00//10 //18
+    {0x60, 0x00}, //00//10 //18
+    {0x61, 0x00}, //00///10 //18
+    {0x62, 0x00}, //00//10 //18
+    {0x63, 0x00}, //00//10 //18
+    {0x64, 0x00}, //00/10 //18
+    {0x65, 0x00}, //00//10 //18
+    {0x66, 0x20}, //1e
+    {0x67, 0x20}, //1e
+    {0x68, 0x20}, //1e
+    {0x69, 0x20}, //1e
+
+
+    {0x76, 0x00}, //0f
+
+    {0x6a, 0x00}, //06
+    {0x6b, 0x00}, //06
+    {0x6c, 0x3e}, //06
+    {0x6d, 0x3e}, //06
+    {0x6e, 0x3f}, //06
+    {0x6f, 0x3f}, //06
+    {0x70, 0x00}, //06
+    {0x71, 0x00}, //06 //manual offset
+
+    {0x76, 0x00}, //1f//add offset
+    {0x72, 0xf0}, //[7:4]BLK DD th [3:0]BLK various th
+    {0x7e, 0x3c}, //ndark
+    {0x7f, 0x00},
+
+    {0xfe, 0x02},
+    {0x48, 0x15},
+    {0x49, 0x00}, //04//04 //ASDE OFFSET SLOPE
+    {0x4b, 0x0b}, //ASDE y OFFSET SLOPE
+    {0xfe, 0x00},
+
+///////////////////////////////////////////////
+/////////// AEC ////////////////////////
+///////////////////////////////////////////////
+    {0xfe, 0x01},
+
+    {0x01, 0x04}, //AEC X1
+    {0x02, 0xc0}, //AEC X2
+    {0x03, 0x04}, //AEC Y1
+    {0x04, 0x90}, //AEC Y2
+    {0x05, 0x30}, //20 //AEC center X1
+    {0x06, 0x90}, //40 //AEC center X2
+    {0x07, 0x20}, //30 //AEC center Y1
+    {0x08, 0x70}, //60 //AEC center Y2
+
+    {0x09, 0x00}, //AEC show mode
+    {0x0a, 0xc2}, //[7]col gain enable
+    {0x0b, 0x11}, //AEC every N
+    {0x0c, 0x10}, //AEC_mode3 center weight
+    {0x13, 0x40}, //2a //AEC Y target
+    {0x17, 0x00}, //AEC ignore mode
+    {0x1c, 0x11}, //
+    {0x1e, 0x61}, //
+    {0x1f, 0x30}, //40//50 //max pre gain
+    {0x20, 0x40}, //60//40 //max post gain
+    {0x22, 0x80}, //AEC outdoor THD
+    {0x23, 0x20}, //target_Y_low_limit
+    {0xfe, 0x02},
+    {0x0f, 0x04}, //05
+    {0xfe, 0x01},
+
+    {0x12, 0x35}, //35 //[5:4]group_size [3]slope_disable [2]outdoor_enable [0]histogram_enable
+    {0x15, 0x50}, //target_Y_high_limit
+    {0x10, 0x31}, //num_thd_high
+    {0x3e, 0x28}, //num_thd_low
+    {0x3f, 0xe0}, //luma_thd
+    {0x40, 0x20}, //luma_slope
+    {0x41, 0x0f}, //color_diff
+
+    {0xfe, 0x02},
+    {0x0f, 0x05}, //max_col_level
+///////////////////////////
+////// INTPEE /////////////
+///////////////////////////
+    {0xfe, 0x02}, //page2
+    {0x90, 0x6c}, //ac //eeintp mode1
+    {0x91, 0x03}, //02 ////eeintp mode2
+    {0x92, 0xc8}, //44 //low criteria for direction
+    {0x94, 0x66},
+    {0x95, 0xb5},
+    {0x97, 0x64}, //78 ////edge effect
+    {0xa2, 0x11}, //fix direction
+    {0xfe, 0x00},
+
+/////////////////////////////
+//////// DNDD///////////////
+/////////////////////////////
+    {0xfe, 0x02},
+    {0x80, 0xc1}, //c1 //[7]share mode [6]skin mode  [5]is 5x5 mode [1:0]noise value select 0:2  1:2.5  2:3  3:4
+    {0x81, 0x08}, //
+    {0x82, 0x08}, //signal a 0.6
+    {0x83, 0x08}, //04 //signal b 2.5
+
+    {0x84, 0x0a}, //10 //05 dark_DD_TH
+    {0x86, 0xf0}, //a0 Y_value_dd_th2
+    {0x87, 0x50}, //90 Y_value_dd_th3
+    {0x88, 0x15}, //60 Y_value_dd_th4
+
+    {0x89, 0x50}, //80  // asde th2
+    {0x8a, 0x30}, //60  // asde th3
+    {0x8b, 0x10}, //30  // asde th4
+
+/////////////////////////////////////////////////
+///////////// ASDE ////////////////////////
+/////////////////////////////////////////////////
+    {0xfe, 0x01}, //page 1
+    {0x21, 0x14}, //luma_value_div_sel(分频,与0xef呈2倍关系,增大1,0xef的值减小1倍)
+//ff  ef  luma_value read_only
+
+    {0xfe, 0x02}, //page2
+    {0xa3, 0x40}, //ASDE_low_luma_value_LSC_th_H
+    {0xa4, 0x20}, //ASDE_low_luma_value_LSC_th_L
+
+    {0xa5, 0x40}, //80 //ASDE_LSC_gain_dec_slope_H
+    {0xa6, 0x80}, // 80 //ASDE_LSC_gain_dec_slope_L
+//ff  a7  ASDE_LSC_gain_dec  //read only
+
+    {0xab, 0x40}, //50 //ASDE_low_luma_value_OT_th
+
+    {0xae, 0x0c}, //[3]EE1_effect_inc_or_dec_high,[2]EE2_effect_inc_or_dec_high,
+    //[1]EE1_effect_inc_or_dec_low,[0]EE2_effect_inc_or_dec_low,  1:inc  0:dec
+
+    {0xb3, 0x34}, //44 //ASDE_EE1_effect_slope_low,ASDE_EE2_effect_slope_low
+    {0xb4, 0x44}, //12 //ASDE_EE1_effect_slope_high,ASDE_EE2_effect_slope_high
+
+    {0xb6, 0x38}, //40//40 //ASDE_auto_saturation_dec_slope
+    {0xb7, 0x02}, //04 //ASDE_sub_saturation_slope
+    {0xb9, 0x30}, //[7:0]ASDE_auto_saturation_low_limit
+    {0x3c, 0x08}, //[3:0]auto gray_dec_slope
+    {0x3d, 0x30}, //[7:0]auto gray_dec_th
+
+
+    {0x4b, 0x0d}, //y offset slope
+    {0x4c, 0x20}, //y offset limit
+
+    {0xfe, 0x00},
+//
+///////////////////gamma1////////////////////
+////Gamma
+    {0xfe, 0x02},
+    {0x10, 0x10},
+    {0x11, 0x15},
+    {0x12, 0x1a},
+    {0x13, 0x1f},
+    {0x14, 0x2c},
+    {0x15, 0x39},
+    {0x16, 0x45},
+    {0x17, 0x54},
+    {0x18, 0x69},
+    {0x19, 0x7d},
+    {0x1a, 0x8f},
+    {0x1b, 0x9d},
+    {0x1c, 0xa9},
+    {0x1d, 0xbd},
+    {0x1e, 0xcd},
+    {0x1f, 0xd9},
+    {0x20, 0xe3},
+    {0x21, 0xea},
+    {0x22, 0xef},
+    {0x23, 0xf5},
+    {0x24, 0xf9},
+    {0x25, 0xff},
+
+/////auto gamma/////
+    {0xfe, 0x02},
+    {0x26, 0x0f},
+    {0x27, 0x14},
+    {0x28, 0x19},
+    {0x29, 0x1e},
+    {0x2a, 0x27},
+    {0x2b, 0x33},
+    {0x2c, 0x3b},
+    {0x2d, 0x45},
+    {0x2e, 0x59},
+    {0x2f, 0x69},
+    {0x30, 0x7c},
+    {0x31, 0x89},
+    {0x32, 0x98},
+    {0x33, 0xae},
+    {0x34, 0xc0},
+    {0x35, 0xcf},
+    {0x36, 0xda},
+    {0x37, 0xe2},
+    {0x38, 0xe9},
+    {0x39, 0xf3},
+    {0x3a, 0xf9},
+    {0x3b, 0xff},
+
+///////////////////////////////////////////////
+///////////   YCP       ///////////////////////
+///////////////////////////////////////////////
+    {0xfe, 0x02},
+    {0xd1, 0x30}, //32 //
+    {0xd2, 0x30}, //32 //
+    {0xd3, 0x45},
+    {0xdd, 0x14}, //edge sa
+    {0xde, 0x86}, //asde auto gray
+    {0xed, 0x01}, //
+    {0xee, 0x28},
+    {0xef, 0x30},
+    {0xd8, 0xd8}, //autogray protecy
+
+////////////////////////////
+//////// LSC  0.8///////////////
+////////////////////////////
+    {0xfe, 0x01},
+    {0xa1, 0x80}, // center_row
+    {0xa2, 0x80}, // center_col
+    {0xa4, 0x00}, // sign of b1
+    {0xa5, 0x00}, // sign of b1
+    {0xa6, 0x70}, // sign of b4
+    {0xa7, 0x00}, // sign of b4
+    {0xa8, 0x77}, // sign of b22
+    {0xa9, 0x77}, // sign of b22
+    {0xaa, 0x1f}, // Q1_b1 of R
+    {0xab, 0x0d}, // Q1_b1 of G
+    {0xac, 0x19}, // Q1_b1 of B
+    {0xad, 0x24}, // Q2_b1 of R
+    {0xae, 0x0e}, // Q2_b1 of G
+    {0xaf, 0x1d}, // Q2_b1 of B
+    {0xb0, 0x12}, // Q3_b1 of R
+    {0xb1, 0x0c}, // Q3_b1 of G
+    {0xb2, 0x06}, // Q3_b1 of B
+    {0xb3, 0x13}, // Q4_b1 of R
+    {0xb4, 0x10}, // Q4_b1 of G
+    {0xb5, 0x0c}, // Q4_b1 of B
+    {0xb6, 0x6a}, // right_b2 of R
+    {0xb7, 0x46}, // right_b2 of G
+    {0xb8, 0x40}, // right_b2 of B
+    {0xb9, 0x0b}, // right_b4 of R
+    {0xba, 0x04}, // right_b4 of G
+    {0xbb, 0x00}, // right_b4 of B
+    {0xbc, 0x53}, // left_b2 of R
+    {0xbd, 0x37}, // left_b2 of G
+    {0xbe, 0x2d}, // left_b2 of B
+    {0xbf, 0x0a}, // left_b4 of R
+    {0xc0, 0x0a}, // left_b4 of G
+    {0xc1, 0x14}, // left_b4 of B
+    {0xc2, 0x34}, // up_b2 of R
+    {0xc3, 0x22}, // up_b2 of G
+    {0xc4, 0x18}, // up_b2 of B
+    {0xc5, 0x23}, // up_b4 of R
+    {0xc6, 0x0f}, // up_b4 of G
+    {0xc7, 0x3c}, // up_b4 of B
+    {0xc8, 0x20}, // down_b2 of R
+    {0xc9, 0x1f}, // down_b2 of G
+    {0xca, 0x17}, // down_b2 of B
+    {0xcb, 0x2d}, // down_b4 of R
+    {0xcc, 0x12}, // down_b4 of G
+    {0xcd, 0x20}, // down_b4 of B
+    {0xd0, 0x61}, // right_up_b22 of R
+    {0xd1, 0x2f}, // right_up_b22 of G
+    {0xd2, 0x39}, // right_up_b22 of B
+    {0xd3, 0x45}, // right_down_b22 of R
+    {0xd4, 0x2c}, // right_down_b22 of G
+    {0xd5, 0x21}, // right_down_b22 of B
+    {0xd6, 0x64}, // left_up_b22 of R
+    {0xd7, 0x2d}, // left_up_b22 of G
+    {0xd8, 0x30}, // left_up_b22 of B
+    {0xd9, 0x42}, // left_down_b22 of R
+    {0xda, 0x27}, // left_down_b22 of G
+    {0xdb, 0x13}, // left_down_b22 of B
+    {0xfe, 0x00},
+
+/////////////////////////////////////////////////
+/////////////    AWB     ////////////////////////
+/////////////////////////////////////////////////
+    {0xfe, 0x01},
+
+    {0x4f, 0x00},
+    {0x4f, 0x00},
+    {0x4b, 0x01},
+    {0x4f, 0x00},
+
+
+    {0x4c, 0x01},
+    {0x4d, 0x6f},
+    {0x4e, 0x02},
+    {0x4c, 0x01},
+    {0x4d, 0x70},
+
+    {0x4e, 0x02},
+    {0x4c, 0x01},
+    {0x4d, 0x8f},
+    {0x4e, 0x02},
+
+    {0x4c, 0x01},
+    {0x4d, 0x90},
+    {0x4e, 0x02}, //light
+
+
+    {0x4c, 0x01},
+    {0x4d, 0xed},
+    {0x4e, 0x33}, //light
+    {0x4c, 0x01},
+    {0x4d, 0xcd},
+    {0x4e, 0x33}, //light
+    {0x4c, 0x01},
+    {0x4d, 0xec},
+    {0x4e, 0x03}, //light
+
+    {0x4c, 0x01},
+    {0x4d, 0x6c},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0x6d},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0x6e},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0x8c},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0x8d},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0x8e},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0xab},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0xac},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0xad},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0xae},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0xcb},
+    {0x4e, 0x03},
+
+    {0x4c, 0x01},
+    {0x4d, 0xcc},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0xce},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0xeb},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0xec},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0xee},
+    {0x4e, 0x03},
+    {0x4c, 0x02},
+    {0x4d, 0x0c},
+    {0x4e, 0x03},
+    {0x4c, 0x02},
+    {0x4d, 0x0d},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0xea},
+    {0x4e, 0x03},
+    {0x4c, 0x01},
+    {0x4d, 0xaf},
+    {0x4e, 0x03}, //dark
+    {0x4c, 0x01},
+    {0x4d, 0xcf},
+    {0x4e, 0x03}, //dark
+
+    {0x4c, 0x01},
+    {0x4d, 0xca},
+    {0x4e, 0x04}, //light
+    {0x4c, 0x02},
+    {0x4d, 0x0b},
+    {0x4e, 0x05}, //light
+    {0x4c, 0x02},
+    {0x4d, 0xc8},
+    {0x4e, 0x06}, //light 100lux
+    {0x4c, 0x02},
+    {0x4d, 0xa8},
+
+    {0x4e, 0x06}, //light
+    {0x4c, 0x02},
+    {0x4d, 0xa9},
+    {0x4e, 0x06}, //light
+
+
+    {0x4c, 0x02},
+    {0x4d, 0x89},
+    {0x4e, 0x06}, //400lux
+    {0x4c, 0x02},
+    {0x4d, 0x69},
+    {0x4e, 0x06}, //f12
+    {0x4c, 0x02},
+    {0x4d, 0x6a},
+    {0x4e, 0x06}, //f12
+    {0x4c, 0x02},
+    {0x4d, 0xc7},
+    {0x4e, 0x07},
+    {0x4c, 0x02},
+    {0x4d, 0xe7},
+    {0x4e, 0x07}, //100lux
+    {0x4c, 0x03},
+    {0x4d, 0x07},
+    {0x4e, 0x07}, //light
+
+    {0x4c, 0x02},
+    {0x4d, 0xe8},
+    {0x4e, 0x07},
+    {0x4c, 0x02},
+    {0x4d, 0xe9},
+    {0x4e, 0x07},
+    {0x4c, 0x03},
+    {0x4d, 0x08},
+    {0x4e, 0x07},
+    {0x4c, 0x03},
+    {0x4d, 0x09},
+    {0x4e, 0x07},
+    {0x4c, 0x03},
+    {0x4d, 0x27},
+    {0x4e, 0x07},
+    {0x4c, 0x03},
+    {0x4d, 0x28},
+    {0x4e, 0x07},
+    {0x4c, 0x03},
+    {0x4d, 0x29},
+    {0x4e, 0x07},
+    {0x4c, 0x03},
+    {0x4d, 0x47},
+    {0x4e, 0x07},
+    {0x4c, 0x03},
+    {0x4d, 0x48},
+    {0x4e, 0x07},
+    {0x4c, 0x03},
+    {0x4d, 0x49},
+    {0x4e, 0x07},
+    {0x4c, 0x03},
+    {0x4d, 0x67},
+    {0x4e, 0x07},
+    {0x4c, 0x03},
+    {0x4d, 0x68},
+    {0x4e, 0x07},
+    {0x4c, 0x03},
+    {0x4d, 0x69},
+    {0x4e, 0x07},
+
+    {0x4f, 0x01},
+    {0xfe, 0x01},
+    {0x50, 0x80}, //AWB_PRE_mode
+    {0x51, 0xa8}, //AWB_pre_THD_min[7:0]
+    {0x52, 0x57}, //AWB_pre_THD_min[15:8] Dominiate luma 0.25=639c 0.22=57a8
+    {0x53, 0x38}, //AWB_pre_THD_min_MIX[7:0]
+    {0x54, 0xc7}, //AWB_pre_THD_min_MIX[15:8] Mix luma 0.5
+
+    {0x56, 0x0e}, //AWB_tone mode
+    {0x58, 0x08}, //AWB_C_num_sel,AWB_D_num_sel
+    {0x5b, 0x00}, //AWB_mix_mode
+
+    {0x5c, 0x74}, //green_num0[7:0]
+    {0x5d, 0x8b}, //green_num0[15:8] 0.35
+
+    {0x61, 0xd3}, //R2G_stand0
+    {0x62, 0xb5}, //B2G_stand0
+    {0x63, 0x00}, //88//a4 //AWB gray mode [7]enable
+    {0x65, 0x04}, //AWB margin
+
+    {0x67, 0xb2}, //R2G_stand3[7:0]  FF/CWF
+    {0x68, 0xac}, //B2G_stand3[7:0]
+    {0x69, 0x00}, //R2G_stand4[9:8] B2G_stand4[9:8] R2G_stand3[9:8] B2G_stand3[9:8]
+    {0x6a, 0xb2}, //R2G_stand4[7:0]  TL84/TL84&CWF
+    {0x6b, 0xac}, //B2G_stand4[7:0]
+    {0x6c, 0xb2}, //R2G_stand5[7:0]  A
+    {0x6d, 0xac}, //B2G_stand5[7:0]
+    {0x6e, 0x40}, //AWB_skin_weight R2G_stand5[9:8] B2G_stand5[9:8]
+    {0x6f, 0x18}, //AWB_indoor_THD (0x21=17 caculate)
+    {0x73, 0x00}, //AWB_indoor_mode
+
+    {0x70, 0x10}, //AWB low luma TH
+    {0x71, 0xe8}, //AWB outdoor TH
+    {0x72, 0xc0}, //outdoor mode
+    {0x74, 0x01}, //[2:0]AWB skip mode 2x2,4x4,4x8,8x8
+    {0x75, 0x01}, //[1:0]AWB_every_N
+    {0x7f, 0x08}, //[3]gray world frame start
+
+    {0x76, 0x70}, //R limit
+    {0x77, 0x58}, //G limit
+    {0x78, 0xa0}, //d8 //B limit
+
+    {0xfe, 0x00},
+//
+//////////////////////////////////////////
+///////////  CC   ////////////////////////
+//////////////////////////////////////////
+    {0xfe, 0x02},
+
+    {0xc0, 0x01}, //[5:4] CC mode [0]CCT enable
+
+    {0xC1, 0x50}, //D50/D65
+    {0xc2, 0xF9},
+    {0xc3, 0x00}, //0
+    {0xc4, 0xe8}, //e0
+    {0xc5, 0x48},
+    {0xc6, 0xf0},
+
+
+    {0xC7, 0x50},
+    {0xc8, 0xf2},
+    {0xc9, 0x00},
+    {0xcA, 0xE0},
+    {0xcB, 0x45},
+    {0xcC, 0xec},
+
+    {0xCd, 0x45},
+    {0xce, 0xf0},
+    {0xcf, 0x00},
+    {0xe3, 0xf0},
+    {0xe4, 0x45},
+    {0xe5, 0xe8},
+
+
+    {0xfe, 0x00},
+
+    {0xf2, 0x0f},
+
+
+//////////////frame rate   50Hz
+    {0xfe, 0x00},
+
+    {0xf7, 0x1d},
+    {0xf8, 0x84},
+    {0xfa, 0x00},
+
+    {0x05, 0x01}, //hb
+    {0x06, 0x3b},
+    {0x07, 0x01}, //Vb
+    {0x08, 0x0b},
+
+    {0xfe, 0x01},
+    {0x25, 0x01},
+    {0x26, 0x32}, //step
+    {0x27, 0x03}, //8.15fps
+    {0x28, 0x96},
+    {0x29, 0x03}, //8.15fps
+    {0x2a, 0x96},
+    {0x2b, 0x03}, //8.15fps
+    {0x2c, 0x96},
+    {0x2d, 0x04}, //8.15fps
+    {0x2e, 0x62},
+    {0x3c, 0x00},
+    {0xfe, 0x00},
+
+/////////dark  sun//////
+    {0xfe, 0x00},
+    {0x18, 0x22},
+    {0xfe, 0x02},
+    {0x40, 0xbf},
+    {0x46, 0xcf},
+    {0xfe, 0x00},
+
+    {0xfe, 0x00},
+
+    {0xf7, 0x1d},
+    {0xf8, 0x84},
+    {0xfa, 0x10},
+
+    {0x05, 0x01}, //hb
+    {0x06, 0x18},
+    {0x07, 0x00}, //Vb
+    {0x08, 0x2e},
+
+    {0xfe, 0x01},
+    {0x25, 0x00},
+    {0x26, 0xa2}, //step
+    {0x27, 0x01},
+    {0x28, 0xe6},
+    {0x29, 0x01},
+    {0x2a, 0xe6},
+    {0x2b, 0x01},
+    {0x2c, 0xe6},
+    {0x2d, 0x04}, // AEC_exp_level4[12:8]
+    {0x2e, 0x62}, // AEC_exp_level4[7:0]
+    {0x3c, 0x00},
+    {0xfe, 0x00},
+
+    {0x09, 0x01}, //row start
+    {0x0a, 0xd0}, //
+    {0x0b, 0x02}, //col start
+    {0x0c, 0x70},
+    {0x0d, 0x01}, //height
+    {0x0e, 0x00},
+    {0x0f, 0x01}, //width
+    {0x10, 0x50},
+
+    {0x90, 0x01}, //crop
+    {0x91, 0x00},
+    {0x92, 0x00},
+    {0x93, 0x00},
+    {0x94, 0x00},
+    {0x95, 0x00},
+    {0x96, 0xf0},
+    {0x97, 0x01},
+    {0x98, 0x40},
+
+
+    {REGLIST_TAIL, 0x00},
+};

+ 34 - 0
std/camera/sensors/private_include/nt99141.h

@@ -0,0 +1,34 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * NT99141 driver.
+ *
+ */
+#ifndef __NT99141_H__
+#define __NT99141_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 nt99141_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int nt99141_init(sensor_t *sensor);
+
+#endif // __NT99141_H__

+ 211 - 0
std/camera/sensors/private_include/nt99141_regs.h

@@ -0,0 +1,211 @@
+/*
+ * NT99141 register definitions.
+ */
+#ifndef __NT99141_REG_REGS_H__
+#define __NT99141_REG_REGS_H__
+
+/* system control registers */
+#define SYSTEM_CTROL0   0x3021  // Bit[7]: Software reset 
+                                // Bit[6]: Software power down 
+                                // Bit[5]: Reserved 
+                                // Bit[4]: SRB clock SYNC enable 
+                                // Bit[3]: Isolation suspend select 
+                                // Bit[2:0]: Not used
+
+/* output format control registers */
+#define FORMAT_CTRL     0x501F // Format select
+                                // Bit[2:0]:
+                                //  000: YUV422						
+                                //  001: RGB
+                                //  010: Dither
+                                //  011: RAW after DPC
+                                //  101: RAW after CIP
+
+/* format control registers */
+#define FORMAT_CTRL00   0x4300
+
+/* frame control registers */
+#define FRAME_CTRL01    0x4201  // Control Passed Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
+                                // Bit[7:4]: Not used
+                                // Bit[3:0]: Frame ON number
+#define FRAME_CTRL02    0x4202  // Control Masked Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
+                                // Bit[7:4]: Not used
+                                // BIT[3:0]: Frame OFF number
+
+/* ISP top control registers */
+#define PRE_ISP_TEST_SETTING_1  0x3025  // Bit[7]: Test enable
+                                        //         0: Test disable
+                                        //         1: Color bar enable
+                                        // Bit[6]: Rolling
+                                        // Bit[5]: Transparent
+                                        // Bit[4]: Square black and white
+                                        // Bit[3:2]: Color bar style
+                                        //         00: Standard 8 color bar
+                                        //         01: Gradual change at vertical mode 1
+                                        //         10: Gradual change at horizontal
+                                        //         11: Gradual change at vertical mode 2
+                                        // Bit[1:0]: Test select
+                                        //         00: Color bar
+                                        //         01: Random data
+                                        //         10: Square data
+                                        //         11: Black image
+
+//exposure = {0x3500[3:0], 0x3501[7:0], 0x3502[7:0]} / 16 × tROW
+
+/* AEC/AGC control functions */
+#define AEC_PK_MANUAL   0x3201  // AEC Manual Mode Control
+                                // Bit[7:6]: Reserved
+                                // Bit[5]: Gain delay option
+                                //         Valid when 0x3503[4]=1’b0
+                                //         0: Delay one frame latch
+                                //         1: One frame latch
+                                // Bit[4:2]: Reserved
+                                // Bit[1]: AGC manual
+                                //         0: Auto enable
+                                //         1: Manual enable
+                                // Bit[0]: AEC manual
+                                //         0: Auto enable
+                                //         1: Manual enable
+
+//gain = {0x350A[1:0], 0x350B[7:0]} / 16
+
+/* mirror and flip registers */
+#define TIMING_TC_REG20 0x3022  // Timing Control Register
+                                // Bit[2:1]: Vertical flip enable
+                                //         00: Normal
+                                //         11: Vertical flip
+                                // Bit[0]: Vertical binning enable
+#define TIMING_TC_REG21 0x3022  // Timing Control Register
+                                // Bit[5]: Compression Enable
+                                // Bit[2:1]: Horizontal mirror enable
+                                //         00: Normal
+                                //         11: Horizontal mirror
+                                // Bit[0]: Horizontal binning enable
+
+#define CLOCK_POL_CONTROL 0x3024// Bit[5]: PCLK polarity 0: active low
+                                //          1: active high
+                                // Bit[3]: Gate PCLK under VSYNC
+                                // Bit[2]: Gate PCLK under HREF
+                                // Bit[1]: HREF polarity
+                                //          0: active low
+                                //          1: active high
+                                // Bit[0] VSYNC polarity
+                                //          0: active low
+                                //          1: active high
+#define DRIVE_CAPABILITY 0x306a // Bit[7:6]:
+                                //          00: 1x
+                                //          01: 2x
+                                //          10: 3x
+                                //          11: 4x
+
+
+#define X_ADDR_ST_H     0x3800 //Bit[3:0]: X address start[11:8]
+#define X_ADDR_ST_L     0x3801 //Bit[7:0]: X address start[7:0]
+#define Y_ADDR_ST_H     0x3802 //Bit[2:0]: Y address start[10:8]
+#define Y_ADDR_ST_L     0x3803 //Bit[7:0]: Y address start[7:0]
+#define X_ADDR_END_H    0x3804 //Bit[3:0]: X address end[11:8]
+#define X_ADDR_END_L    0x3805 //Bit[7:0]:
+#define Y_ADDR_END_H    0x3806 //Bit[2:0]: Y address end[10:8]
+#define Y_ADDR_END_L    0x3807 //Bit[7:0]:
+// Size after scaling
+#define X_OUTPUT_SIZE_H 0x3808 //Bit[3:0]: DVP output horizontal width[11:8]
+#define X_OUTPUT_SIZE_L 0x3809 //Bit[7:0]:
+#define Y_OUTPUT_SIZE_H 0x380a //Bit[2:0]: DVP output vertical height[10:8]
+#define Y_OUTPUT_SIZE_L 0x380b //Bit[7:0]:
+#define X_TOTAL_SIZE_H  0x380c //Bit[3:0]: Total horizontal size[11:8]
+#define X_TOTAL_SIZE_L  0x380d //Bit[7:0]:
+#define Y_TOTAL_SIZE_H  0x380e //Bit[7:0]: Total vertical size[15:8]
+#define Y_TOTAL_SIZE_L  0x380f //Bit[7:0]:
+#define X_OFFSET_H      0x3810 //Bit[3:0]: ISP horizontal offset[11:8]
+#define X_OFFSET_L      0x3811 //Bit[7:0]:
+#define Y_OFFSET_H      0x3812 //Bit[2:0]: ISP vertical offset[10:8]
+#define Y_OFFSET_L      0x3813 //Bit[7:0]:
+#define X_INCREMENT     0x3814 //Bit[7:4]: Horizontal odd subsample increment
+                               //Bit[3:0]: Horizontal even subsample increment
+#define Y_INCREMENT     0x3815 //Bit[7:4]: Vertical odd subsample increment
+                               //Bit[3:0]: Vertical even subsample increment
+// Size before scaling
+//#define X_INPUT_SIZE    (X_ADDR_END - X_ADDR_ST + 1 - (2 * X_OFFSET))
+//#define Y_INPUT_SIZE    (Y_ADDR_END - Y_ADDR_ST + 1 - (2 * Y_OFFSET))
+
+#define ISP_CONTROL_01   0x3021 // Bit[5]: Scale enable
+                                //          0: Disable
+                                //          1: Enable
+
+#define SCALE_CTRL_1     0x5601 // Bit[6:4]: HDIV RW
+                                //          DCW scale times
+                                //          000: DCW 1 time
+                                //          001: DCW 2 times
+                                //          010: DCW 4 times
+                                //          100: DCW 8 times
+                                //          101: DCW 16 times
+                                //          Others: DCW 16 times
+                                // Bit[2:0]: VDIV RW
+                                //          DCW scale times
+                                //          000: DCW 1 time
+                                //          001: DCW 2 times
+                                //          010: DCW 4 times
+                                //          100: DCW 8 times
+                                //          101: DCW 16 times
+                                //          Others: DCW 16 times
+
+#define SCALE_CTRL_2     0x5602 // X_SCALE High Bits
+#define SCALE_CTRL_3     0x5603 // X_SCALE Low Bits
+#define SCALE_CTRL_4     0x5604 // Y_SCALE High Bits
+#define SCALE_CTRL_5     0x5605 // Y_SCALE Low Bits
+#define SCALE_CTRL_6     0x5606 // Bit[3:0]: V Offset
+
+#define PCLK_RATIO       0x3824 // Bit[4:0]: PCLK ratio manual
+#define VFIFO_CTRL0C     0x460C // Bit[1]: PCLK manual enable
+                                //          0: Auto
+                                //          1: Manual by PCLK_RATIO
+
+#define VFIFO_X_SIZE_H   0x4602
+#define VFIFO_X_SIZE_L   0x4603
+#define VFIFO_Y_SIZE_H   0x4604
+#define VFIFO_Y_SIZE_L   0x4605
+
+#define SC_PLLS_CTRL0    0x303a // Bit[7]: PLLS bypass
+#define SC_PLLS_CTRL1    0x303b // Bit[4:0]: PLLS multiplier
+#define SC_PLLS_CTRL2    0x303c // Bit[6:4]: PLLS charge pump control
+                                // Bit[3:0]: PLLS system divider
+#define SC_PLLS_CTRL3    0x303d // Bit[5:4]: PLLS pre-divider
+                                //          00: 1
+                                //          01: 1.5
+                                //          10: 2
+                                //          11: 3
+                                // Bit[2]: PLLS root-divider - 1
+                                // Bit[1:0]: PLLS seld5
+                                //          00: 1
+                                //          01: 1
+                                //          10: 2
+                                //          11: 2.5
+
+#define COMPRESSION_CTRL00 0x4400 //
+#define COMPRESSION_CTRL01 0x4401 //
+#define COMPRESSION_CTRL02 0x4402 //
+#define COMPRESSION_CTRL03 0x4403 //
+#define COMPRESSION_CTRL04 0x4404 //
+#define COMPRESSION_CTRL05 0x4405 //
+#define COMPRESSION_CTRL06 0x4406 //
+#define COMPRESSION_CTRL07 0x3401 // Bit[5:0]: QS
+#define COMPRESSION_ISI_CTRL 0x4408 //
+#define COMPRESSION_CTRL09 0x4409 //
+#define COMPRESSION_CTRL0a 0x440a //
+#define COMPRESSION_CTRL0b 0x440b //
+#define COMPRESSION_CTRL0c 0x440c //
+#define COMPRESSION_CTRL0d 0x440d //
+#define COMPRESSION_CTRL0E 0x440e //
+
+/**
+ * @brief register value
+ */
+#define TEST_COLOR_BAR  0x02    /* Enable Color Bar roling Test */
+
+#define AEC_PK_MANUAL_AGC_MANUALEN  0x02    /* Enable AGC Manual enable */
+#define AEC_PK_MANUAL_AEC_MANUALEN  0x01    /* Enable AEC Manual enable */
+
+#define TIMING_TC_REG20_VFLIP   0x01 /* Vertical flip enable */
+#define TIMING_TC_REG21_HMIRROR 0x02 /* Horizontal mirror enable */
+
+#endif // __NT99141_REG_REGS_H__

+ 825 - 0
std/camera/sensors/private_include/nt99141_settings.h

@@ -0,0 +1,825 @@
+#ifndef _NT99141_SETTINGS_H_
+#define _NT99141_SETTINGS_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp_attr.h"
+#include "nt99141_regs.h"
+
+static const ratio_settings_t ratio_table[] = {
+    //  mw,   mh,  sx,  sy,   ex,   ey, ox, oy,   tx,   ty
+    { 1280, 720,   0,   4, 1283, 723, 0, 4, 1660, 963 }, 
+
+};
+
+#define REG_DLY 0xffff
+#define REGLIST_TAIL 0x0000
+
+static const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
+ //initial
+{0x3021, 0x00},
+{REG_DLY, 100}, // delay 100ms
+{0x3109, 0x04},
+{0x3040, 0x04},
+{0x3041, 0x02},
+{0x3042, 0xFF},
+{0x3043, 0x08},
+{0x3052, 0xE0},
+{0x305F, 0x33},
+{0x3100, 0x07},
+{0x3106, 0x03},
+{0x3105, 0x01},
+{0x3108, 0x05},
+{0x3110, 0x22},
+{0x3111, 0x57},
+{0x3112, 0x22},
+{0x3113, 0x55},
+{0x3114, 0x05},
+{0x3135, 0x00},
+{0x32F0, 0x01},
+{0x3290, 0x01},
+{0x3291, 0x80},
+{0x3296, 0x01},
+{0x3297, 0x73},
+{0x3250, 0x80},
+{0x3251, 0x03},
+{0x3252, 0xFF},
+{0x3253, 0x00},
+{0x3254, 0x03},
+{0x3255, 0xFF},
+{0x3256, 0x00},
+{0x3257, 0x50},
+{0x3270, 0x00},
+{0x3271, 0x0C},
+{0x3272, 0x18},
+{0x3273, 0x32},
+{0x3274, 0x44},
+{0x3275, 0x54},
+{0x3276, 0x70},
+{0x3277, 0x88},
+{0x3278, 0x9D},
+{0x3279, 0xB0},
+{0x327A, 0xCF},
+{0x327B, 0xE2},
+{0x327C, 0xEF},
+{0x327D, 0xF7},
+{0x327E, 0xFF},
+{0x3302, 0x00},
+{0x3303, 0x40},
+{0x3304, 0x00},
+{0x3305, 0x96},
+{0x3306, 0x00},
+{0x3307, 0x29},
+{0x3308, 0x07},
+{0x3309, 0xBA},
+{0x330A, 0x06},
+{0x330B, 0xF5},
+{0x330C, 0x01},
+{0x330D, 0x51},
+{0x330E, 0x01},
+{0x330F, 0x30},
+{0x3310, 0x07},
+{0x3311, 0x16},
+{0x3312, 0x07},
+{0x3313, 0xBA},
+{0x3326, 0x02},
+{0x32F6, 0x0F},
+{0x32F9, 0x42},
+{0x32FA, 0x24},
+{0x3325, 0x4A},
+{0x3330, 0x00},
+{0x3331, 0x0A},
+{0x3332, 0xFF},
+{0x3338, 0x30},
+{0x3339, 0x84},
+{0x333A, 0x48},
+{0x333F, 0x07},
+{0x3360, 0x10},
+{0x3361, 0x18},
+{0x3362, 0x1f},
+{0x3363, 0x37},
+{0x3364, 0x80},
+{0x3365, 0x80},
+{0x3366, 0x68},
+{0x3367, 0x60},
+{0x3368, 0x30},
+{0x3369, 0x28},
+{0x336A, 0x20},
+{0x336B, 0x10},
+{0x336C, 0x00},
+{0x336D, 0x20},
+{0x336E, 0x1C},
+{0x336F, 0x18},
+{0x3370, 0x10},
+{0x3371, 0x38},
+{0x3372, 0x3C},
+{0x3373, 0x3F},
+{0x3374, 0x3F},
+{0x338A, 0x34},
+{0x338B, 0x7F},
+{0x338C, 0x10},
+{0x338D, 0x23},
+{0x338E, 0x7F},
+{0x338F, 0x14},
+{0x3375, 0x08},
+{0x3376, 0x0C},
+{0x3377, 0x18},
+{0x3378, 0x20},
+{0x3012, 0x02},
+{0x3013, 0xD0},
+{0x3025, 0x02}, //colorbar
+{REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = {
+    {0x32F0, 0x70}, // YUV422
+    {REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_raw[][2] = {
+    {0x32F0, 0x50}, // RAW
+    {REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_grayscale[][2] = {
+    {0x32F1, 0x01},
+    {REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_yuv422[][2] = {
+    {0x32F0, 0x00}, // YUV422
+    {REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_rgb565[][2] = {
+    {0x32F0, 0x01}, // RGB
+    {REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint8_t sensor_saturation_levels[9][1] = {
+    {0x60},//-4
+    {0x68},//-3
+    {0x70},//-2
+    {0x78},//-1
+    {0x80},//0
+    {0x88},//+1
+    {0x90},//+2
+    {0x98},//+3
+    {0xA0},//+4
+};
+
+static const DRAM_ATTR uint8_t sensor_special_effects[7][4] = {
+    {0x00, 0x80, 0x80, 0x01},//Normal
+    {0x03, 0x80, 0x80, 0x01},//Negative
+    {0x01, 0x80, 0x80, 0x01},//Grayscale
+    {0x05, 0x2A, 0xF0, 0x01},//Red Tint
+    {0x05, 0x60, 0x20, 0x01},//Green Tint
+    {0x05, 0xF0, 0x80, 0x01},//Blue Tint
+    {0x02, 0x80, 0x80, 0x01},//Sepia
+	
+};
+
+// AE LEVEL
+static const DRAM_ATTR uint16_t sensor_ae_level[][2] = {
+
+// 1. [AE_Target : 0x24]
+// Set_Device_Format = FORMAT_16_8
+// SET_Device_Addr = 0x54
+ {0x32B8, 0x29 },
+ {0x32B9, 0x1F },
+ {0x32BC, 0x24 },
+ {0x32BD, 0x27 },
+ {0x32BE, 0x21 },
+//------------------------------------------------------------------------
+// 2. [AE_Target : 0x28]
+// Set_Device_Format = FORMAT_16_8
+// SET_Device_Addr = 0x54
+ {0x32B8, 0x2D },
+ {0x32B9, 0x23 },
+ {0x32BC, 0x28 },
+ {0x32BD, 0x2B },
+ {0x32BE, 0x25 },
+//------------------------------------------------------------------------
+// 3. [AE_Target : 0x2C]
+// Set_Device_Format = FORMAT_16_8
+// SET_Device_Addr = 0x54
+ {0x32B8, 0x32 },
+ {0x32B9, 0x26 },
+ {0x32BC, 0x2C },
+ {0x32BD, 0x2F },
+ {0x32BE, 0x29 },
+//------------------------------------------------------------------------
+// 4, [AE_Target : 0x30]
+// Set_Device_Format = FORMAT_16_8
+// SET_Device_Addr = 0x54
+ {0x32B8, 0x36 },
+ {0x32B9, 0x2A },
+ {0x32BC, 0x30 },
+ {0x32BD, 0x33 },
+ {0x32BE, 0x2D },
+//------------------------------------------------------------------------
+// 5. [AE_Target : 0x34]
+// Set_Device_Format = FORMAT_16_8
+// SET_Device_Addr = 0x54
+ {0x32B8, 0x3B },
+ {0x32B9, 0x2D },
+ {0x32BC, 0x34 },
+ {0x32BD, 0x38 },
+ {0x32BE, 0x30 },
+//------------------------------------------------------------------------
+// 6. [AE_Target : 0x38]
+// Set_Device_Format = FORMAT_16_8
+// SET_Device_Addr = 0x54
+ {0x32B8, 0x3F },
+ {0x32B9, 0x31 },
+ {0x32BC, 0x38 },
+ {0x32BD, 0x3C },
+ {0x32BE, 0x34 },
+//------------------------------------------------------------------------
+// 7. [AE_Target : 0x3D]
+// Set_Device_Format = FORMAT_16_8
+// SET_Device_Addr = 0x54
+ {0x32B8, 0x44 },
+ {0x32B9, 0x34 },
+ {0x32BC, 0x3C },
+ {0x32BD, 0x40 },
+ {0x32BE, 0x38 },
+//------------------------------------------------------------------------
+// 8. [AE_Target : 0x40]
+// Set_Device_Format = FORMAT_16_8
+// SET_Device_Addr = 0x54
+ {0x32B8, 0x48 },
+ {0x32B9, 0x38 },
+ {0x32BC, 0x40 },
+ {0x32BD, 0x44 },
+ {0x32BE, 0x3C },
+//------------------------------------------------------------------------
+// 9. [AE_Target : 0x44]
+// Set_Device_Format = FORMAT_16_8
+// SET_Device_Addr = 0x54
+ {0x32B8, 0x4D },
+ {0x32B9, 0x3B },
+ {0x32BC, 0x44 },
+ {0x32BD, 0x49 },
+ {0x32BE, 0x3F },
+};
+
+static const DRAM_ATTR uint16_t sensor_framesize_HD[][2] = {
+//[JPEG_1280x720_8.18_8.18_Fps]
+{0x3021, 0x00},
+{REG_DLY, 100}, // delay 100ms
+{0x32BF, 0x60}, 
+{0x32C0, 0x5A}, 
+{0x32C1, 0x5A}, 
+{0x32C2, 0x5A}, 
+{0x32C3, 0x00}, 
+{0x32C4, 0x20}, 
+{0x32C5, 0x20}, 
+{0x32C6, 0x20}, 
+{0x32C7, 0x00}, 
+{0x32C8, 0x3C}, 
+{0x32C9, 0x5A}, 
+{0x32CA, 0x7A}, 
+{0x32CB, 0x7A}, 
+{0x32CC, 0x7A}, 
+{0x32CD, 0x7A}, 
+{0x32DB, 0x5E}, 
+{0x32F0, 0x70}, 
+{0x3400, 0x08}, 
+{0x3400, 0x00}, 
+{0x3401, 0x4E}, 
+{0x3404, 0x00}, 
+{0x3405, 0x00}, 
+{0x3410, 0x00}, 
+{0x3200, 0x3E}, 
+{0x3201, 0x0F}, 
+{0x3028, 0x0F}, 
+{0x3029, 0x00}, 
+{0x302A, 0x08}, 
+{0x3022, 0x24}, 
+{0x3023, 0x24}, 
+{0x3002, 0x00}, 
+{0x3003, 0x04}, 
+{0x3004, 0x00}, 
+{0x3005, 0x04}, 
+{0x3006, 0x05}, 
+{0x3007, 0x03}, 
+{0x3008, 0x02}, 
+{0x3009, 0xD3}, 
+{0x300A, 0x06}, 
+{0x300B, 0x7C}, 
+{0x300C, 0x02}, 
+{0x300D, 0xE0}, 
+{0x300E, 0x05}, 
+{0x300F, 0x00}, 
+{0x3010, 0x02}, 
+{0x3011, 0xD0}, 
+{0x32B8, 0x3F}, 
+{0x32B9, 0x31}, 
+{0x32BB, 0x87}, 
+{0x32BC, 0x38}, 
+{0x32BD, 0x3C}, 
+{0x32BE, 0x34}, 
+{0x3201, 0x3F}, 
+{0x3021, 0x06},
+{0x3025, 0x00}, //normal 
+{0x3400, 0x01}, 
+{0x3060, 0x01}, 
+{REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_framesize_VGA[][2] = {
+//[JPEG_640x480_10.14_10.14_Fps]
+{0x3021, 0x00},
+{REG_DLY, 100}, // delay 100ms
+{0x32BF, 0x60}, 
+{0x32C0, 0x5A}, 
+{0x32C1, 0x5A}, 
+{0x32C2, 0x5A}, 
+{0x32C3, 0x00}, 
+{0x32C4, 0x20}, 
+{0x32C5, 0x20}, 
+{0x32C6, 0x20}, 
+{0x32C7, 0x00}, 
+{0x32C8, 0x4B}, 
+{0x32C9, 0x5A}, 
+{0x32CA, 0x7A}, 
+{0x32CB, 0x7A}, 
+{0x32CC, 0x7A}, 
+{0x32CD, 0x7A}, 
+{0x32DB, 0x62}, 
+{0x32F0, 0x70}, 
+{0x3400, 0x08}, 
+{0x3400, 0x00}, 
+{0x3401, 0x4E}, 
+{0x3404, 0x00}, 
+{0x3405, 0x00}, 
+{0x3410, 0x00}, 
+{0x32E0, 0x02}, 
+{0x32E1, 0x80}, 
+{0x32E2, 0x01}, 
+{0x32E3, 0xE0}, 
+{0x32E4, 0x00}, 
+{0x32E5, 0x80}, 
+{0x32E6, 0x00}, 
+{0x32E7, 0x80}, 
+{0x3200, 0x3E}, 
+{0x3201, 0x0F}, 
+{0x3028, 0x0F}, 
+{0x3029, 0x00}, 
+{0x302A, 0x08}, 
+{0x3022, 0x24}, 
+{0x3023, 0x24}, 
+{0x3002, 0x00}, 
+{0x3003, 0xA4}, 
+{0x3004, 0x00}, 
+{0x3005, 0x04}, 
+{0x3006, 0x04}, 
+{0x3007, 0x63}, 
+{0x3008, 0x02}, 
+{0x3009, 0xD3}, 
+{0x300A, 0x05}, 
+{0x300B, 0x3C}, 
+{0x300C, 0x02}, 
+{0x300D, 0xE0}, 
+{0x300E, 0x03}, 
+{0x300F, 0xC0}, 
+{0x3010, 0x02}, 
+{0x3011, 0xD0}, 
+{0x32B8, 0x3F}, 
+{0x32B9, 0x31}, 
+{0x32BB, 0x87}, 
+{0x32BC, 0x38}, 
+{0x32BD, 0x3C}, 
+{0x32BE, 0x34}, 
+{0x3201, 0x7F}, 
+{0x3021, 0x06},
+{0x3025, 0x00}, //normal 
+{0x3400, 0x01}, 
+{0x3060, 0x01}, 
+{REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_framesize_QVGA[][2] = {
+//[JPEG_320x240_10.14_10.14_Fps] 
+{0x3021, 0x00},
+{REG_DLY, 100}, // delay 100ms
+{0x32BF, 0x60}, 
+{0x32C0, 0x5A}, 
+{0x32C1, 0x5A}, 
+{0x32C2, 0x5A}, 
+{0x32C3, 0x00}, 
+{0x32C4, 0x20}, 
+{0x32C5, 0x20}, 
+{0x32C6, 0x20}, 
+{0x32C7, 0x00}, 
+{0x32C8, 0x4B}, 
+{0x32C9, 0x5A}, 
+{0x32CA, 0x7A}, 
+{0x32CB, 0x7A}, 
+{0x32CC, 0x7A}, 
+{0x32CD, 0x7A}, 
+{0x32DB, 0x62}, 
+{0x32F0, 0x70}, 
+{0x3400, 0x08}, 
+{0x3400, 0x00}, 
+{0x3401, 0x4E}, 
+{0x3404, 0x00}, 
+{0x3405, 0x00}, 
+{0x3410, 0x00}, 
+{0x32E0, 0x01}, 
+{0x32E1, 0x40}, 
+{0x32E2, 0x00}, 
+{0x32E3, 0xF0}, 
+{0x32E4, 0x02}, 
+{0x32E5, 0x02}, 
+{0x32E6, 0x02}, 
+{0x32E7, 0x03}, 
+{0x3200, 0x3E}, 
+{0x3201, 0x0F}, 
+{0x3028, 0x0F}, 
+{0x3029, 0x00}, 
+{0x302A, 0x08}, 
+{0x3022, 0x24}, 
+{0x3023, 0x24}, 
+{0x3002, 0x00}, 
+{0x3003, 0xA4}, 
+{0x3004, 0x00}, 
+{0x3005, 0x04}, 
+{0x3006, 0x04}, 
+{0x3007, 0x63}, 
+{0x3008, 0x02}, 
+{0x3009, 0xD3}, 
+{0x300A, 0x05}, 
+{0x300B, 0x3C}, 
+{0x300C, 0x02}, 
+{0x300D, 0xE0}, 
+{0x300E, 0x03}, 
+{0x300F, 0xC0}, 
+{0x3010, 0x02}, 
+{0x3011, 0xD0}, 
+{0x32B8, 0x3F}, 
+{0x32B9, 0x31}, 
+{0x32BB, 0x87}, 
+{0x32BC, 0x38}, 
+{0x32BD, 0x3C}, 
+{0x32BE, 0x34}, 
+{0x3201, 0x7F}, 
+{0x3021, 0x06},
+{0x3025, 0x00}, //normal
+{0x3400, 0x01}, 
+{0x3060, 0x01}, 
+{REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_framesize_VGA_xyskip[][2] = {
+// [JPEG_640x360_20.00_25.01_Fps_XY_Skip]
+// Set_Device_Format = FORMAT_16_8 
+// SET_Device_Addr = 0x54 
+{0x3021, 0x00},
+{REG_DLY, 100}, // delay 100ms
+{0x32BF, 0x60 },
+{0x320A, 0xB2 },
+{0x32C0, 0x64 },
+{0x32C1, 0x64 },
+{0x32C2, 0x64 },
+{0x32C3, 0x00 },
+{0x32C4, 0x20 },
+{0x32C5, 0x20 },
+{0x32C6, 0x20 },
+{0x32C7, 0x00 },
+{0x32C8, 0x62 },
+{0x32C9, 0x64 },
+{0x32CA, 0x84 },
+{0x32CB, 0x84 },
+{0x32CC, 0x84 },
+{0x32CD, 0x84 },
+{0x32DB, 0x68 },
+{0x32F0, 0x70 },
+{0x3400, 0x08 },
+{0x3400, 0x00 },
+{0x3401, 0x4E },
+{0x3404, 0x00 },
+{0x3405, 0x00 },
+{0x3410, 0x00 },
+{0x3200, 0x3E },
+{0x3201, 0x0F },
+{0x3028, 0x0F },
+{0x3029, 0x00 },
+{0x302A, 0x08 },
+{0x3022, 0x24 },
+{0x3023, 0x6C },
+{0x3002, 0x00 },
+{0x3003, 0x04 },
+{0x3004, 0x00 },
+{0x3005, 0x04 },
+{0x3006, 0x05 },
+{0x3007, 0x03 },
+{0x3008, 0x02 },
+{0x3009, 0xD3 },
+{0x300A, 0x03 },
+{0x300B, 0xFC },
+{0x300C, 0x01 },
+{0x300D, 0x88 },
+{0x300E, 0x02 },
+{0x300F, 0x80 },
+{0x3010, 0x01 },
+{0x3011, 0x68 },
+{0x32B8, 0x3F },
+{0x32B9, 0x31 },
+{0x32BB, 0x87 },
+{0x32BC, 0x38 },
+{0x32BD, 0x3C },
+{0x32BE, 0x34 },
+{0x3201, 0x3F },
+{0x3025, 0x00 }, //normal
+{0x3021, 0x06 },
+{0x3400, 0x01 },
+{0x3060, 0x01 },
+{REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_framesize_VGA_xskip[][2] = {
+//[JPEG_640x480_Xskip_13.32_13.32_Fps]
+{0x3021, 0x00},
+{REG_DLY, 100}, // delay 100ms
+{0x32BF, 0x60}, 
+{0x32C0, 0x5A}, 
+{0x32C1, 0x5A}, 
+{0x32C2, 0x5A}, 
+{0x32C3, 0x00}, 
+{0x32C4, 0x20}, 
+{0x32C5, 0x20}, 
+{0x32C6, 0x20}, 
+{0x32C7, 0x00}, 
+{0x32C8, 0x62}, 
+{0x32C9, 0x5A}, 
+{0x32CA, 0x7A}, 
+{0x32CB, 0x7A}, 
+{0x32CC, 0x7A}, 
+{0x32CD, 0x7A}, 
+{0x32DB, 0x68}, 
+{0x32F0, 0x70}, 
+{0x3400, 0x08}, 
+{0x3400, 0x00}, 
+{0x3401, 0x4E}, 
+{0x3404, 0x00}, 
+{0x3405, 0x00}, 
+{0x3410, 0x00}, 
+{0x32E0, 0x02}, 
+{0x32E1, 0x80}, 
+{0x32E2, 0x01}, 
+{0x32E3, 0xE0}, 
+{0x32E4, 0x00}, 
+{0x32E5, 0x00}, 
+{0x32E6, 0x00}, 
+{0x32E7, 0x80}, 
+{0x3200, 0x3E}, 
+{0x3201, 0x0F}, 
+{0x3028, 0x0F}, 
+{0x3029, 0x00}, 
+{0x302A, 0x08}, 
+{0x3022, 0x24}, 
+{0x3023, 0x2C}, 
+{0x3002, 0x00}, 
+{0x3003, 0x04}, 
+{0x3004, 0x00}, 
+{0x3005, 0x04}, 
+{0x3006, 0x05}, 
+{0x3007, 0x03}, 
+{0x3008, 0x02}, 
+{0x3009, 0xD3}, 
+{0x300A, 0x03}, 
+{0x300B, 0xFC}, 
+{0x300C, 0x02}, 
+{0x300D, 0xE0}, 
+{0x300E, 0x02}, 
+{0x300F, 0x80}, 
+{0x3010, 0x02}, 
+{0x3011, 0xD0}, 
+{0x32B8, 0x3F}, 
+{0x32B9, 0x31}, 
+{0x32BB, 0x87}, 
+{0x32BC, 0x38}, 
+{0x32BD, 0x3C}, 
+{0x32BE, 0x34}, 
+{0x3201, 0x7F}, 
+{0x3021, 0x06},
+{0x3025, 0x00}, //normal 
+{0x3400, 0x01}, 
+{0x3060, 0x01}, 
+{REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_framesize_QVGA_xskip[][2] = {
+{0x3021, 0x00},
+{REG_DLY, 100}, // delay 100ms
+//[JPEG_320x240_Xskip_13.32_13.32_Fps]
+{0x32BF, 0x60}, 
+{0x32C0, 0x5A}, 
+{0x32C1, 0x5A}, 
+{0x32C2, 0x5A}, 
+{0x32C3, 0x00}, 
+{0x32C4, 0x20}, 
+{0x32C5, 0x20}, 
+{0x32C6, 0x20}, 
+{0x32C7, 0x00}, 
+{0x32C8, 0x62}, 
+{0x32C9, 0x5A}, 
+{0x32CA, 0x7A}, 
+{0x32CB, 0x7A}, 
+{0x32CC, 0x7A}, 
+{0x32CD, 0x7A}, 
+{0x32DB, 0x68}, 
+{0x32F0, 0x70}, 
+{0x3400, 0x08}, 
+{0x3400, 0x00}, 
+{0x3401, 0x4E}, 
+{0x3404, 0x00}, 
+{0x3405, 0x00}, 
+{0x3410, 0x00}, 
+{0x32E0, 0x01}, 
+{0x32E1, 0x40}, 
+{0x32E2, 0x00}, 
+{0x32E3, 0xF0}, 
+{0x32E4, 0x01}, 
+{0x32E5, 0x01}, 
+{0x32E6, 0x02}, 
+{0x32E7, 0x03}, 
+{0x3200, 0x3E}, 
+{0x3201, 0x0F}, 
+{0x3028, 0x0F}, 
+{0x3029, 0x00}, 
+{0x302A, 0x08}, 
+{0x3022, 0x24}, 
+{0x3023, 0x2C}, 
+{0x3002, 0x00}, 
+{0x3003, 0x04}, 
+{0x3004, 0x00}, 
+{0x3005, 0x04}, 
+{0x3006, 0x05}, 
+{0x3007, 0x03}, 
+{0x3008, 0x02}, 
+{0x3009, 0xD3}, 
+{0x300A, 0x03}, 
+{0x300B, 0xFC}, 
+{0x300C, 0x02}, 
+{0x300D, 0xE0}, 
+{0x300E, 0x02}, 
+{0x300F, 0x80}, 
+{0x3010, 0x02}, 
+{0x3011, 0xD0}, 
+{0x32B8, 0x3F}, 
+{0x32B9, 0x31}, 
+{0x32BB, 0x87}, 
+{0x32BC, 0x38}, 
+{0x32BD, 0x3C}, 
+{0x32BE, 0x34}, 
+{0x3201, 0x7F}, 
+{0x3021, 0x06},
+{0x3025, 0x00}, //normal 
+{0x3400, 0x01}, 
+{0x3060, 0x01},
+{REGLIST_TAIL, 0x00}, // tail
+};
+
+
+static const DRAM_ATTR uint16_t sensor_framesize_VGA_crop[][2] = {
+//[JPEG_640x480_Crop_19.77_19.77_Fps]
+{0x3021, 0x00},
+{REG_DLY, 100}, // delay 100ms
+{0x32BF, 0x60}, 
+{0x32C0, 0x5A}, 
+{0x32C1, 0x5A}, 
+{0x32C2, 0x5A}, 
+{0x32C3, 0x00}, 
+{0x32C4, 0x20}, 
+{0x32C5, 0x20}, 
+{0x32C6, 0x20}, 
+{0x32C7, 0x00}, 
+{0x32C8, 0x62}, 
+{0x32C9, 0x5A}, 
+{0x32CA, 0x7A}, 
+{0x32CB, 0x7A}, 
+{0x32CC, 0x7A}, 
+{0x32CD, 0x7A}, 
+{0x32DB, 0x68}, 
+{0x32F0, 0x70}, 
+{0x3400, 0x08}, 
+{0x3400, 0x00}, 
+{0x3401, 0x4E}, 
+{0x3404, 0x00}, 
+{0x3405, 0x00}, 
+{0x3410, 0x00}, 
+{0x3200, 0x3E}, 
+{0x3201, 0x0F}, 
+{0x3028, 0x0F}, 
+{0x3029, 0x00}, 
+{0x302A, 0x08}, 
+{0x3022, 0x24}, 
+{0x3023, 0x24}, 
+{0x3002, 0x01}, 
+{0x3003, 0x44}, 
+{0x3004, 0x00}, 
+{0x3005, 0x7C}, 
+{0x3006, 0x03}, 
+{0x3007, 0xC3}, 
+{0x3008, 0x02}, 
+{0x3009, 0x5B}, 
+{0x300A, 0x03}, 
+{0x300B, 0xFC}, 
+{0x300C, 0x01}, 
+{0x300D, 0xF0}, 
+{0x300E, 0x02}, 
+{0x300F, 0x80}, 
+{0x3010, 0x01}, 
+{0x3011, 0xE0}, 
+{0x32B8, 0x3F}, 
+{0x32B9, 0x31}, 
+{0x32BB, 0x87}, 
+{0x32BC, 0x38}, 
+{0x32BD, 0x3C}, 
+{0x32BE, 0x34}, 
+{0x3201, 0x3F}, 
+{0x3021, 0x06},
+{0x3025, 0x00}, //normal 
+{0x3400, 0x01}, 
+{0x3060, 0x01}, 
+{REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_framesize_QVGA_crop[][2] = {
+//[JPEG_320x240_Crop_19.77_19.77_Fps]
+{0x3021, 0x00},
+{REG_DLY, 100}, // delay 100ms
+{0x32BF, 0x60}, 
+{0x32C0, 0x5A}, 
+{0x32C1, 0x5A}, 
+{0x32C2, 0x5A}, 
+{0x32C3, 0x00}, 
+{0x32C4, 0x20}, 
+{0x32C5, 0x20}, 
+{0x32C6, 0x20}, 
+{0x32C7, 0x00}, 
+{0x32C8, 0x62}, 
+{0x32C9, 0x5A}, 
+{0x32CA, 0x7A}, 
+{0x32CB, 0x7A}, 
+{0x32CC, 0x7A}, 
+{0x32CD, 0x7A}, 
+{0x32DB, 0x68}, 
+{0x32F0, 0x70}, 
+{0x3400, 0x08}, 
+{0x3400, 0x00}, 
+{0x3401, 0x4E}, 
+{0x3404, 0x00}, 
+{0x3405, 0x00}, 
+{0x3410, 0x00}, 
+{0x32E0, 0x01}, 
+{0x32E1, 0x40}, 
+{0x32E2, 0x00}, 
+{0x32E3, 0xF0}, 
+{0x32E4, 0x01}, 
+{0x32E5, 0x01}, 
+{0x32E6, 0x01}, 
+{0x32E7, 0x02}, 
+{0x3200, 0x3E}, 
+{0x3201, 0x0F}, 
+{0x3028, 0x0F}, 
+{0x3029, 0x00}, 
+{0x302A, 0x08}, 
+{0x3022, 0x24}, 
+{0x3023, 0x24}, 
+{0x3002, 0x01}, 
+{0x3003, 0x44}, 
+{0x3004, 0x00}, 
+{0x3005, 0x7C}, 
+{0x3006, 0x03}, 
+{0x3007, 0xC3}, 
+{0x3008, 0x02}, 
+{0x3009, 0x5B}, 
+{0x300A, 0x03}, 
+{0x300B, 0xFC}, 
+{0x300C, 0x01}, 
+{0x300D, 0xF0}, 
+{0x300E, 0x02}, 
+{0x300F, 0x80}, 
+{0x3010, 0x01}, 
+{0x3011, 0xE0}, 
+{0x32B8, 0x3F}, 
+{0x32B9, 0x31}, 
+{0x32BB, 0x87}, 
+{0x32BC, 0x38}, 
+{0x32BD, 0x3C}, 
+{0x32BE, 0x34}, 
+{0x3201, 0x7F}, 
+{0x3021, 0x06},
+{0x3025, 0x00}, //normal 
+{0x3400, 0x01}, 
+{0x3060, 0x01}, 
+{REGLIST_TAIL, 0x00}, // tail
+};
+
+#endif
+
+

+ 32 - 0
std/camera/sensors/private_include/ov2640.h

@@ -0,0 +1,32 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV2640 driver.
+ *
+ */
+#ifndef __OV2640_H__
+#define __OV2640_H__
+#include "sensor.h"
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ *     0:       Can't detect this sensor
+ *     Nonzero: This sensor has been detected
+ */
+int ov2640_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int ov2640_init(sensor_t *sensor);
+
+#endif // __OV2640_H__

+ 216 - 0
std/camera/sensors/private_include/ov2640_regs.h

@@ -0,0 +1,216 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV2640 register definitions.
+ */
+#ifndef __REG_REGS_H__
+#define __REG_REGS_H__
+/* DSP register bank FF=0x00*/
+#define R_BYPASS            0x05
+#define QS                  0x44
+#define CTRLI               0x50
+#define HSIZE               0x51
+#define VSIZE               0x52
+#define XOFFL               0x53
+#define YOFFL               0x54
+#define VHYX                0x55
+#define DPRP                0x56
+#define TEST                0x57
+#define ZMOW                0x5A
+#define ZMOH                0x5B
+#define ZMHH                0x5C
+#define BPADDR              0x7C
+#define BPDATA              0x7D
+#define CTRL2               0x86
+#define CTRL3               0x87
+#define SIZEL               0x8C
+#define HSIZE8              0xC0
+#define VSIZE8              0xC1
+#define CTRL0               0xC2
+#define CTRL1               0xC3
+#define R_DVP_SP            0xD3
+#define IMAGE_MODE          0xDA
+#define RESET               0xE0
+#define MS_SP               0xF0
+#define SS_ID               0xF7
+#define SS_CTRL             0xF7
+#define MC_BIST             0xF9
+#define MC_AL               0xFA
+#define MC_AH               0xFB
+#define MC_D                0xFC
+#define P_CMD               0xFD
+#define P_STATUS            0xFE
+#define BANK_SEL            0xFF
+
+#define CTRLI_LP_DP         0x80
+#define CTRLI_ROUND         0x40
+
+#define CTRL0_AEC_EN        0x80
+#define CTRL0_AEC_SEL       0x40
+#define CTRL0_STAT_SEL      0x20
+#define CTRL0_VFIRST        0x10
+#define CTRL0_YUV422        0x08
+#define CTRL0_YUV_EN        0x04
+#define CTRL0_RGB_EN        0x02
+#define CTRL0_RAW_EN        0x01
+
+#define CTRL2_DCW_EN        0x20
+#define CTRL2_SDE_EN        0x10
+#define CTRL2_UV_ADJ_EN     0x08
+#define CTRL2_UV_AVG_EN     0x04
+#define CTRL2_CMX_EN        0x01
+
+#define CTRL3_BPC_EN        0x80
+#define CTRL3_WPC_EN        0x40
+
+#define R_DVP_SP_AUTO_MODE  0x80
+
+#define R_BYPASS_DSP_EN         0x00
+#define R_BYPASS_DSP_BYPAS      0x01
+
+#define IMAGE_MODE_Y8_DVP_EN    0x40
+#define IMAGE_MODE_JPEG_EN      0x10
+#define IMAGE_MODE_YUV422       0x00
+#define IMAGE_MODE_RAW10        0x04
+#define IMAGE_MODE_RGB565       0x08
+#define IMAGE_MODE_HREF_VSYNC   0x02
+#define IMAGE_MODE_LBYTE_FIRST  0x01
+
+#define RESET_MICROC            0x40
+#define RESET_SCCB              0x20
+#define RESET_JPEG              0x10
+#define RESET_DVP               0x04
+#define RESET_IPU               0x02
+#define RESET_CIF               0x01
+
+#define MC_BIST_RESET           0x80
+#define MC_BIST_BOOT_ROM_SEL    0x40
+#define MC_BIST_12KB_SEL        0x20
+#define MC_BIST_12KB_MASK       0x30
+#define MC_BIST_512KB_SEL       0x08
+#define MC_BIST_512KB_MASK      0x0C
+#define MC_BIST_BUSY_BIT_R      0x02
+#define MC_BIST_MC_RES_ONE_SH_W 0x02
+#define MC_BIST_LAUNCH          0x01
+
+
+typedef enum {
+    BANK_DSP, BANK_SENSOR, BANK_MAX
+} ov2640_bank_t;
+
+/* Sensor register bank FF=0x01*/
+#define GAIN                0x00
+#define COM1                0x03
+#define REG04               0x04
+#define REG08               0x08
+#define COM2                0x09
+#define REG_PID             0x0A
+#define REG_VER             0x0B
+#define COM3                0x0C
+#define COM4                0x0D
+#define AEC                 0x10
+#define CLKRC               0x11
+#define COM7                0x12
+#define COM8                0x13
+#define COM9                0x14 /* AGC gain ceiling */
+#define COM10               0x15
+#define HSTART              0x17
+#define HSTOP               0x18
+#define VSTART              0x19
+#define VSTOP               0x1A
+#define REG_MIDH            0x1C
+#define REG_MIDL            0x1D
+#define AEW                 0x24
+#define AEB                 0x25
+#define VV                  0x26
+#define REG2A               0x2A
+#define FRARL               0x2B
+#define ADDVSL              0x2D
+#define ADDVSH              0x2E
+#define YAVG                0x2F
+#define HSDY                0x30
+#define HEDY                0x31
+#define REG32               0x32
+#define ARCOM2              0x34
+#define REG45               0x45
+#define FLL                 0x46
+#define FLH                 0x47
+#define COM19               0x48
+#define ZOOMS               0x49
+#define COM22               0x4B
+#define COM25               0x4E
+#define BD50                0x4F
+#define BD60                0x50
+#define REG5D               0x5D
+#define REG5E               0x5E
+#define REG5F               0x5F
+#define REG60               0x60
+#define HISTO_LOW           0x61
+#define HISTO_HIGH          0x62
+
+#define REG04_DEFAULT       0x28
+#define REG04_HFLIP_IMG     0x80
+#define REG04_VFLIP_IMG     0x40
+#define REG04_VREF_EN       0x10
+#define REG04_HREF_EN       0x08
+#define REG04_SET(x)        (REG04_DEFAULT|x)
+
+#define COM2_STDBY          0x10
+#define COM2_OUT_DRIVE_1x   0x00
+#define COM2_OUT_DRIVE_2x   0x01
+#define COM2_OUT_DRIVE_3x   0x02
+#define COM2_OUT_DRIVE_4x   0x03
+
+#define COM3_DEFAULT        0x38
+#define COM3_BAND_50Hz      0x04
+#define COM3_BAND_60Hz      0x00
+#define COM3_BAND_AUTO      0x02
+#define COM3_BAND_SET(x)    (COM3_DEFAULT|x)
+
+#define COM7_SRST           0x80
+#define COM7_RES_UXGA       0x00 /* UXGA */
+#define COM7_RES_SVGA       0x40 /* SVGA */
+#define COM7_RES_CIF        0x20 /* CIF  */
+#define COM7_ZOOM_EN        0x04 /* Enable Zoom */
+#define COM7_COLOR_BAR      0x02 /* Enable Color Bar Test */
+
+#define COM8_DEFAULT        0xC0
+#define COM8_BNDF_EN        0x20 /* Enable Banding filter */
+#define COM8_AGC_EN         0x04 /* AGC Auto/Manual control selection */
+#define COM8_AEC_EN         0x01 /* Auto/Manual Exposure control */
+#define COM8_SET(x)         (COM8_DEFAULT|x)
+
+#define COM9_DEFAULT        0x08
+#define COM9_AGC_GAIN_2x    0x00 /* AGC:    2x */
+#define COM9_AGC_GAIN_4x    0x01 /* AGC:    4x */
+#define COM9_AGC_GAIN_8x    0x02 /* AGC:    8x */
+#define COM9_AGC_GAIN_16x   0x03 /* AGC:   16x */
+#define COM9_AGC_GAIN_32x   0x04 /* AGC:   32x */
+#define COM9_AGC_GAIN_64x   0x05 /* AGC:   64x */
+#define COM9_AGC_GAIN_128x  0x06 /* AGC:  128x */
+#define COM9_AGC_SET(x)     (COM9_DEFAULT|(x<<5))
+
+#define COM10_HREF_EN       0x80 /* HSYNC changes to HREF */
+#define COM10_HSYNC_EN      0x40 /* HREF changes to HSYNC */
+#define COM10_PCLK_FREE     0x20 /* PCLK output option: free running PCLK */
+#define COM10_PCLK_EDGE     0x10 /* Data is updated at the rising edge of PCLK */
+#define COM10_HREF_NEG      0x08 /* HREF negative */
+#define COM10_VSYNC_NEG     0x02 /* VSYNC negative */
+#define COM10_HSYNC_NEG     0x01 /* HSYNC negative */
+
+#define CTRL1_AWB           0x08 /* Enable AWB */
+
+#define VV_AGC_TH_SET(h,l)  ((h<<4)|(l&0x0F))
+
+#define REG32_UXGA          0x36
+#define REG32_SVGA          0x09
+#define REG32_CIF           0x89
+
+#define CLKRC_2X            0x80
+#define CLKRC_2X_UXGA       (0x01 | CLKRC_2X)
+#define CLKRC_2X_SVGA       CLKRC_2X
+#define CLKRC_2X_CIF        CLKRC_2X
+
+#endif //__REG_REGS_H__

+ 485 - 0
std/camera/sensors/private_include/ov2640_settings.h

@@ -0,0 +1,485 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef _OV2640_SETTINGS_H_
+#define _OV2640_SETTINGS_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp_attr.h"
+#include "ov2640_regs.h"
+
+typedef enum {
+    OV2640_MODE_UXGA, OV2640_MODE_SVGA, OV2640_MODE_CIF, OV2640_MODE_MAX
+} ov2640_sensor_mode_t;
+
+typedef struct {
+        union {
+                struct {
+                        uint8_t pclk_div:7;
+                        uint8_t pclk_auto:1;
+                };
+                uint8_t pclk;
+        };
+        union {
+                struct {
+                        uint8_t clk_div:6;
+                        uint8_t reserved:1;
+                        uint8_t clk_2x:1;
+                };
+                uint8_t clk;
+        };
+} ov2640_clk_t;
+
+typedef struct {
+        uint16_t offset_x;
+        uint16_t offset_y;
+        uint16_t max_x;
+        uint16_t max_y;
+} ov2640_ratio_settings_t;
+
+static const DRAM_ATTR ov2640_ratio_settings_t ratio_table[] = {
+    // ox,  oy,   mx,   my
+    {   0,   0, 1600, 1200 }, //4x3
+    {   8,  72, 1584, 1056 }, //3x2
+    {   0, 100, 1600, 1000 }, //16x10
+    {   0, 120, 1600,  960 }, //5x3
+    {   0, 150, 1600,  900 }, //16x9
+    {   2, 258, 1596,  684 }, //21x9
+    {  50,   0, 1500, 1200 }, //5x4
+    { 200,   0, 1200, 1200 }, //1x1
+    { 462,   0,  676, 1200 }  //9x16
+};
+
+// 30fps@24MHz
+const DRAM_ATTR uint8_t ov2640_settings_cif[][2] = {
+    {BANK_SEL, BANK_DSP},
+    {0x2c, 0xff},
+    {0x2e, 0xdf},
+    {BANK_SEL, BANK_SENSOR},
+    {0x3c, 0x32},
+    {CLKRC, 0x01},
+    {COM2, COM2_OUT_DRIVE_3x},
+    {REG04, REG04_DEFAULT},
+    {COM8, COM8_DEFAULT | COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN},
+    {COM9, COM9_AGC_SET(COM9_AGC_GAIN_8x)},
+    {0x2c, 0x0c},
+    {0x33, 0x78},
+    {0x3a, 0x33},
+    {0x3b, 0xfB},
+    {0x3e, 0x00},
+    {0x43, 0x11},
+    {0x16, 0x10},
+    {0x39, 0x92},
+    {0x35, 0xda},
+    {0x22, 0x1a},
+    {0x37, 0xc3},
+    {0x23, 0x00},
+    {ARCOM2, 0xc0},
+    {0x06, 0x88},
+    {0x07, 0xc0},
+    {COM4, 0x87},
+    {0x0e, 0x41},
+    {0x4c, 0x00},
+    {0x4a, 0x81},
+    {0x21, 0x99},
+    {AEW, 0x40},
+    {AEB, 0x38},
+    {VV, VV_AGC_TH_SET(8,2)},
+    {0x5c, 0x00},
+    {0x63, 0x00},
+    {HISTO_LOW, 0x70},
+    {HISTO_HIGH, 0x80},
+    {0x7c, 0x05},
+    {0x20, 0x80},
+    {0x28, 0x30},
+    {0x6c, 0x00},
+    {0x6d, 0x80},
+    {0x6e, 0x00},
+    {0x70, 0x02},
+    {0x71, 0x94},
+    {0x73, 0xc1},
+    {0x3d, 0x34},
+    {0x5a, 0x57},
+    {BD50, 0xbb},
+    {BD60, 0x9c},
+    {COM7, COM7_RES_CIF},
+    {HSTART, 0x11},
+    {HSTOP, 0x43},
+    {VSTART, 0x00},
+    {VSTOP, 0x25},
+    {REG32, 0x89},
+    {0x37, 0xc0},
+    {BD50, 0xca},
+    {BD60, 0xa8},
+    {0x6d, 0x00},
+    {0x3d, 0x38},
+    {BANK_SEL, BANK_DSP},
+    {0xe5, 0x7f},
+    {MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL},
+    {0x41, 0x24},
+    {RESET, RESET_JPEG | RESET_DVP},
+    {0x76, 0xff},
+    {0x33, 0xa0},
+    {0x42, 0x20},
+    {0x43, 0x18},
+    {0x4c, 0x00},
+    {CTRL3, CTRL3_WPC_EN | 0x10 },
+    {0x88, 0x3f},
+    {0xd7, 0x03},
+    {0xd9, 0x10},
+    {R_DVP_SP, R_DVP_SP_AUTO_MODE | 0x02},
+    {0xc8, 0x08},
+    {0xc9, 0x80},
+    {BPADDR, 0x00},
+    {BPDATA, 0x00},
+    {BPADDR, 0x03},
+    {BPDATA, 0x48},
+    {BPDATA, 0x48},
+    {BPADDR, 0x08},
+    {BPDATA, 0x20},
+    {BPDATA, 0x10},
+    {BPDATA, 0x0e},
+    {0x90, 0x00},
+    {0x91, 0x0e},
+    {0x91, 0x1a},
+    {0x91, 0x31},
+    {0x91, 0x5a},
+    {0x91, 0x69},
+    {0x91, 0x75},
+    {0x91, 0x7e},
+    {0x91, 0x88},
+    {0x91, 0x8f},
+    {0x91, 0x96},
+    {0x91, 0xa3},
+    {0x91, 0xaf},
+    {0x91, 0xc4},
+    {0x91, 0xd7},
+    {0x91, 0xe8},
+    {0x91, 0x20},
+    {0x92, 0x00},
+    {0x93, 0x06},
+    {0x93, 0xe3},
+    {0x93, 0x05},
+    {0x93, 0x05},
+    {0x93, 0x00},
+    {0x93, 0x04},
+    {0x93, 0x00},
+    {0x93, 0x00},
+    {0x93, 0x00},
+    {0x93, 0x00},
+    {0x93, 0x00},
+    {0x93, 0x00},
+    {0x93, 0x00},
+    {0x96, 0x00},
+    {0x97, 0x08},
+    {0x97, 0x19},
+    {0x97, 0x02},
+    {0x97, 0x0c},
+    {0x97, 0x24},
+    {0x97, 0x30},
+    {0x97, 0x28},
+    {0x97, 0x26},
+    {0x97, 0x02},
+    {0x97, 0x98},
+    {0x97, 0x80},
+    {0x97, 0x00},
+    {0x97, 0x00},
+    {0xa4, 0x00},
+    {0xa8, 0x00},
+    {0xc5, 0x11},
+    {0xc6, 0x51},
+    {0xbf, 0x80},
+    {0xc7, 0x10},
+    {0xb6, 0x66},
+    {0xb8, 0xA5},
+    {0xb7, 0x64},
+    {0xb9, 0x7C},
+    {0xb3, 0xaf},
+    {0xb4, 0x97},
+    {0xb5, 0xFF},
+    {0xb0, 0xC5},
+    {0xb1, 0x94},
+    {0xb2, 0x0f},
+    {0xc4, 0x5c},
+    {CTRL1, 0xfd},
+    {0x7f, 0x00},
+    {0xe5, 0x1f},
+    {0xe1, 0x67},
+    {0xdd, 0x7f},
+    {IMAGE_MODE, 0x00},
+    {RESET, 0x00},
+    {R_BYPASS, R_BYPASS_DSP_EN},
+    {0, 0}
+};
+
+const DRAM_ATTR uint8_t ov2640_settings_to_cif[][2] = {
+    {BANK_SEL, BANK_SENSOR},
+    {COM7, COM7_RES_CIF},
+
+    //Set the sensor output window
+    {COM1, 0x0A},
+    {REG32, REG32_CIF},
+    {HSTART, 0x11},
+    {HSTOP, 0x43},
+    {VSTART, 0x00},
+    {VSTOP, 0x25},
+
+    //{CLKRC, 0x00},
+    {BD50, 0xca},
+    {BD60, 0xa8},
+    {0x5a, 0x23},
+    {0x6d, 0x00},
+    {0x3d, 0x38},
+    {0x39, 0x92},
+    {0x35, 0xda},
+    {0x22, 0x1a},
+    {0x37, 0xc3},
+    {0x23, 0x00},
+    {ARCOM2, 0xc0},
+    {0x06, 0x88},
+    {0x07, 0xc0},
+    {COM4, 0x87},
+    {0x0e, 0x41},
+    {0x4c, 0x00},
+    {BANK_SEL, BANK_DSP},
+    {RESET, RESET_DVP},
+
+    //Set the sensor resolution (UXGA, SVGA, CIF)
+    {HSIZE8, 0x32},
+    {VSIZE8, 0x25},
+    {SIZEL, 0x00},
+
+    //Set the image window size >= output size
+    {HSIZE, 0x64},
+    {VSIZE, 0x4a},
+    {XOFFL, 0x00},
+    {YOFFL, 0x00},
+    {VHYX, 0x00},
+    {TEST, 0x00},
+
+    {CTRL2, CTRL2_DCW_EN | 0x1D},
+    {CTRLI, CTRLI_LP_DP | 0x00},
+    //{R_DVP_SP, 0x08},
+    {0, 0}
+};
+
+const DRAM_ATTR uint8_t ov2640_settings_to_svga[][2] = {
+    {BANK_SEL, BANK_SENSOR},
+    {COM7, COM7_RES_SVGA},
+
+    //Set the sensor output window
+    {COM1, 0x0A},
+    {REG32, REG32_SVGA},
+    {HSTART, 0x11},
+    {HSTOP, 0x43},
+    {VSTART, 0x00},
+    {VSTOP, 0x4b},
+
+    //{CLKRC, 0x00},
+    {0x37, 0xc0},
+    {BD50, 0xca},
+    {BD60, 0xa8},
+    {0x5a, 0x23},
+    {0x6d, 0x00},
+    {0x3d, 0x38},
+    {0x39, 0x92},
+    {0x35, 0xda},
+    {0x22, 0x1a},
+    {0x37, 0xc3},
+    {0x23, 0x00},
+    {ARCOM2, 0xc0},
+    {0x06, 0x88},
+    {0x07, 0xc0},
+    {COM4, 0x87},
+    {0x0e, 0x41},
+    {0x42, 0x03},
+    {0x4c, 0x00},
+    {BANK_SEL, BANK_DSP},
+    {RESET, RESET_DVP},
+
+    //Set the sensor resolution (UXGA, SVGA, CIF)
+    {HSIZE8, 0x64},
+    {VSIZE8, 0x4B},
+    {SIZEL, 0x00},
+
+    //Set the image window size >= output size
+    {HSIZE, 0xC8},
+    {VSIZE, 0x96},
+    {XOFFL, 0x00},
+    {YOFFL, 0x00},
+    {VHYX, 0x00},
+    {TEST, 0x00},
+
+    {CTRL2, CTRL2_DCW_EN | 0x1D},
+    {CTRLI, CTRLI_LP_DP | 0x00},
+    //{R_DVP_SP, 0x08},
+    {0, 0}
+};
+
+const DRAM_ATTR uint8_t ov2640_settings_to_uxga[][2] = {
+    {BANK_SEL, BANK_SENSOR},
+    {COM7, COM7_RES_UXGA},
+
+    //Set the sensor output window
+    {COM1, 0x0F},
+    {REG32, REG32_UXGA},
+    {HSTART, 0x11},
+    {HSTOP, 0x75},
+    {VSTART, 0x01},
+    {VSTOP, 0x97},
+
+    //{CLKRC, 0x00},
+    {0x3d, 0x34},
+    {BD50, 0xbb},
+    {BD60, 0x9c},
+    {0x5a, 0x57},
+    {0x6d, 0x80},
+    {0x39, 0x82},
+    {0x23, 0x00},
+    {0x07, 0xc0},
+    {0x4c, 0x00},
+    {0x35, 0x88},
+    {0x22, 0x0a},
+    {0x37, 0x40},
+    {ARCOM2, 0xa0},
+    {0x06, 0x02},
+    {COM4, 0xb7},
+    {0x0e, 0x01},
+    {0x42, 0x83},
+    {BANK_SEL, BANK_DSP},
+    {RESET, RESET_DVP},
+
+    //Set the sensor resolution (UXGA, SVGA, CIF)
+    {HSIZE8, 0xc8},
+    {VSIZE8, 0x96},
+    {SIZEL, 0x00},
+
+    //Set the image window size >= output size
+    {HSIZE, 0x90},
+    {VSIZE, 0x2c},
+    {XOFFL, 0x00},
+    {YOFFL, 0x00},
+    {VHYX, 0x88},
+    {TEST, 0x00},
+
+    {CTRL2, CTRL2_DCW_EN | 0x1d},
+    {CTRLI, 0x00},
+    //{R_DVP_SP, 0x06},
+    {0, 0}
+};
+
+const DRAM_ATTR uint8_t ov2640_settings_jpeg3[][2] = {
+    {BANK_SEL, BANK_DSP},
+    {RESET, RESET_JPEG | RESET_DVP},
+    {IMAGE_MODE, IMAGE_MODE_JPEG_EN | IMAGE_MODE_HREF_VSYNC},
+    {0xD7, 0x03},
+    {0xE1, 0x77},
+    {0xE5, 0x1F},
+    {0xD9, 0x10},
+    {0xDF, 0x80},
+    {0x33, 0x80},
+    {0x3C, 0x10},
+    {0xEB, 0x30},
+    {0xDD, 0x7F},
+    {RESET, 0x00},
+    {0, 0}
+};
+
+static const uint8_t ov2640_settings_yuv422[][2] = {
+    {BANK_SEL, BANK_DSP},
+    {RESET, RESET_DVP},
+    {IMAGE_MODE, IMAGE_MODE_YUV422},
+    {0xD7, 0x01},
+    {0xE1, 0x67},
+    {RESET, 0x00},
+    {0, 0},
+};
+
+static const uint8_t ov2640_settings_rgb565[][2] = {
+    {BANK_SEL, BANK_DSP},
+    {RESET, RESET_DVP},
+    {IMAGE_MODE, IMAGE_MODE_RGB565},
+    {0xD7, 0x03},
+    {0xE1, 0x77},
+    {RESET, 0x00},
+    {0, 0},
+};
+
+#define NUM_BRIGHTNESS_LEVELS (5)
+static const uint8_t brightness_regs[NUM_BRIGHTNESS_LEVELS + 1][5] = {
+    {BPADDR, BPDATA, BPADDR, BPDATA, BPDATA },
+    {0x00, 0x04, 0x09, 0x00, 0x00 }, /* -2 */
+    {0x00, 0x04, 0x09, 0x10, 0x00 }, /* -1 */
+    {0x00, 0x04, 0x09, 0x20, 0x00 }, /*  0 */
+    {0x00, 0x04, 0x09, 0x30, 0x00 }, /* +1 */
+    {0x00, 0x04, 0x09, 0x40, 0x00 }, /* +2 */
+};
+
+#define NUM_CONTRAST_LEVELS (5)
+static const uint8_t contrast_regs[NUM_CONTRAST_LEVELS + 1][7] = {
+    {BPADDR, BPDATA, BPADDR, BPDATA, BPDATA, BPDATA, BPDATA },
+    {0x00, 0x04, 0x07, 0x20, 0x18, 0x34, 0x06 }, /* -2 */
+    {0x00, 0x04, 0x07, 0x20, 0x1c, 0x2a, 0x06 }, /* -1 */
+    {0x00, 0x04, 0x07, 0x20, 0x20, 0x20, 0x06 }, /*  0 */
+    {0x00, 0x04, 0x07, 0x20, 0x24, 0x16, 0x06 }, /* +1 */
+    {0x00, 0x04, 0x07, 0x20, 0x28, 0x0c, 0x06 }, /* +2 */
+};
+
+#define NUM_SATURATION_LEVELS (5)
+static const uint8_t saturation_regs[NUM_SATURATION_LEVELS + 1][5] = {
+    {BPADDR, BPDATA, BPADDR, BPDATA, BPDATA },
+    {0x00, 0x02, 0x03, 0x28, 0x28 }, /* -2 */
+    {0x00, 0x02, 0x03, 0x38, 0x38 }, /* -1 */
+    {0x00, 0x02, 0x03, 0x48, 0x48 }, /*  0 */
+    {0x00, 0x02, 0x03, 0x58, 0x58 }, /* +1 */
+    {0x00, 0x02, 0x03, 0x68, 0x68 }, /* +2 */
+};
+
+#define NUM_SPECIAL_EFFECTS (7)
+static const uint8_t special_effects_regs[NUM_SPECIAL_EFFECTS + 1][5] = {
+    {BPADDR, BPDATA, BPADDR, BPDATA, BPDATA },
+    {0x00, 0X00, 0x05, 0X80, 0X80 }, /* no effect */
+    {0x00, 0X40, 0x05, 0X80, 0X80 }, /* negative */
+    {0x00, 0X18, 0x05, 0X80, 0X80 }, /* black and white */
+    {0x00, 0X18, 0x05, 0X40, 0XC0 }, /* reddish */
+    {0x00, 0X18, 0x05, 0X40, 0X40 }, /* greenish */
+    {0x00, 0X18, 0x05, 0XA0, 0X40 }, /* blue */
+    {0x00, 0X18, 0x05, 0X40, 0XA6 }, /* retro */
+};
+
+#define NUM_WB_MODES (4)
+static const uint8_t wb_modes_regs[NUM_WB_MODES + 1][3] = {
+    {0XCC, 0XCD, 0XCE },
+    {0x5E, 0X41, 0x54 }, /* sunny */
+    {0x65, 0X41, 0x4F }, /* cloudy */
+    {0x52, 0X41, 0x66 }, /* office */
+    {0x42, 0X3F, 0x71 }, /* home */
+};
+
+#define NUM_AE_LEVELS (5)
+static const uint8_t ae_levels_regs[NUM_AE_LEVELS + 1][3] = {
+    { AEW,  AEB,  VV  },
+    {0x20, 0X18, 0x60 },
+    {0x34, 0X1C, 0x00 },
+    {0x3E, 0X38, 0x81 },
+    {0x48, 0X40, 0x81 },
+    {0x58, 0X50, 0x92 },
+};
+
+const uint8_t agc_gain_tbl[31] = {
+    0x00, 0x10, 0x18, 0x30, 0x34, 0x38, 0x3C, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E, 0xF0,
+    0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
+};
+
+#endif /* _OV2640_SETTINGS_H_ */

+ 34 - 0
std/camera/sensors/private_include/ov3660.h

@@ -0,0 +1,34 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV3660 driver.
+ *
+ */
+#ifndef __OV3660_H__
+#define __OV3660_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 ov3660_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int ov3660_init(sensor_t *sensor);
+
+#endif // __OV3660_H__

+ 211 - 0
std/camera/sensors/private_include/ov3660_regs.h

@@ -0,0 +1,211 @@
+/*
+ * OV3660 register definitions.
+ */
+#ifndef __OV3660_REG_REGS_H__
+#define __OV3660_REG_REGS_H__
+
+/* system control registers */
+#define SYSTEM_CTROL0   0x3008  // Bit[7]: Software reset 
+                                // Bit[6]: Software power down 
+                                // Bit[5]: Reserved 
+                                // Bit[4]: SRB clock SYNC enable 
+                                // Bit[3]: Isolation suspend select 
+                                // Bit[2:0]: Not used
+
+/* output format control registers */
+#define FORMAT_CTRL     0x501F // Format select
+                                // Bit[2:0]:
+                                //  000: YUV422
+                                //  001: RGB
+                                //  010: Dither
+                                //  011: RAW after DPC
+                                //  101: RAW after CIP
+
+/* format control registers */
+#define FORMAT_CTRL00   0x4300
+
+/* frame control registers */
+#define FRAME_CTRL01    0x4201  // Control Passed Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
+                                // Bit[7:4]: Not used
+                                // Bit[3:0]: Frame ON number
+#define FRAME_CTRL02    0x4202  // Control Masked Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
+                                // Bit[7:4]: Not used
+                                // BIT[3:0]: Frame OFF number
+
+/* ISP top control registers */
+#define PRE_ISP_TEST_SETTING_1  0x503D  // Bit[7]: Test enable
+                                        //         0: Test disable
+                                        //         1: Color bar enable
+                                        // Bit[6]: Rolling
+                                        // Bit[5]: Transparent
+                                        // Bit[4]: Square black and white
+                                        // Bit[3:2]: Color bar style
+                                        //         00: Standard 8 color bar
+                                        //         01: Gradual change at vertical mode 1
+                                        //         10: Gradual change at horizontal
+                                        //         11: Gradual change at vertical mode 2
+                                        // Bit[1:0]: Test select
+                                        //         00: Color bar
+                                        //         01: Random data
+                                        //         10: Square data
+                                        //         11: Black image
+
+//exposure = {0x3500[3:0], 0x3501[7:0], 0x3502[7:0]} / 16 × tROW
+
+/* AEC/AGC control functions */
+#define AEC_PK_MANUAL   0x3503  // AEC Manual Mode Control
+                                // Bit[7:6]: Reserved
+                                // Bit[5]: Gain delay option
+                                //         Valid when 0x3503[4]=1’b0
+                                //         0: Delay one frame latch
+                                //         1: One frame latch
+                                // Bit[4:2]: Reserved
+                                // Bit[1]: AGC manual
+                                //         0: Auto enable
+                                //         1: Manual enable
+                                // Bit[0]: AEC manual
+                                //         0: Auto enable
+                                //         1: Manual enable
+
+//gain = {0x350A[1:0], 0x350B[7:0]} / 16
+
+/* mirror and flip registers */
+#define TIMING_TC_REG20 0x3820  // Timing Control Register
+                                // Bit[2:1]: Vertical flip enable
+                                //         00: Normal
+                                //         11: Vertical flip
+                                // Bit[0]: Vertical binning enable
+#define TIMING_TC_REG21 0x3821  // Timing Control Register
+                                // Bit[5]: Compression Enable
+                                // Bit[2:1]: Horizontal mirror enable
+                                //         00: Normal
+                                //         11: Horizontal mirror
+                                // Bit[0]: Horizontal binning enable
+
+#define CLOCK_POL_CONTROL 0x4740// Bit[5]: PCLK polarity 0: active low
+                                //          1: active high
+                                // Bit[3]: Gate PCLK under VSYNC
+                                // Bit[2]: Gate PCLK under HREF
+                                // Bit[1]: HREF polarity
+                                //          0: active low
+                                //          1: active high
+                                // Bit[0] VSYNC polarity
+                                //          0: active low
+                                //          1: active high
+#define DRIVE_CAPABILITY 0x302c // Bit[7:6]:
+                                //          00: 1x
+                                //          01: 2x
+                                //          10: 3x
+                                //          11: 4x
+
+
+#define X_ADDR_ST_H     0x3800 //Bit[3:0]: X address start[11:8]
+#define X_ADDR_ST_L     0x3801 //Bit[7:0]: X address start[7:0]
+#define Y_ADDR_ST_H     0x3802 //Bit[2:0]: Y address start[10:8]
+#define Y_ADDR_ST_L     0x3803 //Bit[7:0]: Y address start[7:0]
+#define X_ADDR_END_H    0x3804 //Bit[3:0]: X address end[11:8]
+#define X_ADDR_END_L    0x3805 //Bit[7:0]:
+#define Y_ADDR_END_H    0x3806 //Bit[2:0]: Y address end[10:8]
+#define Y_ADDR_END_L    0x3807 //Bit[7:0]:
+// Size after scaling
+#define X_OUTPUT_SIZE_H 0x3808 //Bit[3:0]: DVP output horizontal width[11:8]
+#define X_OUTPUT_SIZE_L 0x3809 //Bit[7:0]:
+#define Y_OUTPUT_SIZE_H 0x380a //Bit[2:0]: DVP output vertical height[10:8]
+#define Y_OUTPUT_SIZE_L 0x380b //Bit[7:0]:
+#define X_TOTAL_SIZE_H  0x380c //Bit[3:0]: Total horizontal size[11:8]
+#define X_TOTAL_SIZE_L  0x380d //Bit[7:0]:
+#define Y_TOTAL_SIZE_H  0x380e //Bit[7:0]: Total vertical size[15:8]
+#define Y_TOTAL_SIZE_L  0x380f //Bit[7:0]:
+#define X_OFFSET_H      0x3810 //Bit[3:0]: ISP horizontal offset[11:8]
+#define X_OFFSET_L      0x3811 //Bit[7:0]:
+#define Y_OFFSET_H      0x3812 //Bit[2:0]: ISP vertical offset[10:8]
+#define Y_OFFSET_L      0x3813 //Bit[7:0]:
+#define X_INCREMENT     0x3814 //Bit[7:4]: Horizontal odd subsample increment
+                               //Bit[3:0]: Horizontal even subsample increment
+#define Y_INCREMENT     0x3815 //Bit[7:4]: Vertical odd subsample increment
+                               //Bit[3:0]: Vertical even subsample increment
+// Size before scaling
+//#define X_INPUT_SIZE    (X_ADDR_END - X_ADDR_ST + 1 - (2 * X_OFFSET))
+//#define Y_INPUT_SIZE    (Y_ADDR_END - Y_ADDR_ST + 1 - (2 * Y_OFFSET))
+
+#define ISP_CONTROL_01   0x5001 // Bit[5]: Scale enable
+                                //          0: Disable
+                                //          1: Enable
+
+#define SCALE_CTRL_1     0x5601 // Bit[6:4]: HDIV RW
+                                //          DCW scale times
+                                //          000: DCW 1 time
+                                //          001: DCW 2 times
+                                //          010: DCW 4 times
+                                //          100: DCW 8 times
+                                //          101: DCW 16 times
+                                //          Others: DCW 16 times
+                                // Bit[2:0]: VDIV RW
+                                //          DCW scale times
+                                //          000: DCW 1 time
+                                //          001: DCW 2 times
+                                //          010: DCW 4 times
+                                //          100: DCW 8 times
+                                //          101: DCW 16 times
+                                //          Others: DCW 16 times
+
+#define SCALE_CTRL_2     0x5602 // X_SCALE High Bits
+#define SCALE_CTRL_3     0x5603 // X_SCALE Low Bits
+#define SCALE_CTRL_4     0x5604 // Y_SCALE High Bits
+#define SCALE_CTRL_5     0x5605 // Y_SCALE Low Bits
+#define SCALE_CTRL_6     0x5606 // Bit[3:0]: V Offset
+
+#define PCLK_RATIO       0x3824 // Bit[4:0]: PCLK ratio manual
+#define VFIFO_CTRL0C     0x460C // Bit[1]: PCLK manual enable
+                                //          0: Auto
+                                //          1: Manual by PCLK_RATIO
+
+#define VFIFO_X_SIZE_H   0x4602
+#define VFIFO_X_SIZE_L   0x4603
+#define VFIFO_Y_SIZE_H   0x4604
+#define VFIFO_Y_SIZE_L   0x4605
+
+#define SC_PLLS_CTRL0    0x303a // Bit[7]: PLLS bypass
+#define SC_PLLS_CTRL1    0x303b // Bit[4:0]: PLLS multiplier
+#define SC_PLLS_CTRL2    0x303c // Bit[6:4]: PLLS charge pump control
+                                // Bit[3:0]: PLLS system divider
+#define SC_PLLS_CTRL3    0x303d // Bit[5:4]: PLLS pre-divider
+                                //          00: 1
+                                //          01: 1.5
+                                //          10: 2
+                                //          11: 3
+                                // Bit[2]: PLLS root-divider - 1
+                                // Bit[1:0]: PLLS seld5
+                                //          00: 1
+                                //          01: 1
+                                //          10: 2
+                                //          11: 2.5
+
+#define COMPRESSION_CTRL00 0x4400 //
+#define COMPRESSION_CTRL01 0x4401 //
+#define COMPRESSION_CTRL02 0x4402 //
+#define COMPRESSION_CTRL03 0x4403 //
+#define COMPRESSION_CTRL04 0x4404 //
+#define COMPRESSION_CTRL05 0x4405 //
+#define COMPRESSION_CTRL06 0x4406 //
+#define COMPRESSION_CTRL07 0x4407 // Bit[5:0]: QS
+#define COMPRESSION_ISI_CTRL 0x4408 //
+#define COMPRESSION_CTRL09 0x4409 //
+#define COMPRESSION_CTRL0a 0x440a //
+#define COMPRESSION_CTRL0b 0x440b //
+#define COMPRESSION_CTRL0c 0x440c //
+#define COMPRESSION_CTRL0d 0x440d //
+#define COMPRESSION_CTRL0E 0x440e //
+
+/**
+ * @brief register value
+ */
+#define TEST_COLOR_BAR  0xC0    /* Enable Color Bar roling Test */
+
+#define AEC_PK_MANUAL_AGC_MANUALEN  0x02    /* Enable AGC Manual enable */
+#define AEC_PK_MANUAL_AEC_MANUALEN  0x01    /* Enable AEC Manual enable */
+
+#define TIMING_TC_REG20_VFLIP   0x06 /* Vertical flip enable */
+#define TIMING_TC_REG21_HMIRROR 0x06 /* Horizontal mirror enable */
+
+#endif // __OV3660_REG_REGS_H__

+ 318 - 0
std/camera/sensors/private_include/ov3660_settings.h

@@ -0,0 +1,318 @@
+#ifndef _OV3660_SETTINGS_H_
+#define _OV3660_SETTINGS_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp_attr.h"
+#include "ov3660_regs.h"
+
+static const ratio_settings_t ratio_table[] = {
+    //  mw,   mh,  sx,  sy,   ex,   ey, ox, oy,   tx,   ty
+    { 2048, 1536,   0,   0, 2079, 1547, 16, 6, 2300, 1564 }, //4x3
+    { 1920, 1280,  64, 128, 2015, 1419, 16, 6, 2172, 1436 }, //3x2
+    { 2048, 1280,   0, 128, 2079, 1419, 16, 6, 2300, 1436 }, //16x10
+    { 1920, 1152,  64, 192, 2015, 1355, 16, 6, 2172, 1372 }, //5x3
+    { 1920, 1080,  64, 242, 2015, 1333, 16, 6, 2172, 1322 }, //16x9
+    { 2048,  880,   0, 328, 2079, 1219, 16, 6, 2300, 1236 }, //21x9
+    { 1920, 1536,  64,   0, 2015, 1547, 16, 6, 2172, 1564 }, //5x4
+    { 1536, 1536, 256,   0, 1823, 1547, 16, 6, 2044, 1564 }, //1x1
+    {  864, 1536, 592,   0, 1487, 1547, 16, 6, 2044, 1564 }  //9x16
+};
+
+#define REG_DLY 0xffff
+#define REGLIST_TAIL 0x0000
+
+static const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
+    {SYSTEM_CTROL0, 0x82},  // software reset
+    {REG_DLY, 10}, // delay 10ms
+
+    {0x3103, 0x13},
+    {SYSTEM_CTROL0, 0x42},
+    {0x3017, 0xff},
+    {0x3018, 0xff},
+    {DRIVE_CAPABILITY, 0xc3},
+    {CLOCK_POL_CONTROL, 0x21},
+
+    {0x3611, 0x01},
+    {0x3612, 0x2d},
+
+    {0x3032, 0x00},
+    {0x3614, 0x80},
+    {0x3618, 0x00},
+    {0x3619, 0x75},
+    {0x3622, 0x80},
+    {0x3623, 0x00},
+    {0x3624, 0x03},
+    {0x3630, 0x52},
+    {0x3632, 0x07},
+    {0x3633, 0xd2},
+    {0x3704, 0x80},
+    {0x3708, 0x66},
+    {0x3709, 0x12},
+    {0x370b, 0x12},
+    {0x3717, 0x00},
+    {0x371b, 0x60},
+    {0x371c, 0x00},
+    {0x3901, 0x13},
+
+    {0x3600, 0x08},
+    {0x3620, 0x43},
+    {0x3702, 0x20},
+    {0x3739, 0x48},
+    {0x3730, 0x20},
+    {0x370c, 0x0c},
+
+    {0x3a18, 0x00},
+    {0x3a19, 0xf8},
+
+    {0x3000, 0x10},
+    {0x3004, 0xef},
+
+    {0x6700, 0x05},
+    {0x6701, 0x19},
+    {0x6702, 0xfd},
+    {0x6703, 0xd1},
+    {0x6704, 0xff},
+    {0x6705, 0xff},
+
+    {0x3c01, 0x80},
+    {0x3c00, 0x04},
+    {0x3a08, 0x00}, {0x3a09, 0x62}, //50Hz Band Width Step (10bit)
+    {0x3a0e, 0x08}, //50Hz Max Bands in One Frame (6 bit)
+    {0x3a0a, 0x00}, {0x3a0b, 0x52}, //60Hz Band Width Step (10bit)
+    {0x3a0d, 0x09}, //60Hz Max Bands in One Frame (6 bit)
+
+    {0x3a00, 0x3a},//night mode off
+    {0x3a14, 0x09},
+    {0x3a15, 0x30},
+    {0x3a02, 0x09},
+    {0x3a03, 0x30},
+
+    {COMPRESSION_CTRL0E, 0x08},
+    {0x4520, 0x0b},
+    {0x460b, 0x37},
+    {0x4713, 0x02},
+    {0x471c, 0xd0},
+    {0x5086, 0x00},
+
+    {0x5002, 0x00},
+    {0x501f, 0x00},
+
+    {SYSTEM_CTROL0, 0x02},
+
+    {0x5180, 0xff},
+    {0x5181, 0xf2},
+    {0x5182, 0x00},
+    {0x5183, 0x14},
+    {0x5184, 0x25},
+    {0x5185, 0x24},
+    {0x5186, 0x16},
+    {0x5187, 0x16},
+    {0x5188, 0x16},
+    {0x5189, 0x68},
+    {0x518a, 0x60},
+    {0x518b, 0xe0},
+    {0x518c, 0xb2},
+    {0x518d, 0x42},
+    {0x518e, 0x35},
+    {0x518f, 0x56},
+    {0x5190, 0x56},
+    {0x5191, 0xf8},
+    {0x5192, 0x04},
+    {0x5193, 0x70},
+    {0x5194, 0xf0},
+    {0x5195, 0xf0},
+    {0x5196, 0x03},
+    {0x5197, 0x01},
+    {0x5198, 0x04},
+    {0x5199, 0x12},
+    {0x519a, 0x04},
+    {0x519b, 0x00},
+    {0x519c, 0x06},
+    {0x519d, 0x82},
+    {0x519e, 0x38},
+
+    {0x5381, 0x1d},
+    {0x5382, 0x60},
+    {0x5383, 0x03},
+    {0x5384, 0x0c},
+    {0x5385, 0x78},
+    {0x5386, 0x84},
+    {0x5387, 0x7d},
+    {0x5388, 0x6b},
+    {0x5389, 0x12},
+    {0x538a, 0x01},
+    {0x538b, 0x98},
+
+    {0x5480, 0x01},
+//    {0x5481, 0x05},
+//    {0x5482, 0x09},
+//    {0x5483, 0x10},
+//    {0x5484, 0x3a},
+//    {0x5485, 0x4c},
+//    {0x5486, 0x5a},
+//    {0x5487, 0x68},
+//    {0x5488, 0x74},
+//    {0x5489, 0x80},
+//    {0x548a, 0x8e},
+//    {0x548b, 0xa4},
+//    {0x548c, 0xb4},
+//    {0x548d, 0xc8},
+//    {0x548e, 0xde},
+//    {0x548f, 0xf0},
+//    {0x5490, 0x15},
+
+    {0x5000, 0xa7},
+    {0x5800, 0x0C},
+    {0x5801, 0x09},
+    {0x5802, 0x0C},
+    {0x5803, 0x0C},
+    {0x5804, 0x0D},
+    {0x5805, 0x17},
+    {0x5806, 0x06},
+    {0x5807, 0x05},
+    {0x5808, 0x04},
+    {0x5809, 0x06},
+    {0x580a, 0x09},
+    {0x580b, 0x0E},
+    {0x580c, 0x05},
+    {0x580d, 0x01},
+    {0x580e, 0x01},
+    {0x580f, 0x01},
+    {0x5810, 0x05},
+    {0x5811, 0x0D},
+    {0x5812, 0x05},
+    {0x5813, 0x01},
+    {0x5814, 0x01},
+    {0x5815, 0x01},
+    {0x5816, 0x05},
+    {0x5817, 0x0D},
+    {0x5818, 0x08},
+    {0x5819, 0x06},
+    {0x581a, 0x05},
+    {0x581b, 0x07},
+    {0x581c, 0x0B},
+    {0x581d, 0x0D},
+    {0x581e, 0x12},
+    {0x581f, 0x0D},
+    {0x5820, 0x0E},
+    {0x5821, 0x10},
+    {0x5822, 0x10},
+    {0x5823, 0x1E},
+    {0x5824, 0x53},
+    {0x5825, 0x15},
+    {0x5826, 0x05},
+    {0x5827, 0x14},
+    {0x5828, 0x54},
+    {0x5829, 0x25},
+    {0x582a, 0x33},
+    {0x582b, 0x33},
+    {0x582c, 0x34},
+    {0x582d, 0x16},
+    {0x582e, 0x24},
+    {0x582f, 0x41},
+    {0x5830, 0x50},
+    {0x5831, 0x42},
+    {0x5832, 0x15},
+    {0x5833, 0x25},
+    {0x5834, 0x34},
+    {0x5835, 0x33},
+    {0x5836, 0x24},
+    {0x5837, 0x26},
+    {0x5838, 0x54},
+    {0x5839, 0x25},
+    {0x583a, 0x15},
+    {0x583b, 0x25},
+    {0x583c, 0x53},
+    {0x583d, 0xCF},
+
+    {0x3a0f, 0x30},
+    {0x3a10, 0x28},
+    {0x3a1b, 0x30},
+    {0x3a1e, 0x28},
+    {0x3a11, 0x60},
+    {0x3a1f, 0x14},
+
+    {0x5302, 0x28},
+    {0x5303, 0x20},
+
+    {0x5306, 0x1c}, //de-noise offset 1
+    {0x5307, 0x28}, //de-noise offset 2
+
+    {0x4002, 0xc5},
+    {0x4003, 0x81},
+    {0x4005, 0x12},
+
+    {0x5688, 0x11},
+    {0x5689, 0x11},
+    {0x568a, 0x11},
+    {0x568b, 0x11},
+    {0x568c, 0x11},
+    {0x568d, 0x11},
+    {0x568e, 0x11},
+    {0x568f, 0x11},
+
+    {0x5580, 0x06},
+    {0x5588, 0x00},
+    {0x5583, 0x40},
+    {0x5584, 0x2c},
+
+    {ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
+    {REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = {
+    {FORMAT_CTRL, 0x00}, // YUV422
+    {FORMAT_CTRL00, 0x30}, // YUYV
+    {0x3002, 0x00},//0x1c to 0x00 !!!
+    {0x3006, 0xff},//0xc3 to 0xff !!!
+    {0x471c, 0x50},//0xd0 to 0x50 !!!
+    {REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_raw[][2] = {
+    {FORMAT_CTRL00, 0x00}, // RAW
+    {REGLIST_TAIL, 0x00}
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_grayscale[][2] = {
+    {FORMAT_CTRL, 0x00}, // YUV422
+    {FORMAT_CTRL00, 0x10}, // Y8
+    {REGLIST_TAIL, 0x00}
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_yuv422[][2] = {
+    {FORMAT_CTRL, 0x00}, // YUV422
+    {FORMAT_CTRL00, 0x30}, // YUYV
+    {REGLIST_TAIL, 0x00}
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_rgb565[][2] = {
+    {FORMAT_CTRL, 0x01}, // RGB
+    {FORMAT_CTRL00, 0x61}, // RGB565 (BGR)
+    {REGLIST_TAIL, 0x00}
+};
+
+static const DRAM_ATTR uint8_t sensor_saturation_levels[9][11] = {
+    {0x1d, 0x60, 0x03, 0x07, 0x48, 0x4f, 0x4b, 0x40, 0x0b, 0x01, 0x98},//-4
+    {0x1d, 0x60, 0x03, 0x08, 0x54, 0x5c, 0x58, 0x4b, 0x0d, 0x01, 0x98},//-3
+    {0x1d, 0x60, 0x03, 0x0a, 0x60, 0x6a, 0x64, 0x56, 0x0e, 0x01, 0x98},//-2
+    {0x1d, 0x60, 0x03, 0x0b, 0x6c, 0x77, 0x70, 0x60, 0x10, 0x01, 0x98},//-1
+    {0x1d, 0x60, 0x03, 0x0c, 0x78, 0x84, 0x7d, 0x6b, 0x12, 0x01, 0x98},//0
+    {0x1d, 0x60, 0x03, 0x0d, 0x84, 0x91, 0x8a, 0x76, 0x14, 0x01, 0x98},//+1
+    {0x1d, 0x60, 0x03, 0x0e, 0x90, 0x9e, 0x96, 0x80, 0x16, 0x01, 0x98},//+2
+    {0x1d, 0x60, 0x03, 0x10, 0x9c, 0xac, 0xa2, 0x8b, 0x17, 0x01, 0x98},//+3
+    {0x1d, 0x60, 0x03, 0x11, 0xa8, 0xb9, 0xaf, 0x96, 0x19, 0x01, 0x98},//+4
+};
+
+static const DRAM_ATTR uint8_t sensor_special_effects[7][4] = {
+    {0x06, 0x40, 0x2c, 0x08},//Normal
+    {0x46, 0x40, 0x28, 0x08},//Negative
+    {0x1e, 0x80, 0x80, 0x08},//Grayscale
+    {0x1e, 0x80, 0xc0, 0x08},//Red Tint
+    {0x1e, 0x60, 0x60, 0x08},//Green Tint
+    {0x1e, 0xa0, 0x40, 0x08},//Blue Tint
+    {0x1e, 0x40, 0xa0, 0x08},//Sepia
+};
+
+#endif

+ 27 - 0
std/camera/sensors/private_include/ov5640.h

@@ -0,0 +1,27 @@
+
+#ifndef __OV5640_H__
+#define __OV5640_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 ov5640_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int ov5640_init(sensor_t *sensor);
+
+#endif // __OV5640_H__

+ 213 - 0
std/camera/sensors/private_include/ov5640_regs.h

@@ -0,0 +1,213 @@
+/*
+ * OV5640 register definitions.
+ */
+#ifndef __OV5640_REG_REGS_H__
+#define __OV5640_REG_REGS_H__
+
+/* system control registers */
+#define SYSTEM_CTROL0   0x3008  // Bit[7]: Software reset 
+                                // Bit[6]: Software power down 
+                                // Bit[5]: Reserved 
+                                // Bit[4]: SRB clock SYNC enable 
+                                // Bit[3]: Isolation suspend select 
+                                // Bit[2:0]: Not used
+
+#define DRIVE_CAPABILITY 0x302c // Bit[7:6]:
+                                //          00: 1x
+                                //          01: 2x
+                                //          10: 3x
+                                //          11: 4x
+
+#define SC_PLLS_CTRL0    0x303a // Bit[7]: PLLS bypass
+#define SC_PLLS_CTRL1    0x303b // Bit[4:0]: PLLS multiplier
+#define SC_PLLS_CTRL2    0x303c // Bit[6:4]: PLLS charge pump control
+                                // Bit[3:0]: PLLS system divider
+#define SC_PLLS_CTRL3    0x303d // Bit[5:4]: PLLS pre-divider
+                                //          00: 1
+                                //          01: 1.5
+                                //          10: 2
+                                //          11: 3
+                                // Bit[2]: PLLS root-divider - 1
+                                // Bit[1:0]: PLLS seld5
+                                //          00: 1
+                                //          01: 1
+                                //          10: 2
+                                //          11: 2.5
+
+/* AEC/AGC control functions */
+#define AEC_PK_MANUAL   0x3503  // AEC Manual Mode Control
+                                // Bit[7:6]: Reserved
+                                // Bit[5]: Gain delay option
+                                //         Valid when 0x3503[4]=1’b0
+                                //         0: Delay one frame latch
+                                //         1: One frame latch
+                                // Bit[4:2]: Reserved
+                                // Bit[1]: AGC manual
+                                //         0: Auto enable
+                                //         1: Manual enable
+                                // Bit[0]: AEC manual
+                                //         0: Auto enable
+                                //         1: Manual enable
+
+//gain = {0x350A[1:0], 0x350B[7:0]} / 16
+
+
+#define X_ADDR_ST_H     0x3800 //Bit[3:0]: X address start[11:8]
+#define X_ADDR_ST_L     0x3801 //Bit[7:0]: X address start[7:0]
+#define Y_ADDR_ST_H     0x3802 //Bit[2:0]: Y address start[10:8]
+#define Y_ADDR_ST_L     0x3803 //Bit[7:0]: Y address start[7:0]
+#define X_ADDR_END_H    0x3804 //Bit[3:0]: X address end[11:8]
+#define X_ADDR_END_L    0x3805 //Bit[7:0]:
+#define Y_ADDR_END_H    0x3806 //Bit[2:0]: Y address end[10:8]
+#define Y_ADDR_END_L    0x3807 //Bit[7:0]:
+// Size after scaling
+#define X_OUTPUT_SIZE_H 0x3808 //Bit[3:0]: DVP output horizontal width[11:8]
+#define X_OUTPUT_SIZE_L 0x3809 //Bit[7:0]:
+#define Y_OUTPUT_SIZE_H 0x380a //Bit[2:0]: DVP output vertical height[10:8]
+#define Y_OUTPUT_SIZE_L 0x380b //Bit[7:0]:
+#define X_TOTAL_SIZE_H  0x380c //Bit[3:0]: Total horizontal size[11:8]
+#define X_TOTAL_SIZE_L  0x380d //Bit[7:0]:
+#define Y_TOTAL_SIZE_H  0x380e //Bit[7:0]: Total vertical size[15:8]
+#define Y_TOTAL_SIZE_L  0x380f //Bit[7:0]:
+#define X_OFFSET_H      0x3810 //Bit[3:0]: ISP horizontal offset[11:8]
+#define X_OFFSET_L      0x3811 //Bit[7:0]:
+#define Y_OFFSET_H      0x3812 //Bit[2:0]: ISP vertical offset[10:8]
+#define Y_OFFSET_L      0x3813 //Bit[7:0]:
+#define X_INCREMENT     0x3814 //Bit[7:4]: Horizontal odd subsample increment
+                               //Bit[3:0]: Horizontal even subsample increment
+#define Y_INCREMENT     0x3815 //Bit[7:4]: Vertical odd subsample increment
+                               //Bit[3:0]: Vertical even subsample increment
+// Size before scaling
+//#define X_INPUT_SIZE    (X_ADDR_END - X_ADDR_ST + 1 - (2 * X_OFFSET))
+//#define Y_INPUT_SIZE    (Y_ADDR_END - Y_ADDR_ST + 1 - (2 * Y_OFFSET))
+
+/* mirror and flip registers */
+#define TIMING_TC_REG20 0x3820  // Timing Control Register
+                                // Bit[2:1]: Vertical flip enable
+                                //         00: Normal
+                                //         11: Vertical flip
+                                // Bit[0]: Vertical binning enable
+#define TIMING_TC_REG21 0x3821  // Timing Control Register
+                                // Bit[5]: Compression Enable
+                                // Bit[2:1]: Horizontal mirror enable
+                                //         00: Normal
+                                //         11: Horizontal mirror
+                                // Bit[0]: Horizontal binning enable
+
+#define PCLK_RATIO       0x3824 // Bit[4:0]: PCLK ratio manual
+
+/* frame control registers */
+#define FRAME_CTRL01    0x4201  // Control Passed Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
+                                // Bit[7:4]: Not used
+                                // Bit[3:0]: Frame ON number
+#define FRAME_CTRL02    0x4202  // Control Masked Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
+                                // Bit[7:4]: Not used
+                                // BIT[3:0]: Frame OFF number
+
+/* format control registers */
+#define FORMAT_CTRL00   0x4300
+
+#define CLOCK_POL_CONTROL 0x4740// Bit[5]: PCLK polarity 0: active low
+                                //          1: active high
+                                // Bit[3]: Gate PCLK under VSYNC
+                                // Bit[2]: Gate PCLK under HREF
+                                // Bit[1]: HREF polarity
+                                //          0: active low
+                                //          1: active high
+                                // Bit[0] VSYNC polarity
+                                //          0: active low
+                                //          1: active high
+
+#define ISP_CONTROL_01   0x5001 // Bit[5]: Scale enable
+                                //          0: Disable
+                                //          1: Enable
+
+/* output format control registers */
+#define FORMAT_CTRL     0x501F // Format select
+                                // Bit[2:0]:
+                                //  000: YUV422
+                                //  001: RGB
+                                //  010: Dither
+                                //  011: RAW after DPC
+                                //  101: RAW after CIP
+
+/* ISP top control registers */
+#define PRE_ISP_TEST_SETTING_1  0x503D  // Bit[7]: Test enable
+                                        //         0: Test disable
+                                        //         1: Color bar enable
+                                        // Bit[6]: Rolling
+                                        // Bit[5]: Transparent
+                                        // Bit[4]: Square black and white
+                                        // Bit[3:2]: Color bar style
+                                        //         00: Standard 8 color bar
+                                        //         01: Gradual change at vertical mode 1
+                                        //         10: Gradual change at horizontal
+                                        //         11: Gradual change at vertical mode 2
+                                        // Bit[1:0]: Test select
+                                        //         00: Color bar
+                                        //         01: Random data
+                                        //         10: Square data
+                                        //         11: Black image
+
+//exposure = {0x3500[3:0], 0x3501[7:0], 0x3502[7:0]} / 16 × tROW
+
+#define SCALE_CTRL_1     0x5601 // Bit[6:4]: HDIV RW
+                                //          DCW scale times
+                                //          000: DCW 1 time
+                                //          001: DCW 2 times
+                                //          010: DCW 4 times
+                                //          100: DCW 8 times
+                                //          101: DCW 16 times
+                                //          Others: DCW 16 times
+                                // Bit[2:0]: VDIV RW
+                                //          DCW scale times
+                                //          000: DCW 1 time
+                                //          001: DCW 2 times
+                                //          010: DCW 4 times
+                                //          100: DCW 8 times
+                                //          101: DCW 16 times
+                                //          Others: DCW 16 times
+
+#define SCALE_CTRL_2     0x5602 // X_SCALE High Bits
+#define SCALE_CTRL_3     0x5603 // X_SCALE Low Bits
+#define SCALE_CTRL_4     0x5604 // Y_SCALE High Bits
+#define SCALE_CTRL_5     0x5605 // Y_SCALE Low Bits
+#define SCALE_CTRL_6     0x5606 // Bit[3:0]: V Offset
+
+#define VFIFO_CTRL0C     0x460C // Bit[1]: PCLK manual enable
+                                //          0: Auto
+                                //          1: Manual by PCLK_RATIO
+
+#define VFIFO_X_SIZE_H   0x4602
+#define VFIFO_X_SIZE_L   0x4603
+#define VFIFO_Y_SIZE_H   0x4604
+#define VFIFO_Y_SIZE_L   0x4605
+
+#define COMPRESSION_CTRL00 0x4400 //
+#define COMPRESSION_CTRL01 0x4401 //
+#define COMPRESSION_CTRL02 0x4402 //
+#define COMPRESSION_CTRL03 0x4403 //
+#define COMPRESSION_CTRL04 0x4404 //
+#define COMPRESSION_CTRL05 0x4405 //
+#define COMPRESSION_CTRL06 0x4406 //
+#define COMPRESSION_CTRL07 0x4407 // Bit[5:0]: QS
+#define COMPRESSION_ISI_CTRL 0x4408 //
+#define COMPRESSION_CTRL09 0x4409 //
+#define COMPRESSION_CTRL0a 0x440a //
+#define COMPRESSION_CTRL0b 0x440b //
+#define COMPRESSION_CTRL0c 0x440c //
+#define COMPRESSION_CTRL0d 0x440d //
+#define COMPRESSION_CTRL0E 0x440e //
+
+/**
+ * @brief register value
+ */
+#define TEST_COLOR_BAR  0xC0    /* Enable Color Bar roling Test */
+
+#define AEC_PK_MANUAL_AGC_MANUALEN  0x02    /* Enable AGC Manual enable */
+#define AEC_PK_MANUAL_AEC_MANUALEN  0x01    /* Enable AEC Manual enable */
+
+#define TIMING_TC_REG20_VFLIP   0x06 /* Vertical flip enable */
+#define TIMING_TC_REG21_HMIRROR 0x06 /* Horizontal mirror enable */
+
+#endif // __OV3660_REG_REGS_H__

+ 334 - 0
std/camera/sensors/private_include/ov5640_settings.h

@@ -0,0 +1,334 @@
+#ifndef _OV5640_SETTINGS_H_
+#define _OV5640_SETTINGS_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp_attr.h"
+#include "ov5640_regs.h"
+
+static const ratio_settings_t ratio_table[] = {
+    //  mw,   mh,  sx,  sy,   ex,   ey, ox, oy,   tx,   ty
+    { 2560, 1920,   0,   0, 2623, 1951, 32, 16, 2844, 1968 }, //4x3
+    { 2560, 1704,   0, 110, 2623, 1843, 32, 16, 2844, 1752 }, //3x2
+    { 2560, 1600,   0, 160, 2623, 1791, 32, 16, 2844, 1648 }, //16x10
+    { 2560, 1536,   0, 192, 2623, 1759, 32, 16, 2844, 1584 }, //5x3
+    { 2560, 1440,   0, 240, 2623, 1711, 32, 16, 2844, 1488 }, //16x9
+    { 2560, 1080,   0, 420, 2623, 1531, 32, 16, 2844, 1128 }, //21x9
+    { 2400, 1920,  80,   0, 2543, 1951, 32, 16, 2684, 1968 }, //5x4
+    { 1920, 1920, 320,   0, 2543, 1951, 32, 16, 2684, 1968 }, //1x1
+    { 1088, 1920, 736,   0, 1887, 1951, 32, 16, 1884, 1968 }  //9x16
+};
+
+#define REG_DLY 0xffff
+#define REGLIST_TAIL 0x0000
+
+static const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
+    {SYSTEM_CTROL0, 0x82},  // software reset
+    {REG_DLY, 10}, // delay 10ms
+    {SYSTEM_CTROL0, 0x42},  // power down
+
+    //enable pll
+    {0x3103, 0x13},
+
+    //io direction
+    {0x3017, 0xff},
+    {0x3018, 0xff},
+
+    {DRIVE_CAPABILITY, 0xc3},
+    {CLOCK_POL_CONTROL, 0x21},
+
+    {0x4713, 0x02},//jpg mode select
+
+    {ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
+
+    //sys reset
+    {0x3000, 0x00},
+    {0x3002, 0x1c},
+
+    //clock enable
+    {0x3004, 0xff},
+    {0x3006, 0xc3},
+
+    //isp control
+    {0x5000, 0xa7},
+    {ISP_CONTROL_01, 0xa3},//+scaling?
+    {0x5003, 0x08},//special_effect
+
+    //unknown
+    {0x370c, 0x02},//!!IMPORTANT
+    {0x3634, 0x40},//!!IMPORTANT
+
+    //AEC/AGC
+    {0x3a02, 0x03},
+    {0x3a03, 0xd8},
+    {0x3a08, 0x01},
+    {0x3a09, 0x27},
+    {0x3a0a, 0x00},
+    {0x3a0b, 0xf6},
+    {0x3a0d, 0x04},
+    {0x3a0e, 0x03},
+    {0x3a0f, 0x30},//ae_level
+    {0x3a10, 0x28},//ae_level
+    {0x3a11, 0x60},//ae_level
+    {0x3a13, 0x43},
+    {0x3a14, 0x03},
+    {0x3a15, 0xd8},
+    {0x3a18, 0x00},//gainceiling
+    {0x3a19, 0xf8},//gainceiling
+    {0x3a1b, 0x30},//ae_level
+    {0x3a1e, 0x26},//ae_level
+    {0x3a1f, 0x14},//ae_level
+
+    //vcm debug
+    {0x3600, 0x08},
+    {0x3601, 0x33},
+
+    //50/60Hz
+    {0x3c01, 0xa4},
+    {0x3c04, 0x28},
+    {0x3c05, 0x98},
+    {0x3c06, 0x00},
+    {0x3c07, 0x08},
+    {0x3c08, 0x00},
+    {0x3c09, 0x1c},
+    {0x3c0a, 0x9c},
+    {0x3c0b, 0x40},
+
+    {0x460c, 0x22},//disable jpeg footer
+
+    //BLC
+    {0x4001, 0x02},
+    {0x4004, 0x02},
+
+    //AWB
+    {0x5180, 0xff},
+    {0x5181, 0xf2},
+    {0x5182, 0x00},
+    {0x5183, 0x14},
+    {0x5184, 0x25},
+    {0x5185, 0x24},
+    {0x5186, 0x09},
+    {0x5187, 0x09},
+    {0x5188, 0x09},
+    {0x5189, 0x75},
+    {0x518a, 0x54},
+    {0x518b, 0xe0},
+    {0x518c, 0xb2},
+    {0x518d, 0x42},
+    {0x518e, 0x3d},
+    {0x518f, 0x56},
+    {0x5190, 0x46},
+    {0x5191, 0xf8},
+    {0x5192, 0x04},
+    {0x5193, 0x70},
+    {0x5194, 0xf0},
+    {0x5195, 0xf0},
+    {0x5196, 0x03},
+    {0x5197, 0x01},
+    {0x5198, 0x04},
+    {0x5199, 0x12},
+    {0x519a, 0x04},
+    {0x519b, 0x00},
+    {0x519c, 0x06},
+    {0x519d, 0x82},
+    {0x519e, 0x38},
+
+    //color matrix (Saturation)
+    {0x5381, 0x1e},
+    {0x5382, 0x5b},
+    {0x5383, 0x08},
+    {0x5384, 0x0a},
+    {0x5385, 0x7e},
+    {0x5386, 0x88},
+    {0x5387, 0x7c},
+    {0x5388, 0x6c},
+    {0x5389, 0x10},
+    {0x538a, 0x01},
+    {0x538b, 0x98},
+
+    //CIP control (Sharpness)
+    {0x5300, 0x10},//sharpness
+    {0x5301, 0x10},//sharpness
+    {0x5302, 0x18},//sharpness
+    {0x5303, 0x19},//sharpness
+    {0x5304, 0x10},
+    {0x5305, 0x10},
+    {0x5306, 0x08},//denoise
+    {0x5307, 0x16},
+    {0x5308, 0x40},
+    {0x5309, 0x10},//sharpness
+    {0x530a, 0x10},//sharpness
+    {0x530b, 0x04},//sharpness
+    {0x530c, 0x06},//sharpness
+
+    //GAMMA
+    {0x5480, 0x01},
+    {0x5481, 0x00},
+    {0x5482, 0x1e},
+    {0x5483, 0x3b},
+    {0x5484, 0x58},
+    {0x5485, 0x66},
+    {0x5486, 0x71},
+    {0x5487, 0x7d},
+    {0x5488, 0x83},
+    {0x5489, 0x8f},
+    {0x548a, 0x98},
+    {0x548b, 0xa6},
+    {0x548c, 0xb8},
+    {0x548d, 0xca},
+    {0x548e, 0xd7},
+    {0x548f, 0xe3},
+    {0x5490, 0x1d},
+
+    //Special Digital Effects (SDE) (UV adjust)
+    {0x5580, 0x06},//enable brightness and contrast
+    {0x5583, 0x40},//special_effect
+    {0x5584, 0x10},//special_effect
+    {0x5586, 0x20},//contrast
+    {0x5587, 0x00},//brightness
+    {0x5588, 0x00},//brightness
+    {0x5589, 0x10},
+    {0x558a, 0x00},
+    {0x558b, 0xf8},
+    {0x501d, 0x40},// enable manual offset of contrast
+
+    //power on
+    {0x3008, 0x02},
+
+    //50Hz
+    {0x3c00, 0x04},
+    
+    {REG_DLY, 300},
+    {REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = {
+    {FORMAT_CTRL, 0x00}, // YUV422
+    {FORMAT_CTRL00, 0x30}, // YUYV
+    {0x3002, 0x00},//0x1c to 0x00 !!!
+    {0x3006, 0xff},//0xc3 to 0xff !!!
+    {0x471c, 0x50},//0xd0 to 0x50 !!!
+    {REGLIST_TAIL, 0x00}, // tail
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_raw[][2] = {
+    {FORMAT_CTRL, 0x03}, // RAW (DPC)
+    {FORMAT_CTRL00, 0x00}, // RAW
+    {REGLIST_TAIL, 0x00}
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_grayscale[][2] = {
+    {FORMAT_CTRL, 0x00}, // YUV422
+    {FORMAT_CTRL00, 0x10}, // Y8
+    {REGLIST_TAIL, 0x00}
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_yuv422[][2] = {
+    {FORMAT_CTRL, 0x00}, // YUV422
+    {FORMAT_CTRL00, 0x30}, // YUYV
+    {REGLIST_TAIL, 0x00}
+};
+
+static const DRAM_ATTR uint16_t sensor_fmt_rgb565[][2] = {
+    {FORMAT_CTRL, 0x01}, // RGB
+    {FORMAT_CTRL00, 0x61}, // RGB565 (BGR)
+    {REGLIST_TAIL, 0x00}
+};
+
+static const DRAM_ATTR uint8_t sensor_saturation_levels[9][11] = {
+    {0x1d, 0x60, 0x03, 0x07, 0x48, 0x4f, 0x4b, 0x40, 0x0b, 0x01, 0x98},//-4
+    {0x1d, 0x60, 0x03, 0x08, 0x54, 0x5c, 0x58, 0x4b, 0x0d, 0x01, 0x98},//-3
+    {0x1d, 0x60, 0x03, 0x0a, 0x60, 0x6a, 0x64, 0x56, 0x0e, 0x01, 0x98},//-2
+    {0x1d, 0x60, 0x03, 0x0b, 0x6c, 0x77, 0x70, 0x60, 0x10, 0x01, 0x98},//-1
+    {0x1d, 0x60, 0x03, 0x0c, 0x78, 0x84, 0x7d, 0x6b, 0x12, 0x01, 0x98},//0
+    {0x1d, 0x60, 0x03, 0x0d, 0x84, 0x91, 0x8a, 0x76, 0x14, 0x01, 0x98},//+1
+    {0x1d, 0x60, 0x03, 0x0e, 0x90, 0x9e, 0x96, 0x80, 0x16, 0x01, 0x98},//+2
+    {0x1d, 0x60, 0x03, 0x10, 0x9c, 0xac, 0xa2, 0x8b, 0x17, 0x01, 0x98},//+3
+    {0x1d, 0x60, 0x03, 0x11, 0xa8, 0xb9, 0xaf, 0x96, 0x19, 0x01, 0x98},//+4
+};
+
+static const DRAM_ATTR uint8_t sensor_special_effects[7][4] = {
+    {0x06, 0x40, 0x2c, 0x08},//Normal
+    {0x46, 0x40, 0x28, 0x08},//Negative
+    {0x1e, 0x80, 0x80, 0x08},//Grayscale
+    {0x1e, 0x80, 0xc0, 0x08},//Red Tint
+    {0x1e, 0x60, 0x60, 0x08},//Green Tint
+    {0x1e, 0xa0, 0x40, 0x08},//Blue Tint
+    {0x1e, 0x40, 0xa0, 0x08},//Sepia
+};
+
+static const DRAM_ATTR uint16_t sensor_regs_gamma0[][2] = {
+    {0x5480, 0x01},
+    {0x5481, 0x08},
+    {0x5482, 0x14},
+    {0x5483, 0x28},
+    {0x5484, 0x51},
+    {0x5485, 0x65},
+    {0x5486, 0x71},
+    {0x5487, 0x7d},
+    {0x5488, 0x87},
+    {0x5489, 0x91},
+    {0x548a, 0x9a},
+    {0x548b, 0xaa},
+    {0x548c, 0xb8},
+    {0x548d, 0xcd},
+    {0x548e, 0xdd},
+    {0x548f, 0xea},
+    {0x5490, 0x1d}
+};
+
+static const DRAM_ATTR uint16_t sensor_regs_gamma1[][2] = {
+    {0x5480, 0x1},
+    {0x5481, 0x0},
+    {0x5482, 0x1e},
+    {0x5483, 0x3b},
+    {0x5484, 0x58},
+    {0x5485, 0x66},
+    {0x5486, 0x71},
+    {0x5487, 0x7d},
+    {0x5488, 0x83},
+    {0x5489, 0x8f},
+    {0x548a, 0x98},
+    {0x548b, 0xa6},
+    {0x548c, 0xb8},
+    {0x548d, 0xca},
+    {0x548e, 0xd7},
+    {0x548f, 0xe3},
+    {0x5490, 0x1d}
+};
+
+static const DRAM_ATTR uint16_t sensor_regs_awb0[][2] = {
+    {0x5180, 0xff},
+    {0x5181, 0xf2},
+    {0x5182, 0x00},
+    {0x5183, 0x14},
+    {0x5184, 0x25},
+    {0x5185, 0x24},
+    {0x5186, 0x09},
+    {0x5187, 0x09},
+    {0x5188, 0x09},
+    {0x5189, 0x75},
+    {0x518a, 0x54},
+    {0x518b, 0xe0},
+    {0x518c, 0xb2},
+    {0x518d, 0x42},
+    {0x518e, 0x3d},
+    {0x518f, 0x56},
+    {0x5190, 0x46},
+    {0x5191, 0xf8},
+    {0x5192, 0x04},
+    {0x5193, 0x70},
+    {0x5194, 0xf0},
+    {0x5195, 0xf0},
+    {0x5196, 0x03},
+    {0x5197, 0x01},
+    {0x5198, 0x04},
+    {0x5199, 0x12},
+    {0x519a, 0x04},
+    {0x519b, 0x00},
+    {0x519c, 0x06},
+    {0x519d, 0x82},
+    {0x519e, 0x38}
+};
+
+#endif

+ 33 - 0
std/camera/sensors/private_include/ov7670.h

@@ -0,0 +1,33 @@
+/*
+ * This file is part of the OpenMV project.
+ * author: Juan Schiavoni <juanjoseschiavoni@hotmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV7670 driver.
+ *
+ */
+#ifndef __OV7670_H__
+#define __OV7670_H__
+#include "sensor.h"
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ *     0:       Can't detect this sensor
+ *     Nonzero: This sensor has been detected
+ */
+int ov7670_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int ov7670_init(sensor_t *sensor);
+
+#endif // __OV7670_H__

+ 354 - 0
std/camera/sensors/private_include/ov7670_regs.h

@@ -0,0 +1,354 @@
+/*
+ * This file is for the OpenMV project so the OV7670 can be used
+ * author: Juan Schiavoni <juanjoseschiavoni@hotmail.com>
+ *
+ * OV7670 register definitions.
+ */
+#ifndef __OV7670_REG_REGS_H__
+#define __OV7670_REG_REGS_H__
+#define GAIN                    0x00 /* AGC – Gain control gain setting  */
+#define BLUE                    0x01 /* AWB – Blue channel gain setting  */
+#define RED                     0x02 /* AWB – Red channel gain setting   */
+#define VREF                    0x03 /* AWB – Green channel gain setting */
+#define COM1			        0x04 /* Common Control 1 */
+#define BAVG                    0x05 /* U/B Average Level   */
+#define GAVG                    0x06 /* Y/Gb Average Level  */
+#define AECH                    0x07 /* Exposure VAlue - AEC MSB 5 bits  */
+#define RAVG                    0x08 /* V/R Average Level */
+
+#define COM2                    0x09 /* Common Control 2 */
+#define COM2_SOFT_SLEEP         0x10 /* Soft sleep mode  */
+#define COM2_OUT_DRIVE_1x       0x00 /* Output drive capability 1x */
+#define COM2_OUT_DRIVE_2x       0x01 /* Output drive capability 2x */
+#define COM2_OUT_DRIVE_3x       0x02 /* Output drive capability 3x */
+#define COM2_OUT_DRIVE_4x       0x03 /* Output drive capability 4x */
+
+#define REG_PID                 0x0A /* Product ID Number MSB */
+#define REG_VER                 0x0B /* Product ID Number LSB */
+
+#define COM3                    0x0C /* Common Control 3 		 */
+#define COM3_SWAP_OUT           0x40 /* Output data MSB/LSB swap */
+#define COM3_TRI_CLK            0x20 /* Tri-state output clock   */
+#define COM3_TRI_DATA           0x10 /* Tri-state option output  */
+#define COM3_SCALE_EN           0x08 /* Scale enable             */
+#define COM3_DCW                0x04 /* DCW enable               */
+
+#define COM4                    0x0D /* Common Control 4         */
+#define COM4_PLL_BYPASS         0x00 /* Bypass PLL               */
+#define COM4_PLL_4x             0x40 /* PLL frequency 4x         */
+#define COM4_PLL_6x             0x80 /* PLL frequency 6x         */
+#define COM4_PLL_8x             0xc0 /* PLL frequency 8x         */
+#define COM4_AEC_FULL           0x00 /* AEC evaluate full window */
+#define COM4_AEC_1_2            0x10 /* AEC evaluate 1/2 window  */
+#define COM4_AEC_1_4            0x20 /* AEC evaluate 1/4 window  */
+#define COM4_AEC_2_3            0x30 /* AEC evaluate 2/3 window  */
+
+#define COM5                    0x0E /* Common Control 5 */
+#define COM5_AFR                0x80 /* Auto frame rate control ON/OFF selection (night mode) */
+#define COM5_AFR_SPEED          0x40 /* Auto frame rate control speed selection */
+#define COM5_AFR_0              0x00 /* No reduction of frame rate          */
+#define COM5_AFR_1_2            0x10 /* Max reduction to 1/2 frame rate     */
+#define COM5_AFR_1_4            0x20 /* Max reduction to 1/4 frame rate     */
+#define COM5_AFR_1_8            0x30 /* Max reduction to 1/8 frame rate     */
+#define COM5_AFR_4x             0x04 /* Add frame when AGC reaches 4x gain  */
+#define COM5_AFR_8x             0x08 /* Add frame when AGC reaches 8x gain  */
+#define COM5_AFR_16x            0x0c /* Add frame when AGC reaches 16x gain */
+#define COM5_AEC_NO_LIMIT       0x01 /* No limit to AEC increase step       */
+
+#define COM6                    0x0F /* Common Control 6 */
+#define COM6_AUTO_WINDOW        0x01 /* Auto window setting ON/OFF selection when format changes */
+
+#define AEC                     0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */
+#define CLKRC                   0x11 /* Internal Clock */
+
+#define COM7                    0x12 /* Common Control 7         */
+#define COM7_RESET              0x80 /* SCCB Register Reset      */
+#define COM7_RES_VGA            0x00 /* Resolution VGA           */
+#define COM7_RES_QVGA           0x40 /* Resolution QVGA          */
+#define COM7_BT656              0x20 /* BT.656 protocol ON/OFF   */
+#define COM7_SENSOR_RAW         0x10 /* Sensor RAW               */
+#define COM7_FMT_GBR422         0x00 /* RGB output format GBR422 */
+#define COM7_FMT_RGB565         0x04 /* RGB output format RGB565 */
+#define COM7_FMT_RGB555         0x08 /* RGB output format RGB555 */
+#define COM7_FMT_RGB444         0x0C /* RGB output format RGB444 */
+#define COM7_FMT_YUV            0x00 /* Output format YUV        */
+#define COM7_FMT_P_BAYER        0x01 /* Output format Processed Bayer RAW */
+#define COM7_FMT_RGB            0x04 /* Output format RGB        */
+#define COM7_FMT_R_BAYER        0x03 /* Output format Bayer RAW  */
+#define COM7_SET_FMT(r, x)      ((r&0xFC)|((x&0x5)<<0))
+
+#define COM8                    0x13 /* Common Control 8                */
+#define COM8_FAST_AUTO          0x80 /* Enable fast AGC/AEC algorithm   */
+#define COM8_STEP_VSYNC         0x00 /* AEC - Step size limited to vertical blank */
+#define COM8_STEP_UNLIMIT       0x40 /* AEC - Step size unlimited step size       */
+#define COM8_BANDF_EN           0x20 /* Banding filter ON/OFF */
+#define COM8_AEC_BANDF          0x10 /* Enable AEC below banding value */
+#define COM8_AEC_FINE_EN        0x08 /* Fine AEC ON/OFF control */
+#define COM8_AGC_EN             0x04 /* AGC Enable */
+#define COM8_AWB_EN             0x02 /* AWB Enable */
+#define COM8_AEC_EN             0x01 /* AEC Enable */
+#define COM8_SET_AGC(r, x)      ((r&0xFB)|((x&0x1)<<2))
+#define COM8_SET_AWB(r, x)      ((r&0xFD)|((x&0x1)<<1))
+#define COM8_SET_AEC(r, x)      ((r&0xFE)|((x&0x1)<<0))
+
+#define COM9                    0x14 /* Common Control 9 */
+#define COM9_HISTO_AVG          0x80 /* Histogram or average based AEC/AGC selection */
+#define COM9_AGC_GAIN_2x        0x00 /* Automatic Gain Ceiling 2x  */
+#define COM9_AGC_GAIN_4x        0x10 /* Automatic Gain Ceiling 4x  */
+#define COM9_AGC_GAIN_8x        0x20 /* Automatic Gain Ceiling 8x  */
+#define COM9_AGC_GAIN_16x       0x30 /* Automatic Gain Ceiling 16x */
+#define COM9_AGC_GAIN_32x       0x40 /* Automatic Gain Ceiling 32x */
+#define COM9_DROP_VSYNC         0x04 /* Drop VSYNC output of corrupt frame */
+#define COM9_DROP_HREF          0x02 /* Drop HREF output of corrupt frame  */
+#define COM9_SET_AGC(r, x)      ((r&0x8F)|((x&0x07)<<4))
+
+#define COM10                   0x15 /* Common Control 10 */
+#define COM10_NEGATIVE          0x80 /* Output negative data */
+#define COM10_HSYNC_EN          0x40 /* HREF changes to HSYNC */
+#define COM10_PCLK_FREE         0x00 /* PCLK output option: free running PCLK */
+#define COM10_PCLK_MASK         0x20 /* PCLK output option: masked during horizontal blank  */
+#define COM10_PCLK_REV          0x10 /* PCLK reverse */
+#define COM10_HREF_REV          0x08 /* HREF reverse */
+#define COM10_VSYNC_FALLING     0x00 /* VSYNC changes on falling edge of PCLK */
+#define COM10_VSYNC_RISING      0x04 /* VSYNC changes on rising edge of PCLK */
+#define COM10_VSYNC_NEG         0x02 /* VSYNC negative */
+#define COM10_OUT_RANGE_8       0x01 /* Output data range: Full range */
+#define COM10_OUT_RANGE_10      0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */
+
+#define RSVD_16                 0x16 /* Reserved register */
+
+#define HSTART                  0x17  /* Horizontal Frame (HREF column) Start high 8-bit(low 3 bits are at HREF[2:0]) */
+#define HSTOP                   0x18  /* Horizontal Frame (HREF column) end high 8-bit (low 3 bits are at HREF[5:3])  */
+#define VSTART                  0x19  /* Vertical Frame (row) Start high 8-bit (low 2 bits are at VREF[1:0]) */
+#define VSTOP                   0x1A  /* Vertical Frame (row) End high 8-bit (low 2 bits are at VREF[3:2]) */
+#define PSHFT                   0x1B  /* Data Format - Pixel Delay Select */
+#define REG_MIDH                0x1C  /* Manufacturer ID Byte – High */
+#define REG_MIDL                0x1D  /* Manufacturer ID Byte – Low */
+
+#define MVFP			        0x1E  /* Mirror/Vflip Enable */
+#define   MVFP_MIRROR	        0x20  /* Mirror image */
+#define   MVFP_FLIP	            0x10  /* Vertical flip */
+#define   MVFP_SUN	            0x02  /* Black sun enable */
+#define MVFP_SET_MIRROR(r,x)	((r&0xDF)|((x&1)<<5)) /* change only bit5 according to x */
+#define MVFP_SET_FLIP(r,x)	    ((r&0xEF)|((x&1)<<4)) /* change only bit4 according to x */
+
+#define LAEC                    0x1F /* Fine AEC Value - defines exposure value less than one row period (Reserved?) */
+#define ADCCTR0                 0x20 /* ADC control */
+#define ADCCTR1			        0x21 /* reserved */
+#define ADCCTR2                 0x22 /* reserved */
+#define ADCCTR3                 0x23 /* reserved */
+#define AEW                     0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */
+#define AEB                     0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */
+#define VPT                     0x26 /* AGC/AEC Fast Mode Operating Region */
+#define BBIAS 			        0x27 /* B channel signal output bias (effective only when COM6[3]=1) */
+#define GbBIAS                  0x28 /* Gb channel signal output bias (effective only when COM6[3]=1) */
+#define RSVD_29                 0x29 /* reserved */
+#define EXHCH                   0x2A /* Dummy Pixel Insert MSB */
+#define EXHCL                   0x2B /* Dummy Pixel Insert LSB */
+#define RBIAS                   0x2C /* R channel signal output bias (effective only when COM6[3]=1) */
+#define ADVFL                   0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row)  */
+#define ADVFH                   0x2E /* MSB of Insert Dummy Rows in Vertical Sync */
+#define YAVE                    0x2F /* Y/G Channel Average Value */
+#define HSYST                   0x30 /* HSync rising edge delay */
+#define HSYEN                   0x31 /* HSync falling edge delay  */
+#define HREF                    0x32 /* Image Start and Size Control DIFFERENT CONTROL SEQUENCE	 */
+#define CHLF                    0x33 /* Array Current control  */
+#define ARBLM                   0x34 /* Array reference control */
+#define RSVD_35                 0x35 /* Reserved */
+#define RSVD_36                 0x36 /* Reserved */
+#define ADC                     0x37 /* ADC control */
+#define ACOM                    0x38 /* ADC and analog common mode control */
+#define OFON                    0x39 /* ADC offset control */
+#define TSLB                    0x3A /* Line buffer test option  */
+
+#define COM11                   0x3B /* Common control 11 */
+#define   COM11_EXP		        0x02
+#define   COM11_HZAUTO		    0x10 /* Auto detect 50/60 Hz */
+
+#define COM12                   0x3C /* Common control 12 */
+
+#define COM13                   0x3D /* Common control 13 */
+#define   COM13_GAMMA	        0x80 /* Gamma enable */
+#define	  COM13_UVSAT	        0x40 /* UV saturation auto adjustment */
+
+#define COM14                   0x3E /* Common Control 14 */
+
+#define EDGE                    0x3F /* edge enhancement adjustment */
+#define COM15                   0x40 /* Common Control 15 DIFFERENT CONTROLS */
+#define COM15_SET_RGB565(r,x)	((r&0xEF)|((x&1)<<4)) /* set rgb565 mode */
+#define   COM15_RGB565	        0x10 /* RGB565 output */
+#define   COM15_R00FF           0xC0 /* Output range: [00] to [FF] */
+
+#define COM16                   0x41 /* Common Control 16 DIFFERENT CONTROLS */
+#define COM16_AWBGAIN		    0x08 /* AWB gain enable */
+#define COM17                   0x42 /* Common Control 17   */
+
+#define AWBC1                   0x43 /* Reserved */
+#define AWBC2                  	0x44 /* Reserved */
+#define AWBC3                  	0x45 /* Reserved */
+#define AWBC4                   0x46 /* Reserved */
+#define AWBC5                  	0x47 /* Reserved */
+#define AWBC6                  	0x48 /* Reserved */
+
+#define RSVD_49			        0x49 /* Reserved */
+#define RSVD_4A			        0x4A /* Reserved */
+
+#define REG4B                   0x4B /* Register 4B */
+#define DNSTH                   0x4C /* Denoise strength */
+
+#define RSVD_4D			        0x4D /* Reserved */
+#define RSVD_4E			        0x4E /* Reserved */
+
+#define MTX1                    0x4F /* Matrix coefficient 1 */
+#define MTX2                    0x50 /* Matrix coefficient 2 */
+#define MTX3                    0x51 /* Matrix coefficient 3 */
+#define MTX4                    0x52 /* Matrix coefficient 4 */
+#define MTX5                    0x53 /* Matrix coefficient 5 */
+#define MTX6                    0x54 /* Matrix coefficient 6 */
+#define BRIGHTNESS              0x55 /* Brightness control */
+#define CONTRAST		        0x56 /* Contrast control */
+#define CONTRASCENTER           0x57 /* Contrast center */
+#define MTXS			        0x58 /* Matrix coefficient sign for coefficient 5 to 0*/
+
+#define RSVD_59			        0x59 /* Reserved */
+#define RSVD_5A			        0x5A /* Reserved */
+#define RSVD_5B			        0x5B /* Reserved */
+#define RSVD_5C			        0x5C /* Reserved */
+#define RSVD_5D			        0x5D /* Reserved */
+#define RSVD_5E			        0x5E /* Reserved */
+#define RSVD_5F			        0x5F /* Reserved */
+#define RSVD_60			        0x60 /* Reserved */
+#define RSVD_61			        0x61 /* Reserved */
+
+#define LCC1                    0x62 /* Lens correction option 1  */
+
+#define LCC2                    0x63 /* Lens correction option 2 */
+#define LCC3 			        0x64 /* Lens correction option 3 */
+#define LCC4			        0x65 /* Lens correction option 4 */
+#define LCC5			        0x66 /* Lens correction option 5 */
+
+#define MANU       		        0x67 /* Manual U Value      */
+#define MANV      		        0x68 /* Manual V Value */
+#define GFIX                    0x69 /* Fix gain control */
+#define GGAIN                   0x6A /* G channel AWB gain */
+
+#define DBLV               	    0x6B /* PLL and clock ? */
+
+#define AWBCTR3               	0x6C /* AWB Control 3  */
+#define AWBCTR2	                0x6D /* AWB Control 2  */
+#define AWBCTR1                 0x6E /* AWB Control 1  */
+#define AWBCTR0                 0x6F /* AWB Control 0  */
+#define SCALING_XSC             0x70 /* test pattern and horizontal scaling factor */
+#define SCALING_XSC_CBAR(r)	(r&0x7F) /* make sure bit7 is 0 for color bar */
+#define SCALING_YSC             0x71 /* test pattern and vertical scaling factor */
+#define SCALING_YSC_CBAR(r,x)	((r&0x7F)|((x&1)<<7)) /* change bit7 for color bar on/off */
+#define SCALING_DCWCTR          0x72 /* DCW control */
+#define SCALING_PCLK_DIV        0x73 /*  */
+#define REG74                   0x74 /*  */
+#define REG75                   0x75 /*  */
+#define REG76                   0x76 /*  */
+#define REG77	             	0x77 /*  */
+
+#define RSVD_78      		    0x78 /* Reserved */
+#define RSVD_79              	0x79 /* Reserved */
+
+#define SLOP	                0x7A /* Gamma curve highest segment slope */
+#define GAM1	                0x7B /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */
+#define GAM2	                0x7C /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */
+#define GAM3                    0x7D /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */
+#define GAM4                    0x7E /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */
+#define GAM5                    0x7F /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */
+#define GAM6                    0x80 /* Gamma Curve 6rd Segment Input End Point 0x30 Output Value */
+#define GAM7                    0x81 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */
+#define GAM8                    0x82 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */
+#define GAM9                    0x83 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */
+#define GAM10                   0x84 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */
+#define GAM11                   0x85 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */
+#define GAM12                   0x86 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */
+#define GAM13                   0x87 /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */
+#define GAM14                   0x88 /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */
+#define GAM15                   0x89 /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */
+
+#define RSVD_8A      		    0x8A /* Reserved */
+#define RSVD_8B              	0x8B /* Reserved */
+
+#define RGB444                  0x8C /*  */
+
+#define RSVD_8D      		    0x8D /* Reserved */
+#define RSVD_8E              	0x8E /* Reserved */
+#define RSVD_8F      		    0x8F /* Reserved */
+#define RSVD_90              	0x90 /* Reserved */
+#define RSVD_91      		    0x91 /* Reserved */
+
+#define DM_LNL                  0x92 /* Dummy line low 8 bit */
+#define DM_LNH                  0x93 /* Dummy line high 8 bit */
+#define LCC6                    0x94 /* Lens correction option 6 */
+#define LCC7                    0x95 /* Lens correction option 7 */
+
+#define RSVD_96      		    0x96 /* Reserved */
+#define RSVD_97              	0x97 /* Reserved */
+#define RSVD_98      		    0x98 /* Reserved */
+#define RSVD_99              	0x99 /* Reserved */
+#define RSVD_9A      		    0x9A /* Reserved */
+#define RSVD_9B              	0x9B /* Reserved */
+#define RSVD_9C      		    0x9C /* Reserved */
+
+#define BD50ST			        0x9D /* 50 Hz banding filter value */
+#define BD60ST                  0x9E /* 60 Hz banding filter value */
+#define HAECC1                  0x9F /* Histogram-based AEC/AGC control 1 */
+#define HAECC2                  0xA0 /* Histogram-based AEC/AGC control 2 */
+
+#define RSVD_A1      		    0xA1 /* Reserved */
+
+#define SCALING_PCLK_DELAY      0xA2 /* Pixel clock delay */
+
+#define RSVD_A3      		    0xA3 /* Reserved */
+
+#define NT_CNTRL                0xA4 /*  */
+#define BD50MAX			        0xA5 /* 50 Hz banding step limit */
+#define HAECC3                  0xA6 /* Histogram-based AEC/AGC control 3  */
+#define HAECC4 	   	            0xA7 /* Histogram-based AEC/AGC control 4           */
+#define HAECC5		            0xA8 /* Histogram-based AEC/AGC control 5         */
+#define HAECC6		            0xA9 /* Histogram-based AEC/AGC control 6           */
+
+#define HAECC7		            0xAA /* Histogram-based AEC/AGC control 7           */
+#define 	HAECC_EN      	    0x80 /* Histogram-based AEC algorithm enable      */
+
+#define BD60MAX                 0xAB /* 60 Hz banding step limit */
+
+#define STR_OPT                 0xAC /* Register AC */
+#define STR_R			        0xAD /* R gain for led output frame */
+#define STR_G			        0xAE /* G gain for led output frame */
+#define STR_B			        0xAF /* B gain for led output frame */
+#define RSVD_B0      		    0xB0 /* Reserved */
+#define ABLC1			        0xB1 /* */
+#define RSVD_B2      		    0xB2 /* Reserved */
+#define THL_ST			        0xB3 /* ABLC target */
+#define THL_DLT			        0xB5 /* ABLC stable range */
+
+#define RSVD_B6      		    0xB6 /* Reserved */
+#define RSVD_B7      		    0xB7 /* Reserved */
+#define RSVD_B8      		    0xB8 /* Reserved */
+#define RSVD_B9      		    0xB9 /* Reserved */
+#define RSVD_BA      		    0xBA /* Reserved */
+#define RSVD_BB      		    0xBB /* Reserved */
+#define RSVD_BC      		    0xBC /* Reserved */
+#define RSVD_BD      	    	0xBD /* Reserved */
+
+#define AD_CHB			        0xBE /* blue channel black level compensation */
+#define AD_CHR			        0xBF /* Red channel black level compensation */
+#define AD_CHGb			        0xC0 /* Gb channel black level compensation */
+#define AD_CHGr			        0xC1 /* Gr channel black level compensation */
+
+#define RSVD_C2      		    0xC2 /* Reserved */
+#define RSVD_C3      		    0xC3 /* Reserved */
+#define RSVD_C4      		    0xC4 /* Reserved */
+#define RSVD_C5      		    0xC5 /* Reserved */
+#define RSVD_C6      		    0xC6 /* Reserved */
+#define RSVD_C7      		    0xC7 /* Reserved */
+#define RSVD_C8      		    0xC8 /* Reserved */
+
+#define SATCTR			        0xC9 /* Saturation control */
+#define SET_REG(reg, x)         (##reg_DEFAULT|x)
+
+#endif //__OV7670_REG_REGS_H__

+ 33 - 0
std/camera/sensors/private_include/ov7725.h

@@ -0,0 +1,33 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV7725 driver.
+ *
+ */
+#ifndef __OV7725_H__
+#define __OV7725_H__
+#include "sensor.h"
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ *     0:       Can't detect this sensor
+ *     Nonzero: This sensor has been detected
+ */
+int ov7725_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int ov7725_init(sensor_t *sensor);
+
+#endif // __OV7725_H__

+ 335 - 0
std/camera/sensors/private_include/ov7725_regs.h

@@ -0,0 +1,335 @@
+/*
+ * This file is part of the OpenMV project.
+ * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV2640 register definitions.
+ */
+#ifndef __REG_REGS_H__
+#define __REG_REGS_H__
+#define GAIN                    0x00 /* AGC – Gain control gain setting  */
+#define BLUE                    0x01 /* AWB – Blue channel gain setting  */
+#define RED                     0x02 /* AWB – Red channel gain setting   */
+#define GREEN                   0x03 /* AWB – Green channel gain setting */
+#define BAVG                    0x05 /* U/B Average Level   */
+#define GAVG                    0x06 /* Y/Gb Average Level  */
+#define RAVG                    0x07 /* V/R Average Level   */
+#define AECH                    0x08 /* Exposure Value – AEC MSBs */
+
+#define COM2                    0x09 /* Common Control 2 */
+#define COM2_SOFT_SLEEP         0x10 /* Soft sleep mode  */
+#define COM2_OUT_DRIVE_1x       0x00 /* Output drive capability 1x */
+#define COM2_OUT_DRIVE_2x       0x01 /* Output drive capability 2x */
+#define COM2_OUT_DRIVE_3x       0x02 /* Output drive capability 3x */
+#define COM2_OUT_DRIVE_4x       0x03 /* Output drive capability 4x */
+
+#define REG_PID                     0x0A /* Product ID Number MSB */
+#define REG_VER                     0x0B /* Product ID Number LSB */
+
+#define COM3                    0x0C /* Common Control 3                                        */
+#define COM3_VFLIP              0x80 /* Vertical flip image ON/OFF selection                    */
+#define COM3_MIRROR             0x40 /* Horizontal mirror image ON/OFF selection                */
+#define COM3_SWAP_BR            0x20 /* Swap B/R output sequence in RGB output mode             */
+#define COM3_SWAP_YUV           0x10 /* Swap Y/UV output sequence in YUV output mode            */
+#define COM3_SWAP_MSB           0x08 /* Swap output MSB/LSB                                     */
+#define COM3_TRI_CLOCK          0x04 /* Tri-state option for output clock at power-down period  */
+#define COM3_TRI_DATA           0x02 /* Tri-state option for output data at power-down period   */
+#define COM3_COLOR_BAR          0x01 /* Sensor color bar test pattern output enable             */
+#define COM3_SET_CBAR(r, x)     ((r&0xFE)|((x&1)<<0))
+#define COM3_SET_MIRROR(r, x)   ((r&0xBF)|((x&1)<<6))
+#define COM3_SET_FLIP(r, x)     ((r&0x7F)|((x&1)<<7))
+
+#define COM4                    0x0D /* Common Control 4         */
+#define COM4_PLL_BYPASS         0x00 /* Bypass PLL               */
+#define COM4_PLL_4x             0x40 /* PLL frequency 4x         */
+#define COM4_PLL_6x             0x80 /* PLL frequency 6x         */
+#define COM4_PLL_8x             0xc0 /* PLL frequency 8x         */
+#define COM4_AEC_FULL           0x00 /* AEC evaluate full window */
+#define COM4_AEC_1_2            0x10 /* AEC evaluate 1/2 window  */
+#define COM4_AEC_1_4            0x20 /* AEC evaluate 1/4 window  */
+#define COM4_AEC_2_3            0x30 /* AEC evaluate 2/3 window  */
+
+#define COM5                    0x0E /* Common Control 5 */
+#define COM5_AFR                0x80 /* Auto frame rate control ON/OFF selection (night mode) */
+#define COM5_AFR_SPEED          0x40 /* Auto frame rate control speed selection */
+#define COM5_AFR_0              0x00 /* No reduction of frame rate          */
+#define COM5_AFR_1_2            0x10 /* Max reduction to 1/2 frame rate     */
+#define COM5_AFR_1_4            0x20 /* Max reduction to 1/4 frame rate     */
+#define COM5_AFR_1_8            0x30 /* Max reduction to 1/8 frame rate     */
+#define COM5_AFR_4x             0x04 /* Add frame when AGC reaches 4x gain  */
+#define COM5_AFR_8x             0x08 /* Add frame when AGC reaches 8x gain  */
+#define COM5_AFR_16x            0x0c /* Add frame when AGC reaches 16x gain */
+#define COM5_AEC_NO_LIMIT       0x01 /* No limit to AEC increase step       */
+
+#define COM6                    0x0F /* Common Control 6 */
+#define COM6_AUTO_WINDOW        0x01 /* Auto window setting ON/OFF selection when format changes */
+
+#define AEC                     0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */
+#define CLKRC                   0x11 /* Internal Clock */
+
+#define COM7                    0x12 /* Common Control 7         */
+#define COM7_RESET              0x80 /* SCCB Register Reset      */
+#define COM7_RES_VGA            0x00 /* Resolution VGA           */
+#define COM7_RES_QVGA           0x40 /* Resolution QVGA          */
+#define COM7_BT656              0x20 /* BT.656 protocol ON/OFF   */
+#define COM7_SENSOR_RAW         0x10 /* Sensor RAW               */
+#define COM7_FMT_GBR422         0x00 /* RGB output format GBR422 */
+#define COM7_FMT_RGB565         0x04 /* RGB output format RGB565 */
+#define COM7_FMT_RGB555         0x08 /* RGB output format RGB555 */
+#define COM7_FMT_RGB444         0x0C /* RGB output format RGB444 */
+#define COM7_FMT_YUV            0x00 /* Output format YUV        */
+#define COM7_FMT_P_BAYER        0x01 /* Output format Processed Bayer RAW */
+#define COM7_FMT_RGB            0x02 /* Output format RGB        */
+#define COM7_FMT_R_BAYER        0x03 /* Output format Bayer RAW  */
+#define COM7_SET_FMT(r, x)      ((r&0xFC)|((x&0x3)<<0))
+#define COM7_SET_RGB(r, x)      ((r&0xF0)|(x&0x0C)|COM7_FMT_RGB)
+
+#define COM8                    0x13 /* Common Control 8                */
+#define COM8_FAST_AUTO          0x80 /* Enable fast AGC/AEC algorithm   */
+#define COM8_STEP_VSYNC         0x00 /* AEC - Step size limited to vertical blank */
+#define COM8_STEP_UNLIMIT       0x40 /* AEC - Step size unlimited step size       */
+#define COM8_BANDF_EN           0x20 /* Banding filter ON/OFF */
+#define COM8_AEC_BANDF          0x10 /* Enable AEC below banding value */
+#define COM8_AEC_FINE_EN        0x08 /* Fine AEC ON/OFF control */
+#define COM8_AGC_EN             0x04 /* AGC Enable */
+#define COM8_AWB_EN             0x02 /* AWB Enable */
+#define COM8_AEC_EN             0x01 /* AEC Enable */
+#define COM8_SET_AGC(r, x)      ((r&0xFB)|((x&0x1)<<2))
+#define COM8_SET_AWB(r, x)      ((r&0xFD)|((x&0x1)<<1))
+#define COM8_SET_AEC(r, x)      ((r&0xFE)|((x&0x1)<<0))
+
+#define COM9                    0x14 /* Common Control 9 */
+#define COM9_HISTO_AVG          0x80 /* Histogram or average based AEC/AGC selection */
+#define COM9_AGC_GAIN_2x        0x00 /* Automatic Gain Ceiling 2x  */
+#define COM9_AGC_GAIN_4x        0x10 /* Automatic Gain Ceiling 4x  */
+#define COM9_AGC_GAIN_8x        0x20 /* Automatic Gain Ceiling 8x  */
+#define COM9_AGC_GAIN_16x       0x30 /* Automatic Gain Ceiling 16x */
+#define COM9_AGC_GAIN_32x       0x40 /* Automatic Gain Ceiling 32x */
+#define COM9_DROP_VSYNC         0x04 /* Drop VSYNC output of corrupt frame */
+#define COM9_DROP_HREF          0x02 /* Drop HREF output of corrupt frame  */
+#define COM9_SET_AGC(r, x)      ((r&0x8F)|((x&0x07)<<4))
+
+#define COM10                   0x15 /* Common Control 10 */
+#define COM10_NEGATIVE          0x80 /* Output negative data */
+#define COM10_HSYNC_EN          0x40 /* HREF changes to HSYNC */
+#define COM10_PCLK_FREE         0x00 /* PCLK output option: free running PCLK */
+#define COM10_PCLK_MASK         0x20 /* PCLK output option: masked during horizontal blank  */
+#define COM10_PCLK_REV          0x10 /* PCLK reverse */
+#define COM10_HREF_REV          0x08 /* HREF reverse */
+#define COM10_VSYNC_FALLING     0x00 /* VSYNC changes on falling edge of PCLK */
+#define COM10_VSYNC_RISING      0x04 /* VSYNC changes on rising edge of PCLK */
+#define COM10_VSYNC_NEG         0x02 /* VSYNC negative */
+#define COM10_OUT_RANGE_8       0x01 /* Output data range: Full range */
+#define COM10_OUT_RANGE_10      0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */
+
+#define REG16                   0x16 /* Register 16 */
+#define REG16_BIT_SHIFT         0x80 /* Bit shift test pattern options */
+#define HSTART                  0x17 /* Horizontal Frame (HREF column) Start 8 MSBs (2 LSBs are at HREF[5:4]) */
+#define HSIZE                   0x18 /* Horizontal Sensor Size (2 LSBs are at HREF[1:0]) */
+#define VSTART                  0x19 /* Vertical Frame (row) Start 8 MSBs (1 LSB is at HREF[6]) */
+#define VSIZE                   0x1A /* Vertical Sensor Size (1 LSB is at HREF[2]) */
+#define PSHFT                   0x1B /* Data Format - Pixel Delay Select */
+#define REG_MIDH                    0x1C /* Manufacturer ID Byte – High */
+#define REG_MIDL                    0x1D /* Manufacturer ID Byte – Low */
+#define LAEC                    0x1F /* Fine AEC Value - defines exposure value less than one row period */
+
+#define COM11                   0x20 /* Common Control 11 */
+#define COM11_SNGL_FRAME_EN     0x02 /* Single frame ON/OFF selection */
+#define COM11_SNGL_XFR_TRIG     0x01 /* Single frame transfer trigger */
+
+#define BDBASE                  0x22 /* Banding Filter Minimum AEC Value */
+#define DBSTEP                  0x23 /* Banding Filter Maximum Step */
+#define AEW                     0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */
+#define AEB                     0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */
+#define VPT                     0x26 /* AGC/AEC Fast Mode Operating Region */
+#define REG28                   0x28 /* Selection on the number of dummy rows, N */
+#define HOUTSIZE                0x29 /* Horizontal Data Output Size MSBs (2 LSBs at register EXHCH[1:0]) */
+#define EXHCH                   0x2A /* Dummy Pixel Insert MSB */
+#define EXHCL                   0x2B /* Dummy Pixel Insert LSB */
+#define VOUTSIZE                0x2C /* Vertical Data Output Size MSBs (LSB at register EXHCH[2])       */
+#define ADVFL                   0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row)  */
+#define ADVFH                   0x2E /* MSB of Insert Dummy Rows in Vertical Sync */
+#define YAVE                    0x2F /* Y/G Channel Average Value */
+#define LUMHTH                  0x30 /* Histogram AEC/AGC Luminance High Level Threshold */
+#define LUMLTH                  0x31 /* Histogram AEC/AGC Luminance Low Level Threshold  */
+#define HREF                    0x32 /* Image Start and Size Control */
+#define DM_LNL                  0x33 /* Dummy Row Low 8 Bits  */
+#define DM_LNH                  0x34 /* Dummy Row High 8 Bits */
+#define ADOFF_B                 0x35 /* AD Offset Compensation Value for B Channel  */
+#define ADOFF_R                 0x36 /* AD Offset Compensation Value for R Channel  */
+#define ADOFF_GB                0x37 /* AD Offset Compensation Value for GB Channel */
+#define ADOFF_GR                0x38 /* AD Offset Compensation Value for GR Channel */
+#define OFF_B                   0x39 /* AD Offset Compensation Value for B Channel  */
+#define OFF_R                   0x3A /* AD Offset Compensation Value for R Channel  */
+#define OFF_GB                  0x3B /* AD Offset Compensation Value for GB Channel */
+#define OFF_GR                  0x3C /* AD Offset Compensation Value for GR Channel */
+#define COM12                   0x3D /* DC offset compensation for analog process */
+
+#define COM13                   0x3E /* Common Control 13 */
+#define COM13_BLC_EN            0x80 /* BLC enable */
+#define COM13_ADC_EN            0x40 /* ADC channel BLC ON/OFF control */
+#define COM13_ANALOG_BLC        0x20 /* Analog processing channel BLC ON/OFF control */
+#define COM13_ABLC_GAIN_EN      0x04 /* ABLC gain trigger enable */
+
+#define COM14                   0x3F /* Common Control 14 */
+#define COM15                   0x40 /* Common Control 15 */
+#define COM16                   0x41 /* Common Control 16 */
+#define TGT_B                   0x42 /* BLC Blue Channel Target Value   */
+#define TGT_R                   0x43 /* BLC Red Channel Target Value    */
+#define TGT_GB                  0x44 /* BLC Gb Channel Target Value     */
+#define TGT_GR                  0x45 /* BLC Gr Channel Target Value     */
+
+#define LC_CTR                  0x46 /* Lens Correction Control */
+#define LC_CTR_RGB_COMP_1       0x00 /* R, G, and B channel compensation coefficient is set by LC_COEF (0x49) */
+#define LC_CTR_RGB_COMP_3       0x04 /* R, G, and B channel compensation coefficient is set by registers
+                                        LC_COEFB (0x4B), LC_COEF (0x49), and LC_COEFR (0x4C), respectively */
+#define LC_CTR_EN               0x01 /* Lens correction enable */
+#define LC_XC                   0x47 /* X Coordinate of Lens Correction Center Relative to Array Center */
+#define LC_YC                   0x48 /* Y Coordinate of Lens Correction Center Relative to Array Center */
+#define LC_COEF                 0x49 /* Lens Correction Coefficient */
+#define LC_RADI                 0x4A /* Lens Correction Radius */
+#define LC_COEFB                0x4B /* Lens Correction B Channel Compensation Coefficient */
+#define LC_COEFR                0x4C /* Lens Correction R Channel Compensation Coefficient */
+
+#define FIXGAIN                 0x4D /* Analog Fix Gain Amplifier */
+#define AREF0                   0x4E /* Sensor Reference Control */
+#define AREF1                   0x4F /* Sensor Reference Current Control */
+#define AREF2                   0x50 /* Analog Reference Control */
+#define AREF3                   0x51 /* ADC Reference Control */
+#define AREF4                   0x52 /* ADC Reference Control */
+#define AREF5                   0x53 /* ADC Reference Control */
+#define AREF6                   0x54 /* Analog Reference Control */
+#define AREF7                   0x55 /* Analog Reference Control */
+#define UFIX                    0x60 /* U Channel Fixed Value Output */
+#define VFIX                    0x61 /* V Channel Fixed Value Output */
+#define AWBB_BLK                0x62 /* AWB Option for Advanced AWB  */
+
+#define AWB_CTRL0               0x63 /* AWB Control Byte 0   */
+#define AWB_CTRL0_GAIN_EN       0x80 /* AWB gain enable      */
+#define AWB_CTRL0_CALC_EN       0x40 /* AWB calculate enable */
+#define AWB_CTRL0_WBC_MASK      0x0F /* WBC threshold 2      */
+
+#define DSP_CTRL1               0x64 /* DSP Control Byte 1                  */
+#define DSP_CTRL1_FIFO_EN       0x80 /* FIFO enable/disable selection       */
+#define DSP_CTRL1_UV_EN         0x40 /* UV adjust function ON/OFF selection */
+#define DSP_CTRL1_SDE_EN        0x20 /* SDE enable                          */
+#define DSP_CTRL1_MTRX_EN       0x10 /* Color matrix ON/OFF selection       */
+#define DSP_CTRL1_INTRP_EN      0x08 /* Interpolation ON/OFF selection      */
+#define DSP_CTRL1_GAMMA_EN      0x04 /* Gamma function ON/OFF selection     */
+#define DSP_CTRL1_BLACK_EN      0x02 /* Black defect auto correction ON/OFF */
+#define DSP_CTRL1_WHITE_EN      0x01 /* White defect auto correction ON/OFF */
+
+#define DSP_CTRL2               0x65 /* DSP Control Byte 2          */
+#define DSP_CTRL2_VDCW_EN       0x08 /* Vertical DCW enable         */
+#define DSP_CTRL2_HDCW_EN       0x04 /* Horizontal DCW enable       */
+#define DSP_CTRL2_VZOOM_EN      0x02 /* Vertical zoom out enable    */
+#define DSP_CTRL2_HZOOM_EN      0x01 /* Horizontal zoom out enable  */
+
+#define DSP_CTRL3               0x66 /* DSP Control Byte 3                      */
+#define DSP_CTRL3_UV_EN         0x80 /* UV output sequence option               */
+#define DSP_CTRL3_CBAR_EN       0x20 /* DSP color bar ON/OFF selection          */
+#define DSP_CTRL3_FIFO_EN       0x08 /* FIFO power down ON/OFF selection        */
+#define DSP_CTRL3_SCAL1_PWDN    0x04 /* Scaling module power down control 1     */
+#define DSP_CTRL3_SCAL2_PWDN    0x02 /* Scaling module power down control 2     */
+#define DSP_CTRL3_INTRP_PWDN    0x01 /* Interpolation module power down control */
+#define DSP_CTRL3_SET_CBAR(r, x)    ((r&0xDF)|((x&1)<<5))
+
+
+#define DSP_CTRL4               0x67 /* DSP Control Byte 4          */
+#define DSP_CTRL4_YUV_RGB       0x00 /* Output selection YUV or RGB */
+#define DSP_CTRL4_RAW8          0x02 /* Output selection RAW8       */
+#define DSP_CTRL4_RAW10         0x03 /* Output selection RAW10      */
+
+
+#define AWB_BIAS                0x68 /* AWB BLC Level Clip */
+#define AWB_CTRL1               0x69 /* AWB Control 1 */
+#define AWB_CTRL2               0x6A /* AWB Control 2 */
+
+#define AWB_CTRL3               0x6B /* AWB Control 3 */
+#define AWB_CTRL3_ADVANCED      0x80 /* AWB mode select - Advanced AWB */
+#define AWB_CTRL3_SIMPLE        0x00 /* AWB mode select - Simple AWB */
+
+#define AWB_CTRL4               0x6C /* AWB Control 4  */
+#define AWB_CTRL5               0x6D /* AWB Control 5  */
+#define AWB_CTRL6               0x6E /* AWB Control 6  */
+#define AWB_CTRL7               0x6F /* AWB Control 7  */
+#define AWB_CTRL8               0x70 /* AWB Control 8  */
+#define AWB_CTRL9               0x71 /* AWB Control 9  */
+#define AWB_CTRL10              0x72 /* AWB Control 10 */
+#define AWB_CTRL11              0x73 /* AWB Control 11 */
+#define AWB_CTRL12              0x74 /* AWB Control 12 */
+#define AWB_CTRL13              0x75 /* AWB Control 13 */
+#define AWB_CTRL14              0x76 /* AWB Control 14 */
+#define AWB_CTRL15              0x77 /* AWB Control 15 */
+#define AWB_CTRL16              0x78 /* AWB Control 16 */
+#define AWB_CTRL17              0x79 /* AWB Control 17 */
+#define AWB_CTRL18              0x7A /* AWB Control 18 */
+#define AWB_CTRL19              0x7B /* AWB Control 19 */
+#define AWB_CTRL20              0x7C /* AWB Control 20 */
+#define AWB_CTRL21              0x7D /* AWB Control 21 */
+#define GAM1                    0x7E /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */
+#define GAM2                    0x7F /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */
+#define GAM3                    0x80 /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */
+#define GAM4                    0x81 /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */
+#define GAM5                    0x82 /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */
+#define GAM6                    0x83 /* Gamma Curve 6th Segment Input End Point 0x30 Output Value */
+#define GAM7                    0x84 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */
+#define GAM8                    0x85 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */
+#define GAM9                    0x86 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */
+#define GAM10                   0x87 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */
+#define GAM11                   0x88 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */
+#define GAM12                   0x89 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */
+#define GAM13                   0x8A /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */
+#define GAM14                   0x8B /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */
+#define GAM15                   0x8C /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */
+#define SLOP                    0x8D /* Gamma Curve Highest Segment Slope */
+#define DNSTH                   0x8E /* De-noise Threshold */
+#define EDGE0                   0x8F /* Edge Enhancement Strength Control */
+#define EDGE1                   0x90 /* Edge Enhancement Threshold Control */
+#define DNSOFF                  0x91 /* Auto De-noise Threshold Control */
+#define EDGE2                   0x92 /* Edge Enhancement Strength Upper Limit */
+#define EDGE3                   0x93 /* Edge Enhancement Strength Upper Limit */
+#define MTX1                    0x94 /* Matrix Coefficient 1 */
+#define MTX2                    0x95 /* Matrix Coefficient 2 */
+#define MTX3                    0x96 /* Matrix Coefficient 3 */
+#define MTX4                    0x97 /* Matrix Coefficient 4 */
+#define MTX5                    0x98 /* Matrix Coefficient 5 */
+#define MTX6                    0x99 /* Matrix Coefficient 6 */
+
+#define MTX_CTRL                0x9A /* Matrix Control */
+#define MTX_CTRL_DBL_EN         0x80 /* Matrix double ON/OFF selection */
+
+#define BRIGHTNESS              0x9B /* Brightness Control */
+#define CONTRAST                0x9C /* Contrast Gain */
+#define UVADJ0                  0x9E /* Auto UV Adjust Control 0 */
+#define UVADJ1                  0x9F /* Auto UV Adjust Control 1 */
+#define SCAL0                   0xA0 /* DCW Ratio Control */
+#define SCAL1                   0xA1 /* Horizontal Zoom Out Control */
+#define SCAL2                   0xA2 /* Vertical Zoom Out Control */
+#define FIFODLYM                0xA3 /* FIFO Manual Mode Delay Control */
+#define FIFODLYA                0xA4 /* FIFO Auto Mode Delay Control */
+
+#define SDE                     0xA6 /* Special Digital Effect Control  */
+#define SDE_NEGATIVE_EN         0x40 /* Negative image enable           */
+#define SDE_GRAYSCALE_EN        0x20 /* Gray scale image enable         */
+#define SDE_V_FIXED_EN          0x10 /* V fixed value enable            */
+#define SDE_U_FIXED_EN          0x08 /* U fixed value enable            */
+#define SDE_CONT_BRIGHT_EN      0x04 /* Contrast/Brightness enable      */
+#define SDE_SATURATION_EN       0x02 /* Saturation enable               */
+#define SDE_HUE_EN              0x01 /* Hue enable                      */
+
+#define USAT                    0xA7 /* U Component Saturation Gain     */
+#define VSAT                    0xA8 /* V Component Saturation Gain     */
+#define HUECOS                  0xA9 /* Cosine value × 0x80             */
+#define HUESIN                  0xAA /* Sine value × 0x80               */
+#define SIGN_BIT                0xAB /* Sign Bit for Hue and Brightness */
+
+#define DSPAUTO                 0xAC /* DSP Auto Function ON/OFF Control */
+#define DSPAUTO_AWB_EN          0x80 /* AWB auto threshold control */
+#define DSPAUTO_DENOISE_EN      0x40 /* De-noise auto threshold control */
+#define DSPAUTO_EDGE_EN         0x20 /* Sharpness (edge enhancement) auto strength control */
+#define DSPAUTO_UV_EN           0x10 /* UV adjust auto slope control */
+#define DSPAUTO_SCAL0_EN        0x08 /* Auto scaling factor control (register SCAL0 (0xA0)) */
+#define DSPAUTO_SCAL1_EN        0x04 /* Auto scaling factor control (registers SCAL1 (0xA1 and SCAL2 (0xA2))*/
+#define SET_REG(reg, x)         (##reg_DEFAULT|x)
+#endif //__REG_REGS_H__

+ 528 - 0
std/camera/target/ll_cam.c

@@ -0,0 +1,528 @@
+// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+#include <string.h>
+#include "soc/i2s_struct.h"
+#include "esp_idf_version.h"
+#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR > 1)
+#include "hal/gpio_ll.h"
+#else
+#include "soc/gpio_periph.h"
+#define esp_rom_delay_us ets_delay_us
+static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
+{
+    if (gpio_num < 32) {
+        return (hw->in >> gpio_num) & 0x1;
+    } else {
+        return (hw->in1.data >> (gpio_num - 32)) & 0x1;
+    }
+}
+#endif
+#include "ll_cam.h"
+#include "xclk.h"
+#include "cam_hal.h"
+
+#if (ESP_IDF_VERSION_MAJOR >= 5)
+#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
+#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
+#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
+#endif
+
+static const char *TAG = "esp32 ll_cam";
+
+#define I2S_ISR_ENABLE(i) {I2S0.int_clr.i = 1;I2S0.int_ena.i = 1;}
+#define I2S_ISR_DISABLE(i) {I2S0.int_ena.i = 0;I2S0.int_clr.i = 1;}
+
+typedef union {
+    struct {
+        uint32_t sample2:8;
+        uint32_t unused2:8;
+        uint32_t sample1:8;
+        uint32_t unused1:8;
+    };
+    uint32_t val;
+} dma_elem_t;
+
+typedef enum {
+    /* camera sends byte sequence: s1, s2, s3, s4, ...
+     * fifo receives: 00 s1 00 s2, 00 s2 00 s3, 00 s3 00 s4, ...
+     */
+    SM_0A0B_0B0C = 0,
+    /* camera sends byte sequence: s1, s2, s3, s4, ...
+     * fifo receives: 00 s1 00 s2, 00 s3 00 s4, ...
+     */
+    SM_0A0B_0C0D = 1,
+    /* camera sends byte sequence: s1, s2, s3, s4, ...
+     * fifo receives: 00 s1 00 00, 00 s2 00 00, 00 s3 00 00, ...
+     */
+    SM_0A00_0B00 = 3,
+} i2s_sampling_mode_t;
+
+typedef size_t (*dma_filter_t)(uint8_t* dst, const uint8_t* src, size_t len);
+
+static i2s_sampling_mode_t sampling_mode = SM_0A00_0B00;
+
+static size_t ll_cam_bytes_per_sample(i2s_sampling_mode_t mode)
+{
+    switch(mode) {
+    case SM_0A00_0B00:
+        return 4;
+    case SM_0A0B_0B0C:
+        return 4;
+    case SM_0A0B_0C0D:
+        return 2;
+    default:
+        assert(0 && "invalid sampling mode");
+        return 0;
+    }
+}
+
+static size_t IRAM_ATTR ll_cam_dma_filter_jpeg(uint8_t* dst, const uint8_t* src, size_t len)
+{
+    const dma_elem_t* dma_el = (const dma_elem_t*)src;
+    size_t elements = len / sizeof(dma_elem_t);
+    size_t end = elements / 4;
+    // manually unrolling 4 iterations of the loop here
+    for (size_t i = 0; i < end; ++i) {
+        dst[0] = dma_el[0].sample1;
+        dst[1] = dma_el[1].sample1;
+        dst[2] = dma_el[2].sample1;
+        dst[3] = dma_el[3].sample1;
+        dma_el += 4;
+        dst += 4;
+    }
+    return elements;
+}
+
+static size_t IRAM_ATTR ll_cam_dma_filter_grayscale(uint8_t* dst, const uint8_t* src, size_t len)
+{
+    const dma_elem_t* dma_el = (const dma_elem_t*)src;
+    size_t elements = len / sizeof(dma_elem_t);
+    size_t end = elements / 4;
+    for (size_t i = 0; i < end; ++i) {
+        // manually unrolling 4 iterations of the loop here
+        dst[0] = dma_el[0].sample1;
+        dst[1] = dma_el[1].sample1;
+        dst[2] = dma_el[2].sample1;
+        dst[3] = dma_el[3].sample1;
+        dma_el += 4;
+        dst += 4;
+    }
+    return elements;
+}
+
+static size_t IRAM_ATTR ll_cam_dma_filter_grayscale_highspeed(uint8_t* dst, const uint8_t* src, size_t len)
+{
+    const dma_elem_t* dma_el = (const dma_elem_t*)src;
+    size_t elements = len / sizeof(dma_elem_t);
+    size_t end = elements / 8;
+    for (size_t i = 0; i < end; ++i) {
+        // manually unrolling 4 iterations of the loop here
+        dst[0] = dma_el[0].sample1;
+        dst[1] = dma_el[2].sample1;
+        dst[2] = dma_el[4].sample1;
+        dst[3] = dma_el[6].sample1;
+        dma_el += 8;
+        dst += 4;
+    }
+    // the final sample of a line in SM_0A0B_0B0C sampling mode needs special handling
+    if ((elements & 0x7) != 0) {
+        dst[0] = dma_el[0].sample1;
+        dst[1] = dma_el[2].sample1;
+        elements += 1;
+    }
+    return elements / 2;
+}
+
+static size_t IRAM_ATTR ll_cam_dma_filter_yuyv(uint8_t* dst, const uint8_t* src, size_t len)
+{
+    const dma_elem_t* dma_el = (const dma_elem_t*)src;
+    size_t elements = len / sizeof(dma_elem_t);
+    size_t end = elements / 4;
+    for (size_t i = 0; i < end; ++i) {
+        dst[0] = dma_el[0].sample1;//y0
+        dst[1] = dma_el[0].sample2;//u
+        dst[2] = dma_el[1].sample1;//y1
+        dst[3] = dma_el[1].sample2;//v
+
+        dst[4] = dma_el[2].sample1;//y0
+        dst[5] = dma_el[2].sample2;//u
+        dst[6] = dma_el[3].sample1;//y1
+        dst[7] = dma_el[3].sample2;//v
+        dma_el += 4;
+        dst += 8;
+    }
+    return elements * 2;
+}
+
+static size_t IRAM_ATTR ll_cam_dma_filter_yuyv_highspeed(uint8_t* dst, const uint8_t* src, size_t len)
+{
+    const dma_elem_t* dma_el = (const dma_elem_t*)src;
+    size_t elements = len / sizeof(dma_elem_t);
+    size_t end = elements / 8;
+    for (size_t i = 0; i < end; ++i) {
+        dst[0] = dma_el[0].sample1;//y0
+        dst[1] = dma_el[1].sample1;//u
+        dst[2] = dma_el[2].sample1;//y1
+        dst[3] = dma_el[3].sample1;//v
+
+        dst[4] = dma_el[4].sample1;//y0
+        dst[5] = dma_el[5].sample1;//u
+        dst[6] = dma_el[6].sample1;//y1
+        dst[7] = dma_el[7].sample1;//v
+        dma_el += 8;
+        dst += 8;
+    }
+    if ((elements & 0x7) != 0) {
+        dst[0] = dma_el[0].sample1;//y0
+        dst[1] = dma_el[1].sample1;//u
+        dst[2] = dma_el[2].sample1;//y1
+        dst[3] = dma_el[2].sample2;//v
+        elements += 4;
+    }
+    return elements;
+}
+
+static void IRAM_ATTR ll_cam_vsync_isr(void *arg)
+{
+    //DBG_PIN_SET(1);
+    cam_obj_t *cam = (cam_obj_t *)arg;
+    BaseType_t HPTaskAwoken = pdFALSE;
+    // filter
+    ets_delay_us(1);
+    if (gpio_ll_get_level(&GPIO, cam->vsync_pin) == !cam->vsync_invert) {
+        ll_cam_send_event(cam, CAM_VSYNC_EVENT, &HPTaskAwoken);
+        if (HPTaskAwoken == pdTRUE) {
+            portYIELD_FROM_ISR();
+        }
+    }
+    //DBG_PIN_SET(0);
+}
+
+static void IRAM_ATTR ll_cam_dma_isr(void *arg)
+{
+    //DBG_PIN_SET(1);
+    cam_obj_t *cam = (cam_obj_t *)arg;
+    BaseType_t HPTaskAwoken = pdFALSE;
+
+    typeof(I2S0.int_st) status = I2S0.int_st;
+    if (status.val == 0) {
+        return;
+    }
+
+    I2S0.int_clr.val = status.val;
+
+    if (status.in_suc_eof) {
+        ll_cam_send_event(cam, CAM_IN_SUC_EOF_EVENT, &HPTaskAwoken);
+    }
+    if (HPTaskAwoken == pdTRUE) {
+        portYIELD_FROM_ISR();
+    }
+    //DBG_PIN_SET(0);
+}
+
+bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
+{
+    I2S0.conf.rx_start = 0;
+    I2S_ISR_DISABLE(in_suc_eof);
+    I2S0.in_link.stop = 1;
+    return true;
+}
+
+esp_err_t ll_cam_deinit(cam_obj_t *cam)
+{
+    gpio_isr_handler_remove(cam->vsync_pin);
+
+    if (cam->cam_intr_handle) {
+        esp_intr_free(cam->cam_intr_handle);
+        cam->cam_intr_handle = NULL;
+    }
+
+    return ESP_OK;
+}
+
+bool ll_cam_start(cam_obj_t *cam, int frame_pos)
+{
+    I2S0.conf.rx_start = 0;
+
+    I2S_ISR_ENABLE(in_suc_eof);
+
+    I2S0.conf.rx_reset = 1;
+    I2S0.conf.rx_reset = 0;
+    I2S0.conf.rx_fifo_reset = 1;
+    I2S0.conf.rx_fifo_reset = 0;
+    I2S0.lc_conf.in_rst = 1;
+    I2S0.lc_conf.in_rst = 0;
+    I2S0.lc_conf.ahbm_fifo_rst = 1;
+    I2S0.lc_conf.ahbm_fifo_rst = 0;
+    I2S0.lc_conf.ahbm_rst = 1;
+    I2S0.lc_conf.ahbm_rst = 0;
+
+    I2S0.rx_eof_num = cam->dma_half_buffer_size / sizeof(dma_elem_t);
+    I2S0.in_link.addr = ((uint32_t)&cam->dma[0]) & 0xfffff;
+
+    I2S0.in_link.start = 1;
+    I2S0.conf.rx_start = 1;
+    return true;
+}
+
+esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
+{
+    // Enable and configure I2S peripheral
+    periph_module_enable(PERIPH_I2S0_MODULE);
+
+    I2S0.conf.rx_reset = 1;
+    I2S0.conf.rx_reset = 0;
+    I2S0.conf.rx_fifo_reset = 1;
+    I2S0.conf.rx_fifo_reset = 0;
+    I2S0.lc_conf.in_rst = 1;
+    I2S0.lc_conf.in_rst = 0;
+    I2S0.lc_conf.ahbm_fifo_rst = 1;
+    I2S0.lc_conf.ahbm_fifo_rst = 0;
+    I2S0.lc_conf.ahbm_rst = 1;
+    I2S0.lc_conf.ahbm_rst = 0;
+
+    I2S0.conf.rx_slave_mod = 1;
+    I2S0.conf.rx_right_first = 0;
+    I2S0.conf.rx_msb_right = 0;
+    I2S0.conf.rx_msb_shift = 0;
+    I2S0.conf.rx_mono = 0;
+    I2S0.conf.rx_short_sync = 0;
+
+    I2S0.conf2.lcd_en = 1;
+    I2S0.conf2.camera_en = 1;
+
+    // Configure clock divider
+    I2S0.clkm_conf.clkm_div_a = 0;
+    I2S0.clkm_conf.clkm_div_b = 0;
+    I2S0.clkm_conf.clkm_div_num = 2;
+    
+    I2S0.fifo_conf.dscr_en = 1;
+    I2S0.fifo_conf.rx_fifo_mod = sampling_mode;
+    I2S0.fifo_conf.rx_fifo_mod_force_en = 1;
+
+    I2S0.conf_chan.rx_chan_mod = 1;
+    I2S0.sample_rate_conf.rx_bits_mod = 0;
+    I2S0.timing.val = 0;
+    I2S0.timing.rx_dsync_sw = 1;
+
+    return ESP_OK;
+}
+
+void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
+{
+    if (en) {
+        gpio_intr_enable(cam->vsync_pin);
+    } else {
+        gpio_intr_disable(cam->vsync_pin);
+    }
+}
+
+esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config)
+{
+    gpio_config_t io_conf = {0};
+    io_conf.intr_type = cam->vsync_invert ? GPIO_PIN_INTR_NEGEDGE : GPIO_PIN_INTR_POSEDGE;
+    io_conf.pin_bit_mask = 1ULL << config->pin_vsync;
+    io_conf.mode = GPIO_MODE_INPUT;
+    io_conf.pull_up_en = 1;
+    io_conf.pull_down_en = 0;
+    gpio_config(&io_conf);
+    gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM);
+    gpio_isr_handler_add(config->pin_vsync, ll_cam_vsync_isr, cam);
+    gpio_intr_disable(config->pin_vsync);
+
+    PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_pclk], PIN_FUNC_GPIO);
+    gpio_set_direction(config->pin_pclk, GPIO_MODE_INPUT);
+    gpio_set_pull_mode(config->pin_pclk, GPIO_FLOATING);
+    gpio_matrix_in(config->pin_pclk, I2S0I_WS_IN_IDX, false);
+
+    PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_vsync], PIN_FUNC_GPIO);
+    gpio_set_direction(config->pin_vsync, GPIO_MODE_INPUT);
+    gpio_set_pull_mode(config->pin_vsync, GPIO_FLOATING);
+    gpio_matrix_in(config->pin_vsync, I2S0I_V_SYNC_IDX, false);
+
+    PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_href], PIN_FUNC_GPIO);
+    gpio_set_direction(config->pin_href, GPIO_MODE_INPUT);
+    gpio_set_pull_mode(config->pin_href, GPIO_FLOATING);
+    gpio_matrix_in(config->pin_href, I2S0I_H_SYNC_IDX, false);
+
+    int data_pins[8] = {
+        config->pin_d0, config->pin_d1, config->pin_d2, config->pin_d3, config->pin_d4, config->pin_d5, config->pin_d6, config->pin_d7,
+    };
+    for (int i = 0; i < 8; i++) {
+        PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[data_pins[i]], PIN_FUNC_GPIO);
+        gpio_set_direction(data_pins[i], GPIO_MODE_INPUT);
+        gpio_set_pull_mode(data_pins[i], GPIO_FLOATING);
+        gpio_matrix_in(data_pins[i], I2S0I_DATA_IN0_IDX + i, false);
+    }
+
+    gpio_matrix_in(0x38, I2S0I_H_ENABLE_IDX, false);
+    return ESP_OK;
+}
+
+esp_err_t ll_cam_init_isr(cam_obj_t *cam)
+{
+    return esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, ll_cam_dma_isr, cam, &cam->cam_intr_handle);
+}
+
+void ll_cam_do_vsync(cam_obj_t *cam)
+{
+}
+
+uint8_t ll_cam_get_dma_align(cam_obj_t *cam)
+{
+    return 0;
+}
+
+static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
+    size_t dma_half_buffer_max = CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX / 2 / cam->dma_bytes_per_item;
+    size_t dma_buffer_max = 2 * dma_half_buffer_max;
+    size_t node_max = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE / cam->dma_bytes_per_item;
+
+    size_t line_width = cam->width * cam->in_bytes_per_pixel;
+    size_t image_size = cam->height * line_width;
+    if (image_size > (4 * 1024 * 1024) || (line_width > dma_half_buffer_max)) {
+        ESP_LOGE(TAG, "Resolution too high");
+        return 0;
+    }
+
+    size_t node_size = node_max;
+    size_t nodes_per_line = 1;
+    size_t lines_per_node = 1;
+    size_t lines_per_half_buffer = 1;
+    size_t dma_half_buffer_min = node_max;
+    size_t dma_half_buffer = dma_half_buffer_max;
+    size_t dma_buffer_size = dma_buffer_max;
+
+    // Calculate DMA Node Size so that it's divisable by or divisor of the line width
+    if(line_width >= node_max){
+        // One or more nodes will be requied for one line
+        for(size_t i = node_max; i > 0; i=i-1){
+            if ((line_width % i) == 0) {
+                node_size = i;
+                nodes_per_line = line_width / node_size;
+                break;
+            }
+        }
+    } else {
+        // One or more lines can fit into one node
+        for(size_t i = node_max; i > 0; i=i-1){
+            if ((i % line_width) == 0) {
+                node_size = i;
+                lines_per_node = node_size / line_width;
+                while((cam->height % lines_per_node) != 0){
+                    lines_per_node = lines_per_node - 1;
+                    node_size = lines_per_node * line_width;
+                }
+                break;
+            }
+        }
+    }
+    // Calculate minimum EOF size = max(mode_size, line_size)
+    dma_half_buffer_min = node_size * nodes_per_line;
+    // Calculate max EOF size divisable by node size
+    dma_half_buffer = (dma_half_buffer_max / dma_half_buffer_min) * dma_half_buffer_min;
+    // Adjust EOF size so that height will be divisable by the number of lines in each EOF
+    lines_per_half_buffer = dma_half_buffer / line_width;
+    while((cam->height % lines_per_half_buffer) != 0){
+        dma_half_buffer = dma_half_buffer - dma_half_buffer_min;
+        lines_per_half_buffer = dma_half_buffer / line_width;
+    }
+    // Calculate DMA size
+    dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
+    
+    ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u, dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u, image_size: %u", 
+            node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node, dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item, image_size);
+
+    cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
+    cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
+    cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
+    cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
+    return 1;
+}
+
+bool ll_cam_dma_sizes(cam_obj_t *cam)
+{
+    cam->dma_bytes_per_item = ll_cam_bytes_per_sample(sampling_mode);
+    if (cam->jpeg_mode) {
+        cam->dma_half_buffer_cnt = 8;
+        cam->dma_node_buffer_size = 2048;
+        cam->dma_half_buffer_size = cam->dma_node_buffer_size * 2;
+        cam->dma_buffer_size = cam->dma_half_buffer_cnt * cam->dma_half_buffer_size;
+    } else {
+        return ll_cam_calc_rgb_dma(cam);
+    }
+    return 1;
+}
+
+static dma_filter_t dma_filter = ll_cam_dma_filter_jpeg;
+
+size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len)
+{
+    //DBG_PIN_SET(1);
+    size_t r = dma_filter(out, in, len);
+    //DBG_PIN_SET(0);
+    return r;
+}
+
+esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid)
+{
+    if (pix_format == PIXFORMAT_GRAYSCALE) {
+        if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID) {
+            if (xclk_freq_hz > 10000000) {
+                sampling_mode = SM_0A00_0B00;
+                dma_filter = ll_cam_dma_filter_yuyv_highspeed;
+            } else {
+                sampling_mode = SM_0A0B_0C0D;
+                dma_filter = ll_cam_dma_filter_yuyv;
+            }
+            cam->in_bytes_per_pixel = 1;       // camera sends Y8
+        } else {
+            if (xclk_freq_hz > 10000000 && sensor_pid != OV7725_PID) {
+                sampling_mode = SM_0A00_0B00;
+                dma_filter = ll_cam_dma_filter_grayscale_highspeed;
+            } else {
+                sampling_mode = SM_0A0B_0C0D;
+                dma_filter = ll_cam_dma_filter_grayscale;
+            }
+            cam->in_bytes_per_pixel = 2;       // camera sends YU/YV
+        }
+        cam->fb_bytes_per_pixel = 1;       // frame buffer stores Y8
+    } else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
+            if (xclk_freq_hz > 10000000 && sensor_pid != OV7725_PID) {
+                if (sensor_pid == OV7670_PID) {
+                    sampling_mode = SM_0A0B_0B0C;
+                } else {
+                    sampling_mode = SM_0A00_0B00;
+                }
+                dma_filter = ll_cam_dma_filter_yuyv_highspeed;
+            } else {
+                sampling_mode = SM_0A0B_0C0D;
+                dma_filter = ll_cam_dma_filter_yuyv;
+            }
+            cam->in_bytes_per_pixel = 2;       // camera sends YU/YV
+            cam->fb_bytes_per_pixel = 2;       // frame buffer stores YU/YV/RGB565
+    } else if (pix_format == PIXFORMAT_JPEG) {
+        cam->in_bytes_per_pixel = 1;
+        cam->fb_bytes_per_pixel = 1;
+        dma_filter = ll_cam_dma_filter_jpeg;
+        sampling_mode = SM_0A00_0B00;
+    } else {
+        ESP_LOGE(TAG, "Requested format is not supported");
+        return ESP_ERR_NOT_SUPPORTED;
+    }
+    I2S0.fifo_conf.rx_fifo_mod = sampling_mode;
+    return ESP_OK;
+}

+ 141 - 0
std/camera/target/private_include/ll_cam.h

@@ -0,0 +1,141 @@
+// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+#include "sdkconfig.h"
+#include "esp_idf_version.h"
+#if CONFIG_IDF_TARGET_ESP32
+#if ESP_IDF_VERSION_MAJOR >= 4
+#include "esp32/rom/lldesc.h"
+#else
+#include "rom/lldesc.h"
+#endif
+#elif CONFIG_IDF_TARGET_ESP32S2
+#include "esp32s2/rom/lldesc.h"
+#elif CONFIG_IDF_TARGET_ESP32S3
+#include "esp32s3/rom/lldesc.h"
+#endif
+#include "esp_log.h"
+#include "esp_camera.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/queue.h"
+#include "freertos/task.h"
+#include "freertos/semphr.h"
+
+#if __has_include("esp_private/periph_ctrl.h")
+# include "esp_private/periph_ctrl.h"
+#endif
+
+#define CAMERA_DBG_PIN_ENABLE 0
+#if CAMERA_DBG_PIN_ENABLE
+    #if CONFIG_IDF_TARGET_ESP32
+        #define DBG_PIN_NUM 26
+    #else
+        #define DBG_PIN_NUM 7
+    #endif
+    #include "hal/gpio_ll.h"
+    #define DBG_PIN_SET(v) gpio_ll_set_level(&GPIO, DBG_PIN_NUM, v)
+#else
+    #define DBG_PIN_SET(v)
+#endif
+
+#define CAM_CHECK(a, str, ret) if (!(a)) {                                          \
+        ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str);                    \
+        return (ret);                                                               \
+        }
+
+#define CAM_CHECK_GOTO(a, str, lab) if (!(a)) {                                     \
+        ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str);                    \
+        goto lab;                                                                   \
+        }
+
+#define LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE  (4092)
+
+typedef enum {
+    CAM_IN_SUC_EOF_EVENT = 0,
+    CAM_VSYNC_EVENT
+} cam_event_t;
+
+typedef enum {
+    CAM_STATE_IDLE = 0,
+    CAM_STATE_READ_BUF = 1,
+} cam_state_t;
+
+typedef struct {
+    camera_fb_t fb;
+    uint8_t en;
+    //for RGB/YUV modes
+    lldesc_t *dma;
+    size_t fb_offset;
+} cam_frame_t;
+
+typedef struct {
+    uint32_t dma_bytes_per_item;
+    uint32_t dma_buffer_size;
+    uint32_t dma_half_buffer_size;
+    uint32_t dma_half_buffer_cnt;
+    uint32_t dma_node_buffer_size;
+    uint32_t dma_node_cnt;
+    uint32_t frame_copy_cnt;
+
+    //for JPEG mode
+    lldesc_t *dma;
+    uint8_t  *dma_buffer;
+
+    cam_frame_t *frames;
+
+    QueueHandle_t event_queue;
+    QueueHandle_t frame_buffer_queue;
+    TaskHandle_t task_handle;
+    intr_handle_t cam_intr_handle;
+	
+    uint8_t dma_num;//ESP32-S3
+    intr_handle_t dma_intr_handle;//ESP32-S3
+
+    uint8_t jpeg_mode;
+    uint8_t vsync_pin;
+    uint8_t vsync_invert;
+    uint32_t frame_cnt;
+    uint32_t recv_size;
+    bool swap_data;
+    bool psram_mode;
+
+    //for RGB/YUV modes
+    uint16_t width;
+    uint16_t height;
+    uint8_t in_bytes_per_pixel;
+    uint8_t fb_bytes_per_pixel;
+    uint32_t fb_size;
+
+    cam_state_t state;
+} cam_obj_t;
+
+
+bool ll_cam_stop(cam_obj_t *cam);
+bool ll_cam_start(cam_obj_t *cam, int frame_pos);
+esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config);
+esp_err_t ll_cam_deinit(cam_obj_t *cam);
+void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en);
+esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config);
+esp_err_t ll_cam_init_isr(cam_obj_t *cam);
+void ll_cam_do_vsync(cam_obj_t *cam);
+uint8_t ll_cam_get_dma_align(cam_obj_t *cam);
+bool ll_cam_dma_sizes(cam_obj_t *cam);
+size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len);
+esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid);
+
+// implemented in cam_hal
+void ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType_t * HPTaskAwoken);

+ 64 - 0
std/camera/target/xclk.c

@@ -0,0 +1,64 @@
+#include "driver/gpio.h"
+#include "driver/ledc.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_system.h"
+#include "xclk.h"
+#include "esp_camera.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char* TAG = "camera_xclk";
+#endif
+
+static ledc_channel_t g_ledc_channel = 0;
+
+esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz)
+{
+    ledc_timer_config_t timer_conf;
+    timer_conf.duty_resolution = LEDC_TIMER_1_BIT;
+    timer_conf.freq_hz = xclk_freq_hz;
+    timer_conf.speed_mode = LEDC_LOW_SPEED_MODE;
+
+#if ESP_IDF_VERSION_MAJOR >= 4
+    timer_conf.clk_cfg = LEDC_AUTO_CLK;
+#endif
+    timer_conf.timer_num = (ledc_timer_t)ledc_timer;
+    esp_err_t err = ledc_timer_config(&timer_conf);
+    if (err != ESP_OK) {
+        ESP_LOGE(TAG, "ledc_timer_config failed for freq %d, rc=%x", xclk_freq_hz, err);
+    }
+    return err;
+}
+
+esp_err_t camera_enable_out_clock(camera_config_t* config)
+{
+    esp_err_t err = xclk_timer_conf(config->ledc_timer, config->xclk_freq_hz);
+    if (err != ESP_OK) {
+        ESP_LOGE(TAG, "ledc_timer_config failed, rc=%x", err);
+        return err;
+    }
+
+    g_ledc_channel = config->ledc_channel;
+    ledc_channel_config_t ch_conf;
+    ch_conf.gpio_num = config->pin_xclk;
+    ch_conf.speed_mode = LEDC_LOW_SPEED_MODE;
+    ch_conf.channel = config->ledc_channel;
+    ch_conf.intr_type = LEDC_INTR_DISABLE;
+    ch_conf.timer_sel = config->ledc_timer;
+    ch_conf.duty = 1;
+    ch_conf.hpoint = 0;
+    err = ledc_channel_config(&ch_conf);
+    if (err != ESP_OK) {
+        ESP_LOGE(TAG, "ledc_channel_config failed, rc=%x", err);
+        return err;
+    }
+    return ESP_OK;
+}
+
+void camera_disable_out_clock()
+{
+    ledc_stop(LEDC_LOW_SPEED_MODE, g_ledc_channel, 0);
+}

+ 0 - 227
std/inc/bc26_work.h~RF21af2a2.TMP

@@ -1,227 +0,0 @@
-#ifndef  __BC26_WORK_H__
-#define  __BC26_WORK_H__
-
-#include "define.h"
-#include "user_uart.h"
-#include "bc26.h"
-
-#define BC26_AT_TIMEOUT_COUNT																				60000
-#define BC26_OPEN_TIMEOUT																						WORK_INTERVAL*2
-
-#define BC26_Send_String																						UartSendString
-#define BC26_Send_Hex																								UartSendHex
-
-#define BC26_RST_LOW                                         				nrf_gpio_pin_write(BC26_RST, 0);
-#define BC26_RST_HIGH                                         			nrf_gpio_pin_write(BC26_RST, 1);
-
-#define BC26_PWR_LOW                                         				nrf_gpio_pin_write(BC26_PWRKEY, 0);
-#define BC26_PWR_HIGH                                         			nrf_gpio_pin_write(BC26_PWRKEY, 1);
-
-#define BC26_PSM_INT_LOW                                         		nrf_gpio_pin_write(BC26_PSM_INT, 0);
-#define BC26_PSM_INT_HIGH                                         	nrf_gpio_pin_write(BC26_PSM_INT, 1);
-
-typedef enum {
-	BC26_POWER_OFF=0,															//硬件断电
-	BC26_POWER_ON,																//硬件上电
-	BC26_BOOT,																		//硬件开机
-	BC26_RESET,																		//硬件复位															
-	BC26_NRB,																			//软件复位
-	BC26_EXIT_SLEEP,															//从休眠唤醒模块
-	BC26_QSCLK_ON,																//启动休眠
-	BC26_QSCLK_OFF,																//关闭休眠
-	BC26_QSCLK_CHECK,															//查询休眠设置
-	BC26_QPOWD,																		//模块关机/重启指令
-	BC26_NO_REG,																	//未激活			
-	BC26_AT,																			//检查AT指令是否正常
-	BC26_IPR_CHECK,																//检查波特率
-	BC26_IPR_SET,																	//设置波特率
-	BC26_ATI,																			//查询估计版本
-	BC26_ATE,																			//回显设置
-	BC26_CMEE,																		//错误日志设置
-	BC26_CPIN,																		//查询USIM卡状态
-	BC26_IMEI,																		//获取IMEI号
-	BC26_CCID,																		//获取CCID号
-	BC26_IMSI,																		//获取IMSI号
-	BC26_QATWAKEUP,																//模块从深休眠唤醒时上报URC指示20
-	BC26_QNBIOTEVENT,															//设置psm模式进入显示
-	BC26_CSCON_SET,																//使能URC上报功能
-	BC26_PSM_OFF,																	//关闭psm模式
-	BC26_PSM_CHECK,																//查询psm设置
-	BC26_PSM_SET,																	//设置psm模式
-	BC26_EDRX_CHECK,															//查询eDRX设置
-	BC26_EDRX_SET,																//设置eDRX模式
-	BC26_CBC,																			//查询模组输入电压
-	BC26_CSQ,																			//查询网络信号值
-	BC26_CGATT_CHECK,															//检查入网状态
-	BC26_CGATT_ON,																//设置入网
-	BC26_CESQ,																		//获取扩展信号质量30
-	BC26_CEREG_SET,																//设置查询基站功能
-	BC26_CEREG_CHECK,															//查询基站
-	BC26_CGDCONT_CHECK,																	//
-	BC26_CGPADDR,																	//查询IP
-	BC26_QENG,																		//查询网络服务信息
-	BC26_CFUN_CHECK,															//查询当前功能模式
-	BC26_CFUN_MINI,																//设置最小功能模式
-	BC26_CFUN_ALL,																//设置全功能模式
-	BC26_NCSEARFCN,																//清除频点
-	BC26_TIME,																		//获取时间
-	BC26_PSM_DELAY,																//等待进入PSM
-	BC26_PSM_WORKING,															//进入PSM模式
-	
-//	BC26_NCONFIG_CHECK=20,													//查询是否自动联网
-//	BC26_NCONFIG_SET=21,														//设置自动联网
-//	BC26_QREGSWT_CHECK=22,													//查询自动入电信网设置
-//	BC26_QREGSWT_SET=23,														//设置自动入电信网
-//	BC26_NCDP_CHECK=24,														//查询NB服务器设置
-//	BC26_NCDP_SET=25,															//设置NB服务器
-//	BC26_DATA_READY,															//数据准备		
-//	BC26_SEND_ACT,																//触发数据传送
-
-	/*电信*/
-	BC26_QLWSERV,																	//配置IOT平台的IP和PORT
-	BC26_QLWCONF,																	//配置注册参数
-	BC26_QLWADDOBJ_00,														//添加LwM2M对象
-	BC26_QLWADDOBJ_10,														//添加LwM2M对象
-	BC26_QLWDELOBJ,																//删除LwM2M对象
-	BC26_QLWOPEN,																	//连接IOT平台
-	BC26_QLWOPEN_DELAY,														//连接等待
-	BC26_QLWUPDATE,																//更新连接参数
-	BC26_QLWCLOSE,																//关闭连接
-	BC26_QLWDATASEND,															//发送数据
-	BC26_QLWCFG,																	//配置数据格式
-	BC26_QLWDATASTATUS,														//数据发送状态查询
-	BC26_QLWDATASEND_DELAY,												//数据发送等待
-
-	/*LwM2M*/
-	BC26_QLACFG_PLATFORM,													//配置可选注册参数
-	BC26_QLACFG_LIFTTIME,													//配置可选注册参数
-//	BC26_QLACFG_CHECK,														//查询可选注册参数
-	BC26_QLACONFIG_SET,														//配置注册参数
-//	BC26_QLACONFIG_CHECK,													//查询注册参数
-	BC26_QLAADDOBJ_00,														//添加LwM2M对象
-	BC26_QLAADDOBJ_10,														//添加LwM2M对象
-	BC26_QLADELOBJ,																//删除LwM2M对象
-	BC26_QLAREG,																	//发送注册请求
-	BC26_QLADEREG,																//发送注销请求
-	BC26_QLAUPDATE,																//发送更新请求
-	BC26_QLARECOVER,															//手动恢复LwM2M会话
-	BC26_QLASTATUS,																//查询当前LwM2M状态
-	BC26_QLAOBSRSP,																//响应订阅请求
-	BC26_QLANOTIFY,																//上报订阅资源数据
-	
-	/*oneNET*/
-	BC26_MIPLCONFIG_CHECK,												//查询OneNET bootsrap设置
-	BC26_MIPLCONFIG_SET,													//设置bootsrap
-	BC26_MIPLCREATE,															//创建通讯套件
-	BC26_MIPLDELETE,															//删除通讯套件
-	BC26_MIPLADDOBJ,															//添加对象和实例
-	BC26_MIPLDELOBJ,															//删除对象和实例
-	BC26_MIPLOPEN,																//注册
-	BC26_MIPLCLOSE,																//注销
-	BC26_ONENET_OPENING,													//OneNet平台注册中
-	BC26_ONENET_OPENED,														//OneNet平台注册成功
-	BC26_MIPLDISCOVERRSP,													//资源发现操作
-	BC95_MIPLDISCOVERRSPING,											//资源发现操作中
-	BC26_MIPLOBSERVERSP,													//订阅资源发现操作
-	BC95_MIPLOBSERVERSPING,												//订阅资源发现操作中
-	BC26_MIPLREADRSP,															//读请求操作
-	BC26_MIPLWRITERSP,														//写请求操作
-	BC26_MIPLEXECUTERSP,													//执行请求操作
-	BC26_MIPLPARAMETERRSP,												//设置请求操作
-	BC26_MIPLNOTIFY,															//数据上报
-	BC26_MIPLNOTIFING,														//数据上报中
-	BC26_MIPLUPDATE,															//更新注册
-}BC26_NET_STATUS;
-
-typedef enum
-{
-	BC26_QUEUE_TIME=0,
-	BC26_QUEUE_INT=1,
-}BC26_QUEUE_TYPE;
-
-#define ONENET_PARAM_DEFAULT_CONFIG										\
-{																											\
-	.insCount=1,																				\
-	.insBitmap="1",																			\
-	.attrCount=4,																				\
-	.actCount=1,																				\
-	.objid=3300,																				\
-	.resid_u=5751,																			\
-	.resid_d=5750,																			\
-	.resid="5751;5750",																	\
-	.resid_len=9,																				\
-}
-
-#define PSM_PARAM_DEFAULT_CONFIG											\
-{																											\
-	.stat=CPSMS_ON,																					\
-	.T3412="00100110",/*001=1小时,00110=6次,6小时*/		\
-	.T3324="00100001",/*001=1分钟,00001=1,1分钟*/				\
-}
-
-typedef void (*bc26_work_finish_func)(void);
-typedef bool (*bc26_work_continue_func)(void);
-typedef bool (*bc26_work_mcu_rst_func)(void);
-
-typedef struct {
-	bool dev_reg;
-	bool cfun_stat;
-  bool alarm_stat;
-	BC26_BAUD baud;
-	bool usim_stat;
-	bool boot_stat;
-	bool reset_stat;
-	bool psm_lock;
-	bool bc26_work;
-	bool sleep_mode;
-	uint8_t wakeup_mode;
-	bool cfun_mini_stat;
-	uint16_t bat_voltage;
-	bc26_work_finish_func finish_func;
-  bc26_work_continue_func continue_func;
-	BC26_NET_STATUS bc26_status;								//BC26模块当前工作状态
-	BC26_NET_STATUS last_bc26_status;						//执行完当前命令需返回的BC26状态
-	http_request_s http_request;								//http请求值
-	uint8_t open_stat;													//onenet open stat
-	uint8_t onenet_discover_stat;									//onenet发现资源
-	uint8_t onenet_observer_stat;									//onenet订阅请求
-	uint8_t cgatt_timeout;											//附着网络失败
-	bool net_work;															//网络正常数据工作状态
-	bool onenet_close;													//需重置onenet连接
-	uint32_t net_lifetime;											//更新操作计时
-	uint8_t rst_delay;													//重启延时
-	bool commercial_189;												//是否电信商用平台
-	bool imsi_stat;															//IMSI读取准备状态
-	bool edrx_stat;															//EDRX设置状态
-	uint8_t gpio_set_stat;											//gpio操作状态
-	uint8_t bc26_at_count;											//模块AT检查次数
-	uint8_t cfun_timeout;												//cfun操作失败次数
-	uint8_t error_count;												//返回错误次数
-	char *bc26_tx_array;												//发送数据的缓存位置
-	bool first_work;														//模组第一次工作
-	bool heart_check;
-	bool cclk_save;
-	net_type_e net_type;												//网络制式
-	bool net_type_error;												//网络制式错误
-	bool net_connect_error;											//联网错误
-	bc26_work_mcu_rst_func mcu_rst_func;				//芯片复位
-}BC26_STRUCT;
-
-void bc26_struct_Init(void);
-void bc26_timers_init(void);
-void BC26_GPIO_Init(void);
-void bc26_work_start(void);
-void bc26_work_stop(void);
-void BC26_Data_Check(char *str);
-void Set_Bc26_Queue(BC26_QUEUE_TYPE type);
-void Set_Bc26_Reg_Stat(bool stat);
-void Set_Bc26_Commercial_189(bool stat);
-void Set_Bc26_work_finish_func(bc26_work_finish_func func);
-void Set_Bc26_work_continue_func(bc26_work_continue_func func);
-void Set_Bc26_work_mcu_rst_func(bc26_work_mcu_rst_func func);
-void Set_Bc26_txAarry_byte(char *byte);
-
-extern BC26_STRUCT bc26_struct;
-extern uint32_t unixtime;
-
-#endif	//__BC26_WORK_H__

+ 0 - 224
std/inc/bc26_work.h~RFb31113.TMP

@@ -1,224 +0,0 @@
-#ifndef  __BC26_WORK_H__
-#define  __BC26_WORK_H__
-
-#include "define.h"
-#include "user_uart.h"
-#include "bc26.h"
-
-#define BC26_AT_TIMEOUT_COUNT																				60000
-#define BC26_OPEN_TIMEOUT																						WORK_INTERVAL*2
-
-#define BC26_Send_String																						UartSendString
-#define BC26_Send_Hex																								UartSendHex
-
-#define BC26_RST_LOW                                         				nrf_gpio_pin_write(BC26_RST, 0);
-#define BC26_RST_HIGH                                         			nrf_gpio_pin_write(BC26_RST, 1);
-
-#define BC26_PWR_LOW                                         				nrf_gpio_pin_write(BC26_PWRKEY, 0);
-#define BC26_PWR_HIGH                                         			nrf_gpio_pin_write(BC26_PWRKEY, 1);
-
-#define BC26_PSM_INT_LOW                                         		nrf_gpio_pin_write(BC26_PSM_INT, 0);
-#define BC26_PSM_INT_HIGH                                         	nrf_gpio_pin_write(BC26_PSM_INT, 1);
-
-typedef enum {
-	BC26_POWER_OFF=0,															//硬件断电
-	BC26_POWER_ON,																//硬件上电
-	BC26_BOOT,																		//硬件开机
-	BC26_RESET,																		//硬件复位															
-	BC26_NRB,																			//软件复位
-	BC26_EXIT_SLEEP,															//从休眠唤醒模块
-	BC26_QSCLK_ON,																//启动休眠
-	BC26_QSCLK_OFF,																//关闭休眠
-	BC26_QSCLK_CHECK,															//查询休眠设置
-	BC26_QPOWD,																		//模块关机/重启指令
-	BC26_NO_REG,																	//未激活			
-	BC26_AT,																			//检查AT指令是否正常
-	BC26_IPR_CHECK,																//检查波特率
-	BC26_IPR_SET,																	//设置波特率
-	BC26_ATI,																			//查询估计版本
-	BC26_ATE,																			//回显设置
-	BC26_CMEE,																		//错误日志设置
-	BC26_CPIN,																		//查询USIM卡状态
-	BC26_IMEI,																		//获取IMEI号
-	BC26_CCID,																		//获取CCID号
-	BC26_IMSI,																		//获取IMSI号
-	BC26_QATWAKEUP,																//模块从深休眠唤醒时上报URC指示20
-	BC26_QNBIOTEVENT,															//设置psm模式进入显示
-	BC26_CSCON_SET,																//使能URC上报功能
-	BC26_PSM_OFF,																	//关闭psm模式
-	BC26_PSM_CHECK,																//查询psm设置
-	BC26_PSM_SET,																	//设置psm模式
-	BC26_EDRX_CHECK,															//查询eDRX设置
-	BC26_EDRX_SET,																//设置eDRX模式
-	BC26_CBC,																			//查询模组输入电压
-	BC26_CSQ,																			//查询网络信号值
-	BC26_CGATT_CHECK,															//检查入网状态
-	BC26_CGATT_ON,																//设置入网
-	BC26_CESQ,																		//获取扩展信号质量30
-	BC26_CEREG_SET,																//设置查询基站功能
-	BC26_CEREG_CHECK,															//查询基站
-	BC26_COPS_CHECK,																	//
-	BC26_CGPADDR,																	//查询IP
-	BC26_QENG,																		//查询网络服务信息
-	BC26_CFUN_CHECK,															//查询当前功能模式
-	BC26_CFUN_MINI,																//设置最小功能模式
-	BC26_CFUN_ALL,																//设置全功能模式
-	BC26_NCSEARFCN,																//清除频点
-	BC26_TIME,																		//获取时间
-	BC26_PSM_DELAY,																//等待进入PSM
-	BC26_PSM_WORKING,															//进入PSM模式
-	
-//	BC26_NCONFIG_CHECK=20,													//查询是否自动联网
-//	BC26_NCONFIG_SET=21,														//设置自动联网
-//	BC26_QREGSWT_CHECK=22,													//查询自动入电信网设置
-//	BC26_QREGSWT_SET=23,														//设置自动入电信网
-//	BC26_NCDP_CHECK=24,														//查询NB服务器设置
-//	BC26_NCDP_SET=25,															//设置NB服务器
-//	BC26_DATA_READY,															//数据准备		
-//	BC26_SEND_ACT,																//触发数据传送
-
-	/*电信*/
-	BC26_QLWSERV,																	//配置IOT平台的IP和PORT
-	BC26_QLWCONF,																	//配置注册参数
-	BC26_QLWADDOBJ_00,														//添加LwM2M对象
-	BC26_QLWADDOBJ_10,														//添加LwM2M对象
-	BC26_QLWDELOBJ,																//删除LwM2M对象
-	BC26_QLWOPEN,																	//连接IOT平台
-	BC26_QLWOPEN_DELAY,														//连接等待
-	BC26_QLWUPDATE,																//更新连接参数
-	BC26_QLWCLOSE,																//关闭连接
-	BC26_QLWDATASEND,															//发送数据
-	BC26_QLWCFG,																	//配置数据格式
-	BC26_QLWDATASTATUS,														//数据发送状态查询
-	BC26_QLWDATASEND_DELAY,												//数据发送等待
-
-	/*LwM2M*/
-	BC26_QLACFG_PLATFORM,													//配置可选注册参数
-	BC26_QLACFG_LIFTTIME,													//配置可选注册参数
-//	BC26_QLACFG_CHECK,														//查询可选注册参数
-	BC26_QLACONFIG_SET,														//配置注册参数
-//	BC26_QLACONFIG_CHECK,													//查询注册参数
-	BC26_QLAADDOBJ_00,														//添加LwM2M对象
-	BC26_QLAADDOBJ_10,														//添加LwM2M对象
-	BC26_QLADELOBJ,																//删除LwM2M对象
-	BC26_QLAREG,																	//发送注册请求
-	BC26_QLADEREG,																//发送注销请求
-	BC26_QLAUPDATE,																//发送更新请求
-	BC26_QLARECOVER,															//手动恢复LwM2M会话
-	BC26_QLASTATUS,																//查询当前LwM2M状态
-	BC26_QLAOBSRSP,																//响应订阅请求
-	BC26_QLANOTIFY,																//上报订阅资源数据
-	
-	/*oneNET*/
-	BC26_MIPLCONFIG_CHECK,												//查询OneNET bootsrap设置
-	BC26_MIPLCONFIG_SET,													//设置bootsrap
-	BC26_MIPLCREATE,															//创建通讯套件
-	BC26_MIPLDELETE,															//删除通讯套件
-	BC26_MIPLADDOBJ,															//添加对象和实例
-	BC26_MIPLDELOBJ,															//删除对象和实例
-	BC26_MIPLOPEN,																//注册
-	BC26_MIPLCLOSE,																//注销
-	BC26_ONENET_OPENING,													//OneNet平台注册中
-	BC26_ONENET_OPENED,														//OneNet平台注册成功
-	BC26_MIPLDISCOVERRSP,													//资源发现操作
-	BC95_MIPLDISCOVERRSPING,											//资源发现操作中
-	BC26_MIPLOBSERVERSP,													//订阅资源发现操作
-	BC95_MIPLOBSERVERSPING,												//订阅资源发现操作中
-	BC26_MIPLREADRSP,															//读请求操作
-	BC26_MIPLWRITERSP,														//写请求操作
-	BC26_MIPLEXECUTERSP,													//执行请求操作
-	BC26_MIPLPARAMETERRSP,												//设置请求操作
-	BC26_MIPLNOTIFY,															//数据上报
-	BC26_MIPLNOTIFING,														//数据上报中
-	BC26_MIPLUPDATE,															//更新注册
-}BC26_NET_STATUS;
-
-typedef enum
-{
-	BC26_QUEUE_TIME=0,
-	BC26_QUEUE_INT=1,
-}BC26_QUEUE_TYPE;
-
-#define ONENET_PARAM_DEFAULT_CONFIG										\
-{																											\
-	.insCount=1,																				\
-	.insBitmap="1",																			\
-	.attrCount=4,																				\
-	.actCount=1,																				\
-	.objid=3300,																				\
-	.resid_u=5751,																			\
-	.resid_d=5750,																			\
-	.resid="5751;5750",																	\
-	.resid_len=9,																				\
-}
-
-#define PSM_PARAM_DEFAULT_CONFIG											\
-{																											\
-	.stat=CPSMS_ON,																					\
-	.T3412="00100110",/*001=1小时,00110=6次,6小时*/		\
-	.T3324="00100001",/*001=1分钟,00001=1,1分钟*/				\
-}
-
-typedef void (*bc26_work_finish_func)(void);
-typedef bool (*bc26_work_continue_func)(void);
-
-typedef struct {
-	bool dev_reg;
-	bool cfun_stat;
-  bool alarm_stat;
-	BC26_BAUD baud;
-	bool usim_stat;
-	bool boot_stat;
-	bool reset_stat;
-	bool psm_lock;
-	bool bc26_work;
-	bool sleep_mode;
-	uint8_t wakeup_mode;
-	bool cfun_mini_stat;
-	uint16_t bat_voltage;
-	bc26_work_finish_func finish_func;
-  bc26_work_continue_func continue_func;
-	BC26_NET_STATUS bc26_status;								//BC26模块当前工作状态
-	BC26_NET_STATUS last_bc26_status;						//执行完当前命令需返回的BC26状态
-	http_request_s http_request;								//http请求值
-	uint8_t open_stat;													//onenet open stat
-	uint8_t onenet_discover_stat;									//onenet发现资源
-	uint8_t onenet_observer_stat;									//onenet订阅请求
-	uint8_t cgatt_timeout;											//附着网络失败
-	bool net_work;															//网络正常数据工作状态
-	bool onenet_close;													//需重置onenet连接
-	uint32_t net_lifetime;											//更新操作计时
-	uint8_t rst_delay;													//重启延时
-	bool commercial_189;												//是否电信商用平台
-	bool imsi_stat;															//IMSI读取准备状态
-	bool edrx_stat;															//EDRX设置状态
-	uint8_t gpio_set_stat;											//gpio操作状态
-	uint8_t bc26_at_count;											//模块AT检查次数
-	uint8_t cfun_timeout;												//cfun操作失败次数
-	uint8_t error_count;												//返回错误次数
-	char *bc26_tx_array;												//发送数据的缓存位置
-	bool first_work;														//模组第一次工作
-	bool heart_check;
-	bool cclk_save;
-	net_type_e net_type;												//网络制式
-	bool net_type_error;												//网络制式错误
-	bool net_connect_error;											//联网错误
-}BC26_STRUCT;
-
-void bc26_struct_Init(void);
-void bc26_timers_init(void);
-void BC26_GPIO_Init(void);
-void bc26_work_start(void);
-void bc26_work_stop(void);
-void BC26_Data_Check(char *str);
-void Set_Bc26_Queue(BC26_QUEUE_TYPE type);
-void Set_Bc26_Reg_Stat(bool stat);
-void Set_Bc26_Commercial_189(bool stat);
-void Set_Bc26_work_finish_func(bc26_work_finish_func func);
-void Set_Bc26_work_continue_func(bc26_work_continue_func func);
-void Set_Bc26_txAarry_byte(char *byte);
-
-extern BC26_STRUCT bc26_struct;
-extern uint32_t unixtime;
-
-#endif	//__BC26_WORK_H__

+ 0 - 235
std/inc/bc95.h~RF188fa9f5.TMP

@@ -1,235 +0,0 @@
-#ifndef  __BC95_H__
-#define  __BC95_H__
-
-#include "boards.h"
-#include "user_uart.h"
-
-#define BC95_AT_TIMEOUT_COUNT																				60000
-#define BC95_OPEN_TIMEOUT																						12*3600
-
-#define BC95_Send_String																						UartSendString
-#define BC95_Send_Hex																								UartSendHex
-
-#define BC95_RST_LOW                                         				nrf_gpio_pin_write(BC95_RST, 0);
-#define BC95_RST_HIGH                                         			nrf_gpio_pin_write(BC95_RST, 1);
-
-#ifdef BC95_POWER
-#define	POWER_BC95_ENABLE																						nrf_gpio_pin_write(BC95_POWER, 1);
-#define	POWER_BC95_DISABLE																					nrf_gpio_pin_write(BC95_POWER, 0);
-#endif
-
-#define EVENT_BOOTSTRAP_START																				"1"
-#define EVENT_BOOTSTRAP_SUCCESS																			"2"
-#define EVENT_BOOTSTRAP_FAILED																			"3"
-#define EVENT_CONNECT_SUCCESS																				"4"
-#define EVENT_CONNECT_FAILED																				"5"
-#define EVENT_REG_SUCCESS																						"6"
-#define EVENT_REG_FAILED																						"7"
-#define EVENT_REG_TIMEOUT																						"8"
-#define EVENT_LIFETIME_TIMEOUT																			"9"
-#define EVENT_STATUS_HALT																						"10"
-#define EVENT_UPDATE_SUCCESS																				"11"
-#define EVENT_UPDATE_FAILED																					"12"
-#define EVENT_UPDATE_TIMEOUT																				"13"
-#define EVENT_UPDATE_NEED																						"14"
-#define EVENT_DEREG_DONE																						"15"
-#define EVENT_RESPONSE_FAILED																				"20"
-#define EVENT_RESPONSE_SUCCESS																			"21"
-#define EVENT_NOTIFY_FAILED																					"25"
-#define EVENT_NOTIFY_SUCCESS																				"26"
-#define EVENT_FIRMWARE_DOWNLOADING																	"40"
-#define EVENT_FIRMWARE_DOWNLOAD_FAILED															"41"
-#define EVENT_FIRMWARE_DOWNLOADED																		"42"
-#define EVENT_FIRMWARE_UPDATING																			"43"
-#define EVENT_FIRMWARE_UPDATE_SUCCESS																"44"
-#define EVENT_FIRMWARE_UPDATE_FAILED																"45"
-#define EVENT_FIRMWARE_UPDATE_OVER																	"46"
-#define EVENT_FIRMWARE_DOWNLOAD_DISCONNECT													"47"
-#define EVENT_FIRMWARE_ERASE_SUCCESS																"48"
-#define EVENT_FIRMWARE_ERASE_FAIL																		"49"
-
-#define UART_FRAME_ERROR																						"523"
-#define TUP_NOT_REG																									"513"
-
-typedef enum {
-	#ifdef BC95_POWER
-	BC95_OFF=0,																	//硬件电源关机
-	BC95_ON=1,																		//硬件电源开机
-	#endif
-	BC95_BOOT=2,																	//硬件复位
-	BC95_NRB=3,																		//软件复位
-	BC95_NO_REG=4,																//未激活
-	BC95_AT=5,																		//检查AT指令是否正常
-	BC95_NATSPEED_CHECK=6,												//检查波特率
-	BC95_NATSPEED=7,															//设置波特率
-	BC95_ATI=8,																		//查询估计版本
-	BC95_ATE=9,																		//回显设置
-	BC95_CMEE=10,																	//错误日志设置
-	BC95_NPSMR=11,																	//设置psm模式进入显示
-	BC95_PSM_CHECK=12,															//查询psm设置
-	BC95_PSM_SET=13,																//设置psm模式
-	BC95_EDRX_CHECK=14,														//查询eDRX设置
-	BC95_EDRX_SET=15,															//设置eDRX模式
-	BC95_IMEI=16,																	//获取IMEI号
-	BC95_CCID=17,																	//获取CCID号
-	BC95_IMSI=18,																	//获取IMSI号
-	BC95_CSQ=19,																		//查询网络信号值
-	BC95_NCONFIG_CHECK=20,													//查询是否自动联网
-	BC95_NCONFIG_SET=21,														//设置自动联网
-	BC95_QREGSWT_CHECK=22,													//查询自动入电信网设置
-	BC95_QREGSWT_SET=23,														//设置自动入电信网
-	BC95_NCDP_CHECK=24,														//查询NB服务器设置
-	BC95_NCDP_SET=25,															//设置NB服务器
-	BC95_CFUN_CHECK=26,														//查询当前功能模式
-	BC95_CFUN_MINI=27,															//设置最小功能模式
-	BC95_CFUN_ALL=28,															//设置全功能模式
-	BC95_NCSEARFCN=29,															//清除频点
-	BC95_CGATT_CHECK=30,														//检查入网状态
-	BC95_CGATT_ON=31,															//设置入网	
-	BC95_CEREG_SET=32,															//设置查询基站功能
-	BC95_CEREG=33,																	//查询基站
-	BC95_NUESTATS=34,															//查询RSSI,RSRP和SNR
-	BC95_ECL=35,																		//查询ECL
-	BC95_TIME=36,																	//获取时间
-	BC95_CSCON_SET=37,															//使能URC上报功能
-	BC95_DATA_READY=38,														//数据准备		
-	BC95_SEND_ACT=39,															//触发数据传送
-	BC95_PSM_DELAY=40,															//等待进入PSM
-	BC95_PSM_WORKING=41,														//进入PSM模式
-	BC95_MIPLCONFIG_CHECK=42,											//查询OneNET bootsrap设置
-	BC95_MIPLCONFIG_SET=43,												//设置bootsrap
-	BC95_MIPLCREATE=44,														//创建通讯套件
-	BC95_MIPLDELETE=45,														//删除通讯套件
-	BC95_MIPLADDOBJ=46,														//添加对象和实例
-	BC95_MIPLDELOBJ=47,														//删除对象和实例
-	BC95_MIPLOPEN=48,															//注册
-	BC95_MIPLCLOSE=49,															//注销
-	BC95_ONENET_OPENING=50,												//OneNet平台注册中
-	BC95_ONENET_OPENED=51,													//OneNet平台注册成功
-	BC95_MIPLDISCOVERRSP=52,												//资源发现操作
-	BC95_MIPLDISCOVERRSPING=53,										//资源发现操作中
-	BC95_MIPLOBSERVERSP=54,												//订阅资源发现操作
-	BC95_MIPLOBSERVERSPING=55,											//订阅资源发现操作中
-	BC95_MIPLWAITE=56,															//待网
-	BC95_MIPLREADRSP=57,														//读请求操作
-	BC95_MIPLWRITERSP=58,													//写请求操作
-	BC95_MIPLEXECUTERSP=59,												//执行请求操作
-	BC95_MIPLPARAMETERRSP=60,											//设置请求操作
-	BC95_MIPLNOTIFY=61,														//数据上报
-	BC95_MIPLNOTIFING=62,													//数据上报中
-	BC95_MIPLUPDATE=63,														//更新注册
-}BC95_NET_STATUS;
-
-typedef struct{
-	uint32_t unixtime;
-	uint16_t lac;
-	uint32_t cellid;
-	int16_t signalpower;
-	int16_t signalrssi;
-	uint8_t ecl;
-	int16_t snr;
-	uint16_t pci;
-	uint8_t imei[8];
-	uint8_t iccid[10];
-	uint8_t imsi[8];
-}BC95_DATA;
-
-typedef enum{
-	ONENET_OPEN_NULL=0,
-	ONENET_BOOTSTRAP_START,
-	ONENET_BOOTSTRAP_SUCCESS,
-	ONENET_CONNECT_SUCCESS,
-	ONENET_REG_SUCCESS,
-}ONENET_OPEN_STAT;
-
-typedef enum{
-	RESULT_204=2,						//2.04 Changed,indicating the correct result
-	RESULT_400=11,					//4.00 Bad request
-	RESULT_401=12,					//4.01 Unauthorized
-	RESULT_404=13,					//4.04 Not found
-	RESULT_405=14						//4.05 Method not allowed
-}MIPLRESULT;
-
-typedef enum{
-	MOBILE_NULL=0,
-	MOBILE_CMCC,
-	MOBILE_189
-}MOBILE_TYPE;
-
-typedef enum
-{
-	BC95_QUEUE_TIME=0,
-	BC95_QUEUE_INT=1,
-}BC95_QUEUE_TYPE;
-
-typedef void (*bc95_work_finish_func)(void);
-typedef bool (*bc95_work_continue_func)(void);
-
-typedef struct {
-	bool app_time_start;
-	bool dev_reg;
-        bool alarm_stat;
-	bc95_work_finish_func finish_func;
-        bc95_work_continue_func continue_func;
-	MOBILE_TYPE mobile_type;
-//	void (*rst_out)(uint8_t x);
-//	void (*power_out)(uint8_t x);
-//	void (*set_data)(void);
-	BC95_DATA bc95_data;												//模块相关数据
-	BC95_NET_STATUS bc95_status;								//BC95模块当前工作状态
-	BC95_NET_STATUS last_bc95_status;						//执行完当前命令需返回的BC95状态
-	bool set_natspeed;													//设置波特率状态
-	uint16_t ms_count;													//时间计时器
-	uint32_t time_check_count;									//同步时间计时 
-	uint8_t Obj_ID;															//ObjectID,目前=0
-	uint32_t msgid;															//当前MsgID
-	int ob_msgid[2];														//订阅MsgID记录
-	bool onenet_work;														//移动onenet网络正常数据工作状态
-	uint32_t last_tick;													//计时器用
-	uint32_t data_check_count;									//onenet数据上报计时
-	uint32_t discover_timeout;									//资源发现超时
-	ONENET_OPEN_STAT open_stat;									//注册状态
-	uint32_t open_lifetime;											//更新操作计时
-	uint32_t open_timeout;											//注册超时
-	uint8_t update_timeout;											//更新注册信息超时
-	MIPLRESULT miplresult;											//coap返回结果,类似http,默认204
-	bool commercial_189;												//是否电信商用平台
-	bool imsi_stat;															//IMSI读取准备状态
-	bool psm_stat;															//PSM模式设置状态
-	bool edrx_stat;															//EDRX设置状态
-	bool nconfig_stat;													//查询nconfig状态
-	bool rst_stat;															//重启状态
-	bool work_status;														//电信网络正常数据工作状态
-	uint8_t natspeed_type;											//波特率设置类型0-3
-	uint8_t naterror_count;											//波特率设置错误次数
-	uint32_t bc95_check_time;										//模块状态检查计时
-	uint8_t bc95_check_count;										//模块状态检查次数
-	uint8_t cfun_check_count;										//功能模式超时次数
-	uint8_t cfun_timeout;												//cfun操作失败次数
-	uint8_t send_check_count;										//发送数据超时次数
-	uint8_t error_count;												//返回错误次数
-//	uint8_t power_off_timeout;								//模块超时关机
-	uint8_t bc95power_status;										//BC95复位拉底判断
-	bool bc95_at_stat;													//at指令返回状态
-	uint16_t bc95_at_timeout;										//at指令返回超时
-	char * bc95_command_array[5];								//存放的指令字符的内存地址位置
-	bool bc95_command_send_act;									//AT指令发送ACT
-	char bc95_send_Byte[USER_UART_SEND_LEN];		//发送的数据
-}BC95_STRUCT;
-
-void bc95_struct_Init(void);
-void bc95_timers_init(void);
-void BC95_GPIO_Init(void);
-void bc95_work_start(void);
-void bc95_work_stop(void);
-void BC95_Data_Check(char *str);
-void Set_Bc95_Queue(BC95_QUEUE_TYPE type);
-void Set_Bc95_Reg_Stat(bool stat);
-void Set_Bc95_Commercial_189(bool stat);
-void Set_Bc95_work_finish_func(bc95_work_finish_func func);
-void Set_Bc95_work_continue_func(bc95_work_continue_func func);
-
-extern BC95_STRUCT bc95_struct;
-extern uint32_t unixtime;
-
-#endif

+ 0 - 241
std/inc/bc95.h~RF18eae9e4.TMP

@@ -1,241 +0,0 @@
-#ifndef  __BC95_H__
-#define  __BC95_H__
-
-#include "boards.h"
-#include "user_uart.h"
-
-#define BC95_AT_TIMEOUT_COUNT																				60000
-#define BC95_OPEN_TIMEOUT																						12*3600
-
-#define BC95_Send_String																						UartSendString
-#define BC95_Send_Hex																								UartSendHex
-
-#define BC95_RST_LOW                                         				nrf_gpio_pin_write(BC95_RST, 0);
-#define BC95_RST_HIGH                                         			nrf_gpio_pin_write(BC95_RST, 1);
-
-#ifdef BC95_POWER
-#define	POWER_BC95_ENABLE																						nrf_gpio_pin_write(BC95_POWER, 1);
-#define	POWER_BC95_DISABLE																					nrf_gpio_pin_write(BC95_POWER, 0);
-#endif
-
-#define EVENT_BOOTSTRAP_START																				"1"
-#define EVENT_BOOTSTRAP_SUCCESS																			"2"
-#define EVENT_BOOTSTRAP_FAILED																			"3"
-#define EVENT_CONNECT_SUCCESS																				"4"
-#define EVENT_CONNECT_FAILED																				"5"
-#define EVENT_REG_SUCCESS																						"6"
-#define EVENT_REG_FAILED																						"7"
-#define EVENT_REG_TIMEOUT																						"8"
-#define EVENT_LIFETIME_TIMEOUT																			"9"
-#define EVENT_STATUS_HALT																						"10"
-#define EVENT_UPDATE_SUCCESS																				"11"
-#define EVENT_UPDATE_FAILED																					"12"
-#define EVENT_UPDATE_TIMEOUT																				"13"
-#define EVENT_UPDATE_NEED																						"14"
-#define EVENT_DEREG_DONE																						"15"
-#define EVENT_RESPONSE_FAILED																				"20"
-#define EVENT_RESPONSE_SUCCESS																			"21"
-#define EVENT_NOTIFY_FAILED																					"25"
-#define EVENT_NOTIFY_SUCCESS																				"26"
-#define EVENT_FIRMWARE_DOWNLOADING																	"40"
-#define EVENT_FIRMWARE_DOWNLOAD_FAILED															"41"
-#define EVENT_FIRMWARE_DOWNLOADED																		"42"
-#define EVENT_FIRMWARE_UPDATING																			"43"
-#define EVENT_FIRMWARE_UPDATE_SUCCESS																"44"
-#define EVENT_FIRMWARE_UPDATE_FAILED																"45"
-#define EVENT_FIRMWARE_UPDATE_OVER																	"46"
-#define EVENT_FIRMWARE_DOWNLOAD_DISCONNECT													"47"
-#define EVENT_FIRMWARE_ERASE_SUCCESS																"48"
-#define EVENT_FIRMWARE_ERASE_FAIL																		"49"
-
-#define UART_FRAME_ERROR																						"523"
-#define TUP_NOT_REG																									"513"
-
-typedef enum {
-	#ifdef BC95_POWER
-	BC95_OFF=0,																	//硬件电源关机
-	BC95_ON=1,																		//硬件电源开机
-	#endif
-	BC95_BOOT=2,																	//硬件复位
-	BC95_NRB=3,																		//软件复位
-	BC95_NO_REG=4,																//未激活
-	BC95_AT=5,																		//检查AT指令是否正常
-	BC95_NATSPEED_CHECK=6,												//检查波特率
-	BC95_NATSPEED=7,															//设置波特率
-	BC95_ATI=8,																		//查询估计版本
-	BC95_ATE=9,																		//回显设置
-	BC95_CMEE=10,																	//错误日志设置
-	BC95_NPSMR=11,																	//设置psm模式进入显示
-	BC95_PSM_CHECK=12,															//查询psm设置
-	BC95_PSM_SET=13,																//设置psm模式
-	BC95_EDRX_CHECK=14,														//查询eDRX设置
-	BC95_EDRX_SET=15,															//设置eDRX模式
-	BC95_IMEI=16,																	//获取IMEI号
-	BC95_CCID=17,																	//获取CCID号
-	BC95_IMSI=18,																	//获取IMSI号
-	BC95_CSQ=19,																		//查询网络信号值
-	BC95_NCONFIG_CHECK=20,													//查询是否自动联网
-	BC95_NCONFIG_SET=21,														//设置自动联网
-	BC95_QREGSWT_CHECK=22,													//查询自动入电信网设置
-	BC95_QREGSWT_SET=23,														//设置自动入电信网
-	BC95_NCDP_CHECK=24,														//查询NB服务器设置
-	BC95_NCDP_SET=25,															//设置NB服务器
-	BC95_CFUN_CHECK=26,														//查询当前功能模式
-	BC95_CFUN_MINI=27,															//设置最小功能模式
-	BC95_CFUN_ALL=28,															//设置全功能模式
-	BC95_NCSEARFCN=29,															//清除频点
-	BC95_CGATT_CHECK=30,														//检查入网状态
-	BC95_CGATT_ON=31,															//设置入网	
-	BC95_CEREG_SET=32,															//设置查询基站功能
-	BC95_CEREG=33,																	//查询基站
-	BC95_NUESTATS=34,															//查询RSSI,RSRP和SNR
-	BC95_ECL=35,																		//查询ECL
-	BC95_TIME=36,																	//获取时间
-	BC95_CSCON_SET=37,															//使能URC上报功能
-	BC95_DATA_READY=38,														//数据准备		
-	BC95_SEND_ACT=39,															//触发数据传送
-	BC95_PSM_DELAY=40,															//等待进入PSM
-	BC95_PSM_WORKING=41,														//进入PSM模式
-	BC95_MIPLCONFIG_CHECK=42,											//查询OneNET bootsrap设置
-	BC95_MIPLCONFIG_SET=43,												//设置bootsrap
-	BC95_MIPLCREATE=44,														//创建通讯套件
-	BC95_MIPLDELETE=45,														//删除通讯套件
-	BC95_MIPLADDOBJ=46,														//添加对象和实例
-	BC95_MIPLDELOBJ=47,														//删除对象和实例
-	BC95_MIPLOPEN=48,															//注册
-	BC95_MIPLCLOSE=49,															//注销
-	BC95_ONENET_OPENING=50,												//OneNet平台注册中
-	BC95_ONENET_OPENED=51,													//OneNet平台注册成功
-	BC95_MIPLDISCOVERRSP=52,												//资源发现操作
-	BC95_MIPLDISCOVERRSPING=53,										//资源发现操作中
-	BC95_MIPLOBSERVERSP=54,												//订阅资源发现操作
-	BC95_MIPLOBSERVERSPING=55,											//订阅资源发现操作中
-	BC95_MIPLWAITE=56,															//待网
-	BC95_MIPLREADRSP=57,														//读请求操作
-	BC95_MIPLWRITERSP=58,													//写请求操作
-	BC95_MIPLEXECUTERSP=59,												//执行请求操作
-	BC95_MIPLPARAMETERRSP=60,											//设置请求操作
-	BC95_MIPLNOTIFY=61,														//数据上报
-	BC95_MIPLNOTIFING=62,													//数据上报中
-	BC95_MIPLUPDATE=63,														//更新注册
-}BC95_NET_STATUS;
-
-typedef struct{
-	uint32_t unixtime;
-	uint16_t lac;
-	uint32_t cellid;
-	int16_t signalpower;
-	int16_t signalrssi;
-	uint8_t ecl;
-	int16_t snr;
-	uint16_t pci;
-	uint8_t imei[8];
-	uint8_t iccid[10];
-	uint8_t imsi[8];
-}BC95_DATA;
-
-typedef enum{
-	ONENET_OPEN_NULL=0,
-	ONENET_BOOTSTRAP_START,
-	ONENET_BOOTSTRAP_SUCCESS,
-	ONENET_CONNECT_SUCCESS,
-	ONENET_REG_SUCCESS,
-}ONENET_OPEN_STAT;
-
-typedef enum{
-	RESULT_204=2,						//2.04 Changed,indicating the correct result
-	RESULT_400=11,					//4.00 Bad request
-	RESULT_401=12,					//4.01 Unauthorized
-	RESULT_404=13,					//4.04 Not found
-	RESULT_405=14						//4.05 Method not allowed
-}MIPLRESULT;
-
-typedef enum{
-	MOBILE_NULL=0,
-	MOBILE_CMCC,
-	MOBILE_189
-}MOBILE_TYPE;
-
-typedef enum{
-	IOT189_TYPE_AEP=0,
-	IOTTEST_TYPE_AEP,
-	IOT189_TYPE_COMMERCIAL,
-}IOT189_TYPE;
-
-typedef enum
-{
-	BC95_QUEUE_TIME=0,
-	BC95_QUEUE_INT=1,
-}BC95_QUEUE_TYPE;
-
-typedef void (*bc95_work_finish_func)(void);
-typedef bool (*bc95_work_continue_func)(void);
-
-typedef struct {
-	bool app_time_start;
-	bool dev_reg;
-	bool alarm_stat;
-	bc95_work_finish_func finish_func;
-	bc95_work_continue_func continue_func;
-	MOBILE_TYPE mobile_type;
-//	void (*rst_out)(uint8_t x);
-//	void (*power_out)(uint8_t x);
-//	void (*set_data)(void);
-	BC95_DATA bc95_data;												//模块相关数据
-	BC95_NET_STATUS bc95_status;								//BC95模块当前工作状态
-	BC95_NET_STATUS last_bc95_status;						//执行完当前命令需返回的BC95状态
-	bool set_natspeed;													//设置波特率状态
-	uint16_t ms_count;													//时间计时器
-	uint32_t time_check_count;									//同步时间计时 
-	uint8_t Obj_ID;															//ObjectID,目前=0
-	uint32_t msgid;															//当前MsgID
-	int ob_msgid[2];														//订阅MsgID记录
-	bool onenet_work;														//移动onenet网络正常数据工作状态
-	uint32_t last_tick;													//计时器用
-	uint32_t data_check_count;									//onenet数据上报计时
-	uint32_t discover_timeout;									//资源发现超时
-	ONENET_OPEN_STAT open_stat;									//注册状态
-	uint32_t open_lifetime;											//更新操作计时
-	uint32_t open_timeout;											//注册超时
-	uint8_t update_timeout;											//更新注册信息超时
-	MIPLRESULT miplresult;											//coap返回结果,类似http,默认204
-	IOT189_TYPE iot189type;											//电信平台类型
-	bool imsi_stat;															//IMSI读取准备状态
-	bool psm_stat;															//PSM模式设置状态
-	bool edrx_stat;															//EDRX设置状态
-	bool nconfig_stat;													//查询nconfig状态
-	bool rst_stat;															//重启状态
-	bool work_status;														//电信网络正常数据工作状态
-	uint8_t natspeed_type;											//波特率设置类型0-3
-	uint8_t naterror_count;											//波特率设置错误次数
-	uint32_t bc95_check_time;										//模块状态检查计时
-	uint8_t bc95_check_count;										//模块状态检查次数
-	uint8_t cfun_check_count;										//功能模式超时次数
-	uint8_t cfun_timeout;												//cfun操作失败次数
-	uint8_t send_check_count;										//发送数据超时次数
-	uint8_t error_count;												//返回错误次数
-//	uint8_t power_off_timeout;								//模块超时关机
-	uint8_t bc95power_status;										//BC95复位拉底判断
-	bool bc95_at_stat;													//at指令返回状态
-	uint16_t bc95_at_timeout;										//at指令返回超时
-	char * bc95_command_array[5];								//存放的指令字符的内存地址位置
-	bool bc95_command_send_act;									//AT指令发送ACT
-	char bc95_send_Byte[USER_UART_SEND_LEN];		//发送的数据
-}BC95_STRUCT;
-
-void bc95_struct_Init(void);
-void bc95_timers_init(void);
-void BC95_GPIO_Init(void);
-void bc95_work_start(void);
-void bc95_work_stop(void);
-void BC95_Data_Check(char *str);
-void Set_Bc95_Queue(BC95_QUEUE_TYPE type);
-void Set_Bc95_Reg_Stat(bool stat);
-void Set_Bc95_Commercial_189(bool stat);
-void Set_Bc95_work_finish_func(bc95_work_finish_func func);
-void Set_Bc95_work_continue_func(bc95_work_continue_func func);
-
-extern BC95_STRUCT bc95_struct;
-extern uint32_t unixtime;
-
-#endif

+ 5 - 7
std/inc/user_uart.h → std/inc/nrf_usr_uart.h

@@ -1,5 +1,5 @@
-#ifndef USER_UART_H
-#define USER_UART_H
+#ifndef NRF_USR_UART_H
+#define NRF_USR_UART_H
 
 #ifdef __cplusplus
 extern "C" {
@@ -10,10 +10,8 @@ extern "C" {
 #include "nrf_uart.h"
 #include "app_uart.h"
 
-#define UART_TX_BUF_SIZE 																		1024       //串口发送缓存大小(字节数)
-#define UART_RX_BUF_SIZE 																		1       //串口接收缓存大小(字节数)
-#define USER_UART_REBYTE_LEN                                512
-#define USER_UART_SEND_LEN                                  512
+#define UART_TX_BUF_SIZE 																		USER_UART_SEND_LEN       	//串口发送缓存大小(字节数)
+#define UART_RX_BUF_SIZE 																		1       									//串口接收缓存大小(字节数)
 	
 typedef void (*modbus_rec_func)(uint8_t *data,uint8_t *len,bool *stat);
 typedef void (*uart_rec_func)(char *data);
@@ -84,4 +82,4 @@ extern user_uart_s usr_uart;
 }
 #endif
 
-#endif // USER_UART_H
+#endif // NRF_USR_UART_H

+ 2 - 2
std/src/user_uart.c → std/src/nrf_usr_uart.c

@@ -1,4 +1,4 @@
-#include "user_uart.h"
+#include "nrf_usr_uart.h"
 #include "_string.h"
 #include "app_scheduler.h"
 
@@ -36,7 +36,7 @@ user_uart_s usr_uart={
   * @param  None
   * @retval None
   */
-void UartSendChar(uint8_t ch)
+static void UartSendChar(uint8_t ch)
 {
 	uint32_t err_code;
 	if(usr_uart.uart_init_stat!=true)