diff --git a/README.md b/README.md
index 6acec1bb..12f75f88 100644
--- a/README.md
+++ b/README.md
@@ -54,24 +54,35 @@ In other cases you can contact the developer via email:
RGB565/555
8-bit compressed data
8/10-bit Raw RGB data | 1/4" |
+| OV3660 | 2048 x 1536 | color | raw RGB data
RGB565/555/444
CCIR656
YCbCr422
compression | 1/5" |
+| OV5640 | 2592 x 1944 | color | RAW RGB
RGB565/555/444
CCIR656
YUV422/420
YCbCr422
compression | 1/4" |
+| OV7670 | 640 x 480 | color | Raw Bayer RGB
Processed Bayer RGB
YUV/YCbCr422
GRB422
RGB565/555 | 1/6" |
+| OV7725 | 640 x 480 | color | Raw RGB
GRB 422
RGB565/555/444
YCbCr 422 | 1/4" |
+| NT99141 | 1280 x 720 | color | YCbCr 422
RGB565/555/444
Raw
CCIR656
JPEG compression | 1/4" |
+| GC032A | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/10" |
+| GC0308 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/6.5" |
+| GC2145 | 1600 x 1200 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/5" |
## Important to Remember
@@ -17,7 +38,7 @@ This repository hosts ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670
### Using esp-idf
- Clone or download and extract the repository to the components folder of your ESP-IDF project
-- Enable PSRAM in `menuconfig`
+- Enable PSRAM in `menuconfig` (also set Flash and PSRAM frequiencies to 80MHz)
- Include `esp_camera.h` in your code
### Using PlatformIO
@@ -75,17 +96,6 @@ However with a bit of patience and experimenting you'll figure the Kconfig out.
If you miss-skip-ignore this critical step the camera module will compile but camera logic inside the library will be 'empty' because the Kconfig sets the proper #ifdef statements during the build process to initialize the selected cameras. It's very not optional!
-### Kconfig options
-
-| config | description | default |
-| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------ |
-| CONFIG_OV2640_SUPPORT | Support for OV2640 camera | enabled |
-| CONFIG_OV7725_SUPPORT | Support for OV7725 camera | disabled |
-| CONFIG_OV3660_SUPPORT | Support for OV3660 camera | enabled |
-| CONFIG_OV5640_SUPPORT | Support for OV5640 camera | enabled |
-| CONFIG_SCCB_HARDWARE_I2C | Enable this option if you want to use hardware I2C to control the camera. Disable this option to use software I2C. | enabled |
-| CONFIG_SCCB_HARDWARE_I2C_PORT | I2C peripheral to use for SCCB. Can be I2C0 and I2C1. | CONFIG_SCCB_HARDWARE_I2C_PORT1 |
-| CONFIG_CAMERA_TASK_PINNED_TO_CORE | Pin the camera handle task to a certain core(0/1). It can also be done automatically choosing NO_AFFINITY. Can be CAMERA_CORE0, CAMERA_CORE1 or NO_AFFINITY. | CONFIG_CAMERA_CORE0 |
## Examples
@@ -132,8 +142,7 @@ static camera_config_t camera_config = {
.pin_href = CAM_PIN_HREF,
.pin_pclk = CAM_PIN_PCLK,
- //XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
- .xclk_freq_hz = 20000000,
+ .xclk_freq_hz = 20000000,//EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
@@ -141,7 +150,8 @@ static camera_config_t camera_config = {
.frame_size = FRAMESIZE_UXGA,//QQVGA-QXGA Do not use sizes above QVGA when not JPEG
.jpeg_quality = 12, //0-63 lower number means higher quality
- .fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
+ .fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
+ .grab_mode = CAMERA_GRAB_WHEN_EMPTY//CAMERA_GRAB_LATEST. Sets when buffers should be filled
};
esp_err_t camera_init(){
diff --git a/code/components/esp32-camera-master/component.mk b/code/components/esp32-camera-master/component.mk
index e4ffee18..8db15eb8 100644
--- a/code/components/esp32-camera-master/component.mk
+++ b/code/components/esp32-camera-master/component.mk
@@ -1,4 +1,4 @@
COMPONENT_ADD_INCLUDEDIRS := driver/include conversions/include
-COMPONENT_PRIV_INCLUDEDIRS := driver/private_include conversions/private_include sensors/private_include
-COMPONENT_SRCDIRS := driver conversions sensors
+COMPONENT_PRIV_INCLUDEDIRS := driver/private_include conversions/private_include sensors/private_include target/private_include
+COMPONENT_SRCDIRS := driver conversions sensors target target/esp32
CXXFLAGS += -fno-rtti
diff --git a/code/components/esp32-camera-master/conversions/esp_jpg_decode.c b/code/components/esp32-camera-master/conversions/esp_jpg_decode.c
index d42794fc..a9615e36 100644
--- a/code/components/esp32-camera-master/conversions/esp_jpg_decode.c
+++ b/code/components/esp32-camera-master/conversions/esp_jpg_decode.c
@@ -17,7 +17,11 @@
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/tjpgd.h"
-#else
+#elif CONFIG_IDF_TARGET_ESP32S2
+#include "tjpgd.h"
+#elif CONFIG_IDF_TARGET_ESP32S3
+#include "esp32s3/rom/tjpgd.h"
+#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
diff --git a/code/components/esp32-camera-master/conversions/include/img_converters.h b/code/components/esp32-camera-master/conversions/include/img_converters.h
index 330f8db8..f736200a 100644
--- a/code/components/esp32-camera-master/conversions/include/img_converters.h
+++ b/code/components/esp32-camera-master/conversions/include/img_converters.h
@@ -22,6 +22,7 @@ extern "C" {
#include
#include
#include "esp_camera.h"
+#include "esp_jpg_decode.h"
typedef size_t (* jpg_out_cb)(void * arg, size_t index, const void* data, size_t len);
@@ -120,6 +121,8 @@ bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len);
*/
bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf);
+bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale);
+
#ifdef __cplusplus
}
#endif
diff --git a/code/components/esp32-camera-master/conversions/to_bmp.c b/code/components/esp32-camera-master/conversions/to_bmp.c
index 3b5e2b70..5a54bdba 100644
--- a/code/components/esp32-camera-master/conversions/to_bmp.c
+++ b/code/components/esp32-camera-master/conversions/to_bmp.c
@@ -24,6 +24,10 @@
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/spiram.h"
+#elif CONFIG_IDF_TARGET_ESP32S2
+#include "esp32s2/spiram.h"
+#elif CONFIG_IDF_TARGET_ESP32S3
+#include "esp32s3/spiram.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
@@ -115,6 +119,54 @@ static bool _rgb_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t
return true;
}
+static bool _rgb565_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data)
+{
+ rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
+ if(!data){
+ if(x == 0 && y == 0){
+ //write start
+ jpeg->width = w;
+ jpeg->height = h;
+ //if output is null, this is BMP
+ if(!jpeg->output){
+ jpeg->output = (uint8_t *)_malloc((w*h*3)+jpeg->data_offset);
+ if(!jpeg->output){
+ return false;
+ }
+ }
+ } else {
+ //write end
+ }
+ return true;
+ }
+
+ size_t jw = jpeg->width*3;
+ size_t jw2 = jpeg->width*2;
+ size_t t = y * jw;
+ size_t t2 = y * jw2;
+ size_t b = t + (h * jw);
+ size_t l = x * 2;
+ uint8_t *out = jpeg->output+jpeg->data_offset;
+ uint8_t *o = out;
+ size_t iy, iy2, ix, ix2;
+
+ w = w * 3;
+
+ for(iy=t, iy2=t2; iy> 3);
+ o[ix2+1] = c>>8;
+ o[ix2] = c&0xff;
+ }
+ data+=w;
+ }
+ return true;
+}
+
//input buffer
static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
{
@@ -140,6 +192,21 @@ static bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, jpg_sc
return true;
}
+bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale)
+{
+ rgb_jpg_decoder jpeg;
+ jpeg.width = 0;
+ jpeg.height = 0;
+ jpeg.input = src;
+ jpeg.output = out;
+ jpeg.data_offset = 0;
+
+ if(esp_jpg_decode(src_len, scale, _jpg_read, _rgb565_write, (void*)&jpeg) != ESP_OK){
+ return false;
+ }
+ return true;
+}
+
bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_len)
{
@@ -317,7 +384,7 @@ bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixf
}
*out = out_buf;
*out_len = out_size;
- return true;
+ return true;
}
bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len)
diff --git a/code/components/esp32-camera-master/conversions/to_jpg.cpp b/code/components/esp32-camera-master/conversions/to_jpg.cpp
index f8987a84..9b8905a7 100644
--- a/code/components/esp32-camera-master/conversions/to_jpg.cpp
+++ b/code/components/esp32-camera-master/conversions/to_jpg.cpp
@@ -25,6 +25,10 @@
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/spiram.h"
+#elif CONFIG_IDF_TARGET_ESP32S2
+#include "esp32s2/spiram.h"
+#elif CONFIG_IDF_TARGET_ESP32S3
+#include "esp32s3/spiram.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
@@ -195,7 +199,7 @@ public:
return true;
}
if ((size_t)len > (max_len - index)) {
- ESP_LOGW(TAG, "JPG output overflow: %d bytes", len - (max_len - index));
+ //ESP_LOGW(TAG, "JPG output overflow: %d bytes (%d,%d,%d)", len - (max_len - index), len, index, max_len);
len = max_len - index;
}
if (len) {
@@ -215,7 +219,7 @@ bool fmt2jpg(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixf
{
//todo: allocate proper buffer for holding JPEG data
//this should be enough for CIF frame size
- int jpg_buf_len = 64*1024;
+ int jpg_buf_len = 128*1024;
uint8_t * jpg_buf = (uint8_t *)_malloc(jpg_buf_len);
diff --git a/code/components/esp32-camera-master/driver/cam_hal.c b/code/components/esp32-camera-master/driver/cam_hal.c
new file mode 100644
index 00000000..c54fb817
--- /dev/null
+++ b/code/components/esp32-camera-master/driver/cam_hal.c
@@ -0,0 +1,483 @@
+// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include "esp_heap_caps.h"
+#include "ll_cam.h"
+#include "cam_hal.h"
+
+static const char *TAG = "cam_hal";
+
+static cam_obj_t *cam_obj = NULL;
+
+static const uint32_t JPEG_SOI_MARKER = 0xFFD8FF; // written in little-endian for esp32
+static const uint16_t JPEG_EOI_MARKER = 0xD9FF; // written in little-endian for esp32
+
+static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length)
+{
+ uint32_t sig = *((uint32_t *)inbuf) & 0xFFFFFF;
+ if(sig != JPEG_SOI_MARKER) {
+ for (uint32_t i = 0; i < length; i++) {
+ sig = *((uint32_t *)(&inbuf[i])) & 0xFFFFFF;
+ if (sig == JPEG_SOI_MARKER) {
+ ESP_LOGW(TAG, "SOI: %d", i);
+ return i;
+ }
+ }
+ ESP_LOGW(TAG, "NO-SOI");
+ return -1;
+ }
+ return 0;
+}
+
+static int cam_verify_jpeg_eoi(const uint8_t *inbuf, uint32_t length)
+{
+ int offset = -1;
+ uint8_t *dptr = (uint8_t *)inbuf + length - 2;
+ while (dptr > inbuf) {
+ uint16_t sig = *((uint16_t *)dptr);
+ if (JPEG_EOI_MARKER == sig) {
+ offset = dptr - inbuf;
+ //ESP_LOGW(TAG, "EOI: %d", length - (offset + 2));
+ return offset;
+ }
+ dptr--;
+ }
+ return -1;
+}
+
+static bool cam_get_next_frame(int * frame_pos)
+{
+ if(!cam_obj->frames[*frame_pos].en){
+ for (int x = 0; x < cam_obj->frame_cnt; x++) {
+ if (cam_obj->frames[x].en) {
+ *frame_pos = x;
+ return true;
+ }
+ }
+ } else {
+ return true;
+ }
+ return false;
+}
+
+static bool cam_start_frame(int * frame_pos)
+{
+ if (cam_get_next_frame(frame_pos)) {
+ if(ll_cam_start(cam_obj, *frame_pos)){
+ // Vsync the frame manually
+ ll_cam_do_vsync(cam_obj);
+ uint64_t us = (uint64_t)esp_timer_get_time();
+ cam_obj->frames[*frame_pos].fb.timestamp.tv_sec = us / 1000000UL;
+ cam_obj->frames[*frame_pos].fb.timestamp.tv_usec = us % 1000000UL;
+ return true;
+ }
+ }
+ return false;
+}
+
+void IRAM_ATTR ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType_t * HPTaskAwoken)
+{
+ if (xQueueSendFromISR(cam->event_queue, (void *)&cam_event, HPTaskAwoken) != pdTRUE) {
+ ll_cam_stop(cam);
+ cam->state = CAM_STATE_IDLE;
+ ESP_EARLY_LOGE(TAG, "EV-%s-OVF", cam_event==CAM_IN_SUC_EOF_EVENT ? "EOF" : "VSYNC");
+ }
+}
+
+//Copy fram from DMA dma_buffer to fram dma_buffer
+static void cam_task(void *arg)
+{
+ int cnt = 0;
+ int frame_pos = 0;
+ cam_obj->state = CAM_STATE_IDLE;
+ cam_event_t cam_event = 0;
+
+ xQueueReset(cam_obj->event_queue);
+
+ while (1) {
+ xQueueReceive(cam_obj->event_queue, (void *)&cam_event, portMAX_DELAY);
+ DBG_PIN_SET(1);
+ switch (cam_obj->state) {
+
+ case CAM_STATE_IDLE: {
+ if (cam_event == CAM_VSYNC_EVENT) {
+ //DBG_PIN_SET(1);
+ if(cam_start_frame(&frame_pos)){
+ cam_obj->frames[frame_pos].fb.len = 0;
+ cam_obj->state = CAM_STATE_READ_BUF;
+ }
+ cnt = 0;
+ }
+ }
+ break;
+
+ case CAM_STATE_READ_BUF: {
+ camera_fb_t * frame_buffer_event = &cam_obj->frames[frame_pos].fb;
+ size_t pixels_per_dma = (cam_obj->dma_half_buffer_size * cam_obj->fb_bytes_per_pixel) / (cam_obj->dma_bytes_per_item * cam_obj->in_bytes_per_pixel);
+
+ if (cam_event == CAM_IN_SUC_EOF_EVENT) {
+ if(!cam_obj->psram_mode){
+ if (cam_obj->fb_size < (frame_buffer_event->len + pixels_per_dma)) {
+ ESP_LOGW(TAG, "FB-OVF");
+ ll_cam_stop(cam_obj);
+ DBG_PIN_SET(0);
+ continue;
+ }
+ frame_buffer_event->len += ll_cam_memcpy(cam_obj,
+ &frame_buffer_event->buf[frame_buffer_event->len],
+ &cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
+ cam_obj->dma_half_buffer_size);
+ }
+ //Check for JPEG SOI in the first buffer. stop if not found
+ if (cam_obj->jpeg_mode && cnt == 0 && cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len) != 0) {
+ ll_cam_stop(cam_obj);
+ cam_obj->state = CAM_STATE_IDLE;
+ }
+ cnt++;
+
+ } else if (cam_event == CAM_VSYNC_EVENT) {
+ //DBG_PIN_SET(1);
+ ll_cam_stop(cam_obj);
+
+ if (cnt || !cam_obj->jpeg_mode || cam_obj->psram_mode) {
+ if (cam_obj->jpeg_mode) {
+ if (!cam_obj->psram_mode) {
+ if (cam_obj->fb_size < (frame_buffer_event->len + pixels_per_dma)) {
+ ESP_LOGW(TAG, "FB-OVF");
+ cnt--;
+ } else {
+ frame_buffer_event->len += ll_cam_memcpy(cam_obj,
+ &frame_buffer_event->buf[frame_buffer_event->len],
+ &cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
+ cam_obj->dma_half_buffer_size);
+ }
+ }
+ cnt++;
+ }
+
+ cam_obj->frames[frame_pos].en = 0;
+
+ if (cam_obj->psram_mode) {
+ if (cam_obj->jpeg_mode) {
+ frame_buffer_event->len = cnt * cam_obj->dma_half_buffer_size;
+ } else {
+ frame_buffer_event->len = cam_obj->recv_size;
+ }
+ } else if (!cam_obj->jpeg_mode) {
+ if (frame_buffer_event->len != cam_obj->fb_size) {
+ cam_obj->frames[frame_pos].en = 1;
+ ESP_LOGE(TAG, "FB-SIZE: %u != %u", frame_buffer_event->len, cam_obj->fb_size);
+ }
+ }
+ //send frame
+ if(!cam_obj->frames[frame_pos].en && xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, 0) != pdTRUE) {
+ //pop frame buffer from the queue
+ camera_fb_t * fb2 = NULL;
+ if(xQueueReceive(cam_obj->frame_buffer_queue, &fb2, 0) == pdTRUE) {
+ //push the new frame to the end of the queue
+ if (xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, 0) != pdTRUE) {
+ cam_obj->frames[frame_pos].en = 1;
+ ESP_LOGE(TAG, "FBQ-SND");
+ }
+ //free the popped buffer
+ cam_give(fb2);
+ } else {
+ //queue is full and we could not pop a frame from it
+ cam_obj->frames[frame_pos].en = 1;
+ ESP_LOGE(TAG, "FBQ-RCV");
+ }
+ }
+ }
+
+ if(!cam_start_frame(&frame_pos)){
+ cam_obj->state = CAM_STATE_IDLE;
+ } else {
+ cam_obj->frames[frame_pos].fb.len = 0;
+ }
+ cnt = 0;
+ }
+ }
+ break;
+ }
+ DBG_PIN_SET(0);
+ }
+}
+
+static lldesc_t * allocate_dma_descriptors(uint32_t count, uint16_t size, uint8_t * buffer)
+{
+ lldesc_t *dma = (lldesc_t *)heap_caps_malloc(count * sizeof(lldesc_t), MALLOC_CAP_DMA);
+ if (dma == NULL) {
+ return dma;
+ }
+
+ for (int x = 0; x < count; x++) {
+ dma[x].size = size;
+ dma[x].length = 0;
+ dma[x].sosf = 0;
+ dma[x].eof = 0;
+ dma[x].owner = 1;
+ dma[x].buf = (buffer + size * x);
+ dma[x].empty = (uint32_t)&dma[(x + 1) % count];
+ }
+ return dma;
+}
+
+static esp_err_t cam_dma_config(const camera_config_t *config)
+{
+ bool ret = ll_cam_dma_sizes(cam_obj);
+ if (0 == ret) {
+ return ESP_FAIL;
+ }
+
+ cam_obj->dma_node_cnt = (cam_obj->dma_buffer_size) / cam_obj->dma_node_buffer_size; // Number of DMA nodes
+ cam_obj->frame_copy_cnt = cam_obj->recv_size / cam_obj->dma_half_buffer_size; // Number of interrupted copies, ping-pong copy
+
+ ESP_LOGI(TAG, "buffer_size: %d, half_buffer_size: %d, node_buffer_size: %d, node_cnt: %d, total_cnt: %d",
+ cam_obj->dma_buffer_size, cam_obj->dma_half_buffer_size, cam_obj->dma_node_buffer_size, cam_obj->dma_node_cnt, cam_obj->frame_copy_cnt);
+
+ cam_obj->dma_buffer = NULL;
+ cam_obj->dma = NULL;
+
+ cam_obj->frames = (cam_frame_t *)heap_caps_calloc(1, cam_obj->frame_cnt * sizeof(cam_frame_t), MALLOC_CAP_DEFAULT);
+ CAM_CHECK(cam_obj->frames != NULL, "frames malloc failed", ESP_FAIL);
+
+ uint8_t dma_align = 0;
+ size_t fb_size = cam_obj->fb_size;
+ if (cam_obj->psram_mode) {
+ dma_align = ll_cam_get_dma_align(cam_obj);
+ if (cam_obj->fb_size < cam_obj->recv_size) {
+ fb_size = cam_obj->recv_size;
+ }
+ }
+
+ /* Allocate memeory 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;
+ }
+ }
+}
diff --git a/code/components/esp32-camera-master/driver/camera.c b/code/components/esp32-camera-master/driver/camera.c
deleted file mode 100644
index 9e6a7164..00000000
--- a/code/components/esp32-camera-master/driver/camera.c
+++ /dev/null
@@ -1,1592 +0,0 @@
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#include
-#include
-#include
-#include "time.h"
-#include "sys/time.h"
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-#include "freertos/semphr.h"
-#include "soc/soc.h"
-#include "soc/gpio_sig_map.h"
-#include "soc/i2s_reg.h"
-#include "soc/i2s_struct.h"
-#include "soc/io_mux_reg.h"
-#include "driver/gpio.h"
-#include "driver/rtc_io.h"
-#include "driver/periph_ctrl.h"
-#include "esp_intr_alloc.h"
-#include "esp_system.h"
-#include "nvs_flash.h"
-#include "nvs.h"
-#include "sensor.h"
-#include "sccb.h"
-#include "esp_camera.h"
-#include "camera_common.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
-
-typedef enum {
- CAMERA_NONE = 0,
- CAMERA_UNKNOWN = 1,
- CAMERA_OV7725 = 7725,
- CAMERA_OV2640 = 2640,
- CAMERA_OV3660 = 3660,
- CAMERA_OV5640 = 5640,
- CAMERA_OV7670 = 7670,
- CAMERA_NT99141 = 9141,
-} camera_model_t;
-
-#define REG_PID 0x0A
-#define REG_VER 0x0B
-#define REG_MIDH 0x1C
-#define REG_MIDL 0x1D
-
-#define REG16_CHIDH 0x300A
-#define REG16_CHIDL 0x300B
-
-#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
-static const char* CAMERA_SENSOR_NVS_KEY = "sensor";
-static const char* CAMERA_PIXFORMAT_NVS_KEY = "pixformat";
-
-typedef void (*dma_filter_t)(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst);
-
-typedef struct camera_fb_s {
- uint8_t * buf;
- size_t len;
- size_t width;
- size_t height;
- pixformat_t format;
- struct timeval timestamp;
- size_t size;
- uint8_t ref;
- uint8_t bad;
- struct camera_fb_s * next;
-} camera_fb_int_t;
-
-typedef struct fb_s {
- uint8_t * buf;
- size_t len;
- struct fb_s * next;
-} fb_item_t;
-
-typedef struct {
- camera_config_t config;
- sensor_t sensor;
-
- camera_fb_int_t *fb;
- size_t fb_size;
- size_t data_size;
-
- size_t width;
- size_t height;
- size_t in_bytes_per_pixel;
- size_t fb_bytes_per_pixel;
-
- size_t dma_received_count;
- size_t dma_filtered_count;
- size_t dma_per_line;
- size_t dma_buf_width;
- size_t dma_sample_count;
-
- lldesc_t *dma_desc;
- dma_elem_t **dma_buf;
- size_t dma_desc_count;
- size_t dma_desc_cur;
-
- i2s_sampling_mode_t sampling_mode;
- dma_filter_t dma_filter;
- intr_handle_t i2s_intr_handle;
- QueueHandle_t data_ready;
- QueueHandle_t fb_in;
- QueueHandle_t fb_out;
-
- SemaphoreHandle_t frame_ready;
- TaskHandle_t dma_filter_task;
-} camera_state_t;
-
-camera_state_t* s_state = NULL;
-
-static void i2s_init();
-static int i2s_run();
-static void IRAM_ATTR vsync_isr(void* arg);
-static void IRAM_ATTR i2s_isr(void* arg);
-static esp_err_t dma_desc_init();
-static void dma_desc_deinit();
-static void dma_filter_task(void *pvParameters);
-static void dma_filter_grayscale(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst);
-static void dma_filter_grayscale_highspeed(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst);
-static void dma_filter_yuyv(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst);
-static void dma_filter_yuyv_highspeed(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst);
-static void dma_filter_jpeg(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst);
-static void i2s_stop(bool* need_yield);
-
-static bool is_hs_mode()
-{
- return s_state->config.xclk_freq_hz > 10000000;
-}
-
-static size_t i2s_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 int IRAM_ATTR _gpio_get_level(gpio_num_t gpio_num)
-{
- if (gpio_num < 32) {
- return (GPIO.in >> gpio_num) & 0x1;
- } else {
- return (GPIO.in1.data >> (gpio_num - 32)) & 0x1;
- }
-}
-
-static void IRAM_ATTR vsync_intr_disable()
-{
- gpio_set_intr_type(s_state->config.pin_vsync, GPIO_INTR_DISABLE);
-}
-
-static void vsync_intr_enable()
-{
- gpio_set_intr_type(s_state->config.pin_vsync, GPIO_INTR_NEGEDGE);
-}
-
-static int skip_frame()
-{
- if (s_state == NULL) {
- return -1;
- }
- int64_t st_t = esp_timer_get_time();
- while (_gpio_get_level(s_state->config.pin_vsync) == 0) {
- if((esp_timer_get_time() - st_t) > 1000000LL){
- goto timeout;
- }
- }
- while (_gpio_get_level(s_state->config.pin_vsync) != 0) {
- if((esp_timer_get_time() - st_t) > 1000000LL){
- goto timeout;
- }
- }
- while (_gpio_get_level(s_state->config.pin_vsync) == 0) {
- if((esp_timer_get_time() - st_t) > 1000000LL){
- goto timeout;
- }
- }
- return 0;
-
-timeout:
- ESP_LOGE(TAG, "Timeout waiting for VSYNC");
- return -1;
-}
-
-static void camera_fb_deinit()
-{
- camera_fb_int_t * _fb1 = s_state->fb, * _fb2 = NULL;
- while(s_state->fb) {
- _fb2 = s_state->fb;
- s_state->fb = _fb2->next;
- if(_fb2->next == _fb1) {
- s_state->fb = NULL;
- }
- free(_fb2->buf);
- free(_fb2);
- }
-}
-
-static esp_err_t camera_fb_init(size_t count)
-{
- if(!count) {
- return ESP_ERR_INVALID_ARG;
- }
-
- camera_fb_deinit();
-
- ESP_LOGI(TAG, "Allocating %u frame buffers (%d KB total)", count, (s_state->fb_size * count) / 1024);
-
- camera_fb_int_t * _fb = NULL, * _fb1 = NULL, * _fb2 = NULL;
- for(size_t i = 0; i < count; i++) {
- _fb2 = (camera_fb_int_t *)malloc(sizeof(camera_fb_int_t));
- if(!_fb2) {
- goto fail;
- }
- memset(_fb2, 0, sizeof(camera_fb_int_t));
- _fb2->size = s_state->fb_size;
- _fb2->buf = (uint8_t*) calloc(_fb2->size, 1);
- if(!_fb2->buf) {
- ESP_LOGI(TAG, "Allocating %d KB frame buffer in PSRAM", s_state->fb_size/1024);
- _fb2->buf = (uint8_t*) heap_caps_calloc(_fb2->size, 1, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
- } else {
- ESP_LOGI(TAG, "Allocating %d KB frame buffer in OnBoard RAM", s_state->fb_size/1024);
- }
- if(!_fb2->buf) {
- free(_fb2);
- ESP_LOGE(TAG, "Allocating %d KB frame buffer Failed", s_state->fb_size/1024);
- goto fail;
- }
- memset(_fb2->buf, 0, _fb2->size);
- _fb2->next = _fb;
- _fb = _fb2;
- if(!i) {
- _fb1 = _fb2;
- }
- }
- if(_fb1) {
- _fb1->next = _fb;
- }
-
- s_state->fb = _fb;//load first buffer
-
- return ESP_OK;
-
-fail:
- while(_fb) {
- _fb2 = _fb;
- _fb = _fb->next;
- free(_fb2->buf);
- free(_fb2);
- }
- return ESP_ERR_NO_MEM;
-}
-
-static esp_err_t dma_desc_init()
-{
- assert(s_state->width % 4 == 0);
- size_t line_size = s_state->width * s_state->in_bytes_per_pixel *
- i2s_bytes_per_sample(s_state->sampling_mode);
- ESP_LOGD(TAG, "Line width (for DMA): %d bytes", line_size);
- size_t dma_per_line = 1;
- size_t buf_size = line_size;
- while (buf_size >= 4096) {
- buf_size /= 2;
- dma_per_line *= 2;
- }
- size_t dma_desc_count = dma_per_line * 4;
- s_state->dma_buf_width = line_size;
- s_state->dma_per_line = dma_per_line;
- s_state->dma_desc_count = dma_desc_count;
- ESP_LOGD(TAG, "DMA buffer size: %d, DMA buffers per line: %d", buf_size, dma_per_line);
- ESP_LOGD(TAG, "DMA buffer count: %d", dma_desc_count);
- ESP_LOGD(TAG, "DMA buffer total: %d bytes", buf_size * dma_desc_count);
-
- s_state->dma_buf = (dma_elem_t**) malloc(sizeof(dma_elem_t*) * dma_desc_count);
- if (s_state->dma_buf == NULL) {
- return ESP_ERR_NO_MEM;
- }
- s_state->dma_desc = (lldesc_t*) malloc(sizeof(lldesc_t) * dma_desc_count);
- if (s_state->dma_desc == NULL) {
- return ESP_ERR_NO_MEM;
- }
- size_t dma_sample_count = 0;
- for (int i = 0; i < dma_desc_count; ++i) {
- ESP_LOGD(TAG, "Allocating DMA buffer #%d, size=%d", i, buf_size);
- dma_elem_t* buf = (dma_elem_t*) malloc(buf_size);
- if (buf == NULL) {
- return ESP_ERR_NO_MEM;
- }
- s_state->dma_buf[i] = buf;
- ESP_LOGV(TAG, "dma_buf[%d]=%p", i, buf);
-
- lldesc_t* pd = &s_state->dma_desc[i];
- pd->length = buf_size;
- if (s_state->sampling_mode == SM_0A0B_0B0C &&
- (i + 1) % dma_per_line == 0) {
- pd->length -= 4;
- }
- dma_sample_count += pd->length / 4;
- pd->size = pd->length;
- pd->owner = 1;
- pd->sosf = 1;
- pd->buf = (uint8_t*) buf;
- pd->offset = 0;
- pd->empty = 0;
- pd->eof = 1;
- pd->qe.stqe_next = &s_state->dma_desc[(i + 1) % dma_desc_count];
- }
- s_state->dma_sample_count = dma_sample_count;
- return ESP_OK;
-}
-
-static void dma_desc_deinit()
-{
- if (s_state->dma_buf) {
- for (int i = 0; i < s_state->dma_desc_count; ++i) {
- free(s_state->dma_buf[i]);
- }
- }
- free(s_state->dma_buf);
- free(s_state->dma_desc);
-}
-
-static inline void IRAM_ATTR i2s_conf_reset()
-{
- const uint32_t lc_conf_reset_flags = I2S_IN_RST_M | I2S_AHBM_RST_M
- | I2S_AHBM_FIFO_RST_M;
- I2S0.lc_conf.val |= lc_conf_reset_flags;
- I2S0.lc_conf.val &= ~lc_conf_reset_flags;
-
- const uint32_t conf_reset_flags = I2S_RX_RESET_M | I2S_RX_FIFO_RESET_M
- | I2S_TX_RESET_M | I2S_TX_FIFO_RESET_M;
- I2S0.conf.val |= conf_reset_flags;
- I2S0.conf.val &= ~conf_reset_flags;
- while (I2S0.state.rx_fifo_reset_back) {
- ;
- }
-}
-
-static void i2s_gpio_init(const camera_config_t* config)
-{
- // Configure input GPIOs
- const gpio_num_t pins[] = {
- config->pin_d7,
- config->pin_d6,
- config->pin_d5,
- config->pin_d4,
- config->pin_d3,
- config->pin_d2,
- config->pin_d1,
- config->pin_d0,
- config->pin_vsync,
- config->pin_href,
- config->pin_pclk
- };
- gpio_config_t conf = {
- .mode = GPIO_MODE_INPUT,
- .pull_up_en = GPIO_PULLUP_ENABLE,
- .pull_down_en = GPIO_PULLDOWN_DISABLE,
- .intr_type = GPIO_INTR_DISABLE,
- .pin_bit_mask = 0LL
- };
- for (int i = 0; i < sizeof(pins) / sizeof(gpio_num_t); ++i) {
- if (rtc_gpio_is_valid_gpio(pins[i])) {
- rtc_gpio_deinit(pins[i]);
- }
- conf.pin_bit_mask |= 1LL << pins[i];
- }
- gpio_config(&conf);
-}
-
-static void i2s_init()
-{
- camera_config_t* config = &s_state->config;
-
- // Route input GPIOs to I2S peripheral using GPIO matrix
- gpio_matrix_in(config->pin_d0, I2S0I_DATA_IN0_IDX, false);
- gpio_matrix_in(config->pin_d1, I2S0I_DATA_IN1_IDX, false);
- gpio_matrix_in(config->pin_d2, I2S0I_DATA_IN2_IDX, false);
- gpio_matrix_in(config->pin_d3, I2S0I_DATA_IN3_IDX, false);
- gpio_matrix_in(config->pin_d4, I2S0I_DATA_IN4_IDX, false);
- gpio_matrix_in(config->pin_d5, I2S0I_DATA_IN5_IDX, false);
- gpio_matrix_in(config->pin_d6, I2S0I_DATA_IN6_IDX, false);
- gpio_matrix_in(config->pin_d7, I2S0I_DATA_IN7_IDX, false);
- gpio_matrix_in(config->pin_vsync, I2S0I_V_SYNC_IDX, false);
- gpio_matrix_in(0x38, I2S0I_H_SYNC_IDX, false);
- gpio_matrix_in(config->pin_href, I2S0I_H_ENABLE_IDX, false);
- gpio_matrix_in(config->pin_pclk, I2S0I_WS_IN_IDX, false);
-
- // Enable and configure I2S peripheral
- periph_module_enable(PERIPH_I2S0_MODULE);
- // Toggle some reset bits in LC_CONF register
- // Toggle some reset bits in CONF register
- i2s_conf_reset();
- // Enable slave mode (sampling clock is external)
- I2S0.conf.rx_slave_mod = 1;
- // Enable parallel mode
- I2S0.conf2.lcd_en = 1;
- // Use HSYNC/VSYNC/HREF to control sampling
- I2S0.conf2.camera_en = 1;
- // Configure clock divider
- I2S0.clkm_conf.clkm_div_a = 1;
- I2S0.clkm_conf.clkm_div_b = 0;
- I2S0.clkm_conf.clkm_div_num = 2;
- // FIFO will sink data to DMA
- I2S0.fifo_conf.dscr_en = 1;
- // FIFO configuration
- I2S0.fifo_conf.rx_fifo_mod = s_state->sampling_mode;
- I2S0.fifo_conf.rx_fifo_mod_force_en = 1;
- I2S0.conf_chan.rx_chan_mod = 1;
- // Clear flags which are used in I2S serial mode
- I2S0.sample_rate_conf.rx_bits_mod = 0;
- 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.timing.val = 0;
- I2S0.timing.rx_dsync_sw = 1;
-
- // Allocate I2S interrupt, keep it disabled
- ESP_ERROR_CHECK(esp_intr_alloc(ETS_I2S0_INTR_SOURCE,
- ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM,
- &i2s_isr, NULL, &s_state->i2s_intr_handle));
-}
-
-static void IRAM_ATTR i2s_start_bus()
-{
- s_state->dma_desc_cur = 0;
- s_state->dma_received_count = 0;
- //s_state->dma_filtered_count = 0;
- esp_intr_disable(s_state->i2s_intr_handle);
- i2s_conf_reset();
-
- I2S0.rx_eof_num = s_state->dma_sample_count;
- I2S0.in_link.addr = (uint32_t) &s_state->dma_desc[0];
- I2S0.in_link.start = 1;
- I2S0.int_clr.val = I2S0.int_raw.val;
- I2S0.int_ena.val = 0;
- I2S0.int_ena.in_done = 1;
-
- esp_intr_enable(s_state->i2s_intr_handle);
- I2S0.conf.rx_start = 1;
- if (s_state->config.pixel_format == PIXFORMAT_JPEG) {
- vsync_intr_enable();
- }
-}
-
-static int i2s_run()
-{
- for (int i = 0; i < s_state->dma_desc_count; ++i) {
- lldesc_t* d = &s_state->dma_desc[i];
- ESP_LOGV(TAG, "DMA desc %2d: %u %u %u %u %u %u %p %p",
- i, d->length, d->size, d->offset, d->eof, d->sosf, d->owner, d->buf, d->qe.stqe_next);
- memset(s_state->dma_buf[i], 0, d->length);
- }
-
- // wait for frame
- camera_fb_int_t * fb = s_state->fb;
- while(s_state->config.fb_count > 1) {
- while(s_state->fb->ref && s_state->fb->next != fb) {
- s_state->fb = s_state->fb->next;
- }
- if(s_state->fb->ref == 0) {
- break;
- }
- vTaskDelay(2);
- }
-
- //todo: wait for vsync
- ESP_LOGV(TAG, "Waiting for negative edge on VSYNC");
-
- int64_t st_t = esp_timer_get_time();
- while (_gpio_get_level(s_state->config.pin_vsync) != 0) {
- if((esp_timer_get_time() - st_t) > 1000000LL){
- ESP_LOGE(TAG, "Timeout waiting for VSYNC");
- return -1;
- }
- }
- ESP_LOGV(TAG, "Got VSYNC");
- i2s_start_bus();
- return 0;
-}
-
-static void IRAM_ATTR i2s_stop_bus()
-{
- esp_intr_disable(s_state->i2s_intr_handle);
- vsync_intr_disable();
- i2s_conf_reset();
- I2S0.conf.rx_start = 0;
-}
-
-static void IRAM_ATTR i2s_stop(bool* need_yield)
-{
- if(s_state->config.fb_count == 1 && !s_state->fb->bad) {
- i2s_stop_bus();
- } else {
- s_state->dma_received_count = 0;
- }
-
- size_t val = SIZE_MAX;
- BaseType_t higher_priority_task_woken;
- BaseType_t ret = xQueueSendFromISR(s_state->data_ready, &val, &higher_priority_task_woken);
- if(need_yield && !*need_yield) {
- *need_yield = (ret == pdTRUE && higher_priority_task_woken == pdTRUE);
- }
-}
-
-static void IRAM_ATTR signal_dma_buf_received(bool* need_yield)
-{
- size_t dma_desc_filled = s_state->dma_desc_cur;
- s_state->dma_desc_cur = (dma_desc_filled + 1) % s_state->dma_desc_count;
- s_state->dma_received_count++;
- if(!s_state->fb->ref && s_state->fb->bad){
- *need_yield = false;
- return;
- }
- BaseType_t higher_priority_task_woken;
- BaseType_t ret = xQueueSendFromISR(s_state->data_ready, &dma_desc_filled, &higher_priority_task_woken);
- if (ret != pdTRUE) {
- if(!s_state->fb->ref) {
- s_state->fb->bad = 1;
- }
- //ESP_EARLY_LOGW(TAG, "qsf:%d", s_state->dma_received_count);
- //ets_printf("qsf:%d\n", s_state->dma_received_count);
- //ets_printf("qovf\n");
- }
- *need_yield = (ret == pdTRUE && higher_priority_task_woken == pdTRUE);
-}
-
-static void IRAM_ATTR i2s_isr(void* arg)
-{
- I2S0.int_clr.val = I2S0.int_raw.val;
- bool need_yield = false;
- signal_dma_buf_received(&need_yield);
- if (s_state->config.pixel_format != PIXFORMAT_JPEG
- && s_state->dma_received_count == s_state->height * s_state->dma_per_line) {
- i2s_stop(&need_yield);
- }
- if (need_yield) {
- portYIELD_FROM_ISR();
- }
-}
-
-static void IRAM_ATTR vsync_isr(void* arg)
-{
- GPIO.status1_w1tc.val = GPIO.status1.val;
- GPIO.status_w1tc = GPIO.status;
- bool need_yield = false;
- //if vsync is low and we have received some data, frame is done
- if (_gpio_get_level(s_state->config.pin_vsync) == 0) {
- if(s_state->dma_received_count > 0) {
- signal_dma_buf_received(&need_yield);
- //ets_printf("end_vsync\n");
- if(s_state->dma_filtered_count > 1 || s_state->fb->bad || s_state->config.fb_count > 1) {
- i2s_stop(&need_yield);
- }
- //ets_printf("vs\n");
- }
- if(s_state->config.fb_count > 1 || s_state->dma_filtered_count < 2) {
- I2S0.conf.rx_start = 0;
- I2S0.in_link.start = 0;
- I2S0.int_clr.val = I2S0.int_raw.val;
- i2s_conf_reset();
- s_state->dma_desc_cur = (s_state->dma_desc_cur + 1) % s_state->dma_desc_count;
- //I2S0.rx_eof_num = s_state->dma_sample_count;
- I2S0.in_link.addr = (uint32_t) &s_state->dma_desc[s_state->dma_desc_cur];
- I2S0.in_link.start = 1;
- I2S0.conf.rx_start = 1;
- s_state->dma_received_count = 0;
- }
- }
- if (need_yield) {
- portYIELD_FROM_ISR();
- }
-}
-
-static void IRAM_ATTR camera_fb_done()
-{
- camera_fb_int_t * fb = NULL, * fb2 = NULL;
- BaseType_t taskAwoken = 0;
-
- if(s_state->config.fb_count == 1) {
- xSemaphoreGive(s_state->frame_ready);
- return;
- }
-
- fb = s_state->fb;
- if(!fb->ref && fb->len) {
- //add reference
- fb->ref = 1;
-
- //check if the queue is full
- if(xQueueIsQueueFullFromISR(s_state->fb_out) == pdTRUE) {
- //pop frame buffer from the queue
- if(xQueueReceiveFromISR(s_state->fb_out, &fb2, &taskAwoken) == pdTRUE) {
- //free the popped buffer
- fb2->ref = 0;
- fb2->len = 0;
- //push the new frame to the end of the queue
- xQueueSendFromISR(s_state->fb_out, &fb, &taskAwoken);
- } else {
- //queue is full and we could not pop a frame from it
- }
- } else {
- //push the new frame to the end of the queue
- xQueueSendFromISR(s_state->fb_out, &fb, &taskAwoken);
- }
- } else {
- //frame was referenced or empty
- }
-
- //return buffers to be filled
- while(xQueueReceiveFromISR(s_state->fb_in, &fb2, &taskAwoken) == pdTRUE) {
- fb2->ref = 0;
- fb2->len = 0;
- }
-
- //advance frame buffer only if the current one has data
- if(s_state->fb->len) {
- s_state->fb = s_state->fb->next;
- }
- //try to find the next free frame buffer
- while(s_state->fb->ref && s_state->fb->next != fb) {
- s_state->fb = s_state->fb->next;
- }
- //is the found frame buffer free?
- if(!s_state->fb->ref) {
- //buffer found. make sure it's empty
- s_state->fb->len = 0;
- *((uint32_t *)s_state->fb->buf) = 0;
- } else {
- //stay at the previous buffer
- s_state->fb = fb;
- }
-}
-
-static void IRAM_ATTR dma_finish_frame()
-{
- size_t buf_len = s_state->width * s_state->fb_bytes_per_pixel / s_state->dma_per_line;
-
- if(!s_state->fb->ref) {
- // is the frame bad?
- if(s_state->fb->bad){
- s_state->fb->bad = 0;
- s_state->fb->len = 0;
- *((uint32_t *)s_state->fb->buf) = 0;
- if(s_state->config.fb_count == 1) {
- i2s_start_bus();
- }
- //ets_printf("bad\n");
- } else {
- s_state->fb->len = s_state->dma_filtered_count * buf_len;
- if(s_state->fb->len) {
- //find the end marker for JPEG. Data after that can be discarded
- if(s_state->fb->format == PIXFORMAT_JPEG){
- uint8_t * dptr = &s_state->fb->buf[s_state->fb->len - 1];
- while(dptr > s_state->fb->buf){
- if(dptr[0] == 0xFF && dptr[1] == 0xD9 && dptr[2] == 0x00 && dptr[3] == 0x00){
- dptr += 2;
- s_state->fb->len = dptr - s_state->fb->buf;
- if((s_state->fb->len & 0x1FF) == 0){
- s_state->fb->len += 1;
- }
- if((s_state->fb->len % 100) == 0){
- s_state->fb->len += 1;
- }
- break;
- }
- dptr--;
- }
- }
- //send out the frame
- camera_fb_done();
- } else if(s_state->config.fb_count == 1){
- //frame was empty?
- i2s_start_bus();
- } else {
- //ets_printf("empty\n");
- }
- }
- } else if(s_state->fb->len) {
- camera_fb_done();
- }
- s_state->dma_filtered_count = 0;
-}
-
-static void IRAM_ATTR dma_filter_buffer(size_t buf_idx)
-{
- //no need to process the data if frame is in use or is bad
- if(s_state->fb->ref || s_state->fb->bad) {
- return;
- }
-
- //check if there is enough space in the frame buffer for the new data
- size_t buf_len = s_state->width * s_state->fb_bytes_per_pixel / s_state->dma_per_line;
- size_t fb_pos = s_state->dma_filtered_count * buf_len;
- if(fb_pos > s_state->fb_size - buf_len) {
- //size_t processed = s_state->dma_received_count * buf_len;
- //ets_printf("[%s:%u] ovf pos: %u, processed: %u\n", __FUNCTION__, __LINE__, fb_pos, processed);
- return;
- }
-
- //convert I2S DMA buffer to pixel data
- (*s_state->dma_filter)(s_state->dma_buf[buf_idx], &s_state->dma_desc[buf_idx], s_state->fb->buf + fb_pos);
-
- //first frame buffer
- if(!s_state->dma_filtered_count) {
- //check for correct JPEG header
- if(s_state->sensor.pixformat == PIXFORMAT_JPEG) {
- uint32_t sig = *((uint32_t *)s_state->fb->buf) & 0xFFFFFF;
- if(sig != 0xffd8ff) {
- ESP_LOGD(TAG,"unexpected JPEG signature 0x%08x\n", sig);
- s_state->fb->bad = 1;
- return;
- }
- }
- //set the frame properties
- s_state->fb->width = resolution[s_state->sensor.status.framesize].width;
- s_state->fb->height = resolution[s_state->sensor.status.framesize].height;
- s_state->fb->format = s_state->sensor.pixformat;
-
- uint64_t us = (uint64_t)esp_timer_get_time();
- s_state->fb->timestamp.tv_sec = us / 1000000UL;
- s_state->fb->timestamp.tv_usec = us % 1000000UL;
- }
- s_state->dma_filtered_count++;
-}
-
-static void IRAM_ATTR dma_filter_task(void *pvParameters)
-{
- s_state->dma_filtered_count = 0;
- while (true) {
- size_t buf_idx;
- if(xQueueReceive(s_state->data_ready, &buf_idx, portMAX_DELAY) == pdTRUE) {
- if (buf_idx == SIZE_MAX) {
- //this is the end of the frame
- dma_finish_frame();
- } else {
- dma_filter_buffer(buf_idx);
- }
- }
- }
-}
-
-static void IRAM_ATTR dma_filter_jpeg(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst)
-{
- size_t end = dma_desc->length / sizeof(dma_elem_t) / 4;
- // manually unrolling 4 iterations of the loop here
- for (size_t i = 0; i < end; ++i) {
- dst[0] = src[0].sample1;
- dst[1] = src[1].sample1;
- dst[2] = src[2].sample1;
- dst[3] = src[3].sample1;
- src += 4;
- dst += 4;
- }
-}
-
-static void IRAM_ATTR dma_filter_grayscale(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst)
-{
- size_t end = dma_desc->length / sizeof(dma_elem_t) / 4;
- for (size_t i = 0; i < end; ++i) {
- // manually unrolling 4 iterations of the loop here
- dst[0] = src[0].sample1;
- dst[1] = src[1].sample1;
- dst[2] = src[2].sample1;
- dst[3] = src[3].sample1;
- src += 4;
- dst += 4;
- }
-}
-
-static void IRAM_ATTR dma_filter_grayscale_highspeed(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst)
-{
- size_t end = dma_desc->length / sizeof(dma_elem_t) / 8;
- for (size_t i = 0; i < end; ++i) {
- // manually unrolling 4 iterations of the loop here
- dst[0] = src[0].sample1;
- dst[1] = src[2].sample1;
- dst[2] = src[4].sample1;
- dst[3] = src[6].sample1;
- src += 8;
- dst += 4;
- }
- // the final sample of a line in SM_0A0B_0B0C sampling mode needs special handling
- if ((dma_desc->length & 0x7) != 0) {
- dst[0] = src[0].sample1;
- dst[1] = src[2].sample1;
- }
-}
-
-static void IRAM_ATTR dma_filter_yuyv(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst)
-{
- size_t end = dma_desc->length / sizeof(dma_elem_t) / 4;
- for (size_t i = 0; i < end; ++i) {
- dst[0] = src[0].sample1;//y0
- dst[1] = src[0].sample2;//u
- dst[2] = src[1].sample1;//y1
- dst[3] = src[1].sample2;//v
-
- dst[4] = src[2].sample1;//y0
- dst[5] = src[2].sample2;//u
- dst[6] = src[3].sample1;//y1
- dst[7] = src[3].sample2;//v
- src += 4;
- dst += 8;
- }
-}
-
-static void IRAM_ATTR dma_filter_yuyv_highspeed(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst)
-{
- size_t end = dma_desc->length / sizeof(dma_elem_t) / 8;
- for (size_t i = 0; i < end; ++i) {
- dst[0] = src[0].sample1;//y0
- dst[1] = src[1].sample1;//u
- dst[2] = src[2].sample1;//y1
- dst[3] = src[3].sample1;//v
-
- dst[4] = src[4].sample1;//y0
- dst[5] = src[5].sample1;//u
- dst[6] = src[6].sample1;//y1
- dst[7] = src[7].sample1;//v
- src += 8;
- dst += 8;
- }
- if ((dma_desc->length & 0x7) != 0) {
- dst[0] = src[0].sample1;//y0
- dst[1] = src[1].sample1;//u
- dst[2] = src[2].sample1;//y1
- dst[3] = src[2].sample2;//v
- }
-}
-
-static void IRAM_ATTR dma_filter_rgb888(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst)
-{
- size_t end = dma_desc->length / sizeof(dma_elem_t) / 4;
- uint8_t lb, hb;
- for (size_t i = 0; i < end; ++i) {
- hb = src[0].sample1;
- lb = src[0].sample2;
- dst[0] = (lb & 0x1F) << 3;
- dst[1] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
- dst[2] = hb & 0xF8;
-
- hb = src[1].sample1;
- lb = src[1].sample2;
- dst[3] = (lb & 0x1F) << 3;
- dst[4] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
- dst[5] = hb & 0xF8;
-
- hb = src[2].sample1;
- lb = src[2].sample2;
- dst[6] = (lb & 0x1F) << 3;
- dst[7] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
- dst[8] = hb & 0xF8;
-
- hb = src[3].sample1;
- lb = src[3].sample2;
- dst[9] = (lb & 0x1F) << 3;
- dst[10] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
- dst[11] = hb & 0xF8;
- src += 4;
- dst += 12;
- }
-}
-
-static void IRAM_ATTR dma_filter_rgb888_highspeed(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst)
-{
- size_t end = dma_desc->length / sizeof(dma_elem_t) / 8;
- uint8_t lb, hb;
- for (size_t i = 0; i < end; ++i) {
- hb = src[0].sample1;
- lb = src[1].sample1;
- dst[0] = (lb & 0x1F) << 3;
- dst[1] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
- dst[2] = hb & 0xF8;
-
- hb = src[2].sample1;
- lb = src[3].sample1;
- dst[3] = (lb & 0x1F) << 3;
- dst[4] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
- dst[5] = hb & 0xF8;
-
- hb = src[4].sample1;
- lb = src[5].sample1;
- dst[6] = (lb & 0x1F) << 3;
- dst[7] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
- dst[8] = hb & 0xF8;
-
- hb = src[6].sample1;
- lb = src[7].sample1;
- dst[9] = (lb & 0x1F) << 3;
- dst[10] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
- dst[11] = hb & 0xF8;
-
- src += 8;
- dst += 12;
- }
- if ((dma_desc->length & 0x7) != 0) {
- hb = src[0].sample1;
- lb = src[1].sample1;
- dst[0] = (lb & 0x1F) << 3;
- dst[1] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
- dst[2] = hb & 0xF8;
-
- hb = src[2].sample1;
- lb = src[2].sample2;
- dst[3] = (lb & 0x1F) << 3;
- dst[4] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
- dst[5] = hb & 0xF8;
- }
-}
-
-/*
- * Public Methods
- * */
-
-esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera_model)
-{
- if (s_state != NULL) {
- return ESP_ERR_INVALID_STATE;
- }
-
- s_state = (camera_state_t*) calloc(sizeof(*s_state), 1);
- if (!s_state) {
- return ESP_ERR_NO_MEM;
- }
-
- if(config->pin_xclk >= 0) {
- ESP_LOGD(TAG, "Enabling XCLK output");
- camera_enable_out_clock(config);
- }
-
- if (config->pin_sscb_sda != -1) {
- ESP_LOGD(TAG, "Initializing SSCB");
- SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl);
- }
-
- if(config->pin_pwdn >= 0) {
- ESP_LOGD(TAG, "Resetting camera by power down line");
- gpio_config_t conf = { 0 };
- conf.pin_bit_mask = 1LL << config->pin_pwdn;
- conf.mode = GPIO_MODE_OUTPUT;
- gpio_config(&conf);
-
- // carefull, logic is inverted compared to reset pin
- gpio_set_level(config->pin_pwdn, 1);
- vTaskDelay(10 / portTICK_PERIOD_MS);
- gpio_set_level(config->pin_pwdn, 0);
- vTaskDelay(10 / portTICK_PERIOD_MS);
- }
-
- if(config->pin_reset >= 0) {
- ESP_LOGD(TAG, "Resetting camera");
- gpio_config_t conf = { 0 };
- conf.pin_bit_mask = 1LL << config->pin_reset;
- conf.mode = GPIO_MODE_OUTPUT;
- gpio_config(&conf);
-
- gpio_set_level(config->pin_reset, 0);
- vTaskDelay(10 / portTICK_PERIOD_MS);
- gpio_set_level(config->pin_reset, 1);
- vTaskDelay(10 / portTICK_PERIOD_MS);
- }
-
- ESP_LOGD(TAG, "Searching for camera address");
- vTaskDelay(10 / portTICK_PERIOD_MS);
- uint8_t slv_addr = SCCB_Probe();
- if (slv_addr == 0) {
- *out_camera_model = CAMERA_NONE;
- camera_disable_out_clock();
- return ESP_ERR_CAMERA_NOT_DETECTED;
- }
-
- //slv_addr = 0x30;
- ESP_LOGD(TAG, "Detected camera at address=0x%02x", slv_addr);
- sensor_id_t* id = &s_state->sensor.id;
-
-#if CONFIG_OV2640_SUPPORT
- if (slv_addr == 0x30) {
- ESP_LOGD(TAG, "Resetting OV2640");
- //camera might be OV2640. try to reset it
- SCCB_Write(0x30, 0xFF, 0x01);//bank sensor
- SCCB_Write(0x30, 0x12, 0x80);//reset
- vTaskDelay(10 / portTICK_PERIOD_MS);
- slv_addr = SCCB_Probe();
- }
-#endif
-#if CONFIG_NT99141_SUPPORT
- if (slv_addr == 0x2a)
- {
- ESP_LOGD(TAG, "Resetting NT99141");
- SCCB_Write16(0x2a, 0x3008, 0x01);//bank sensor
- }
-#endif
-
- s_state->sensor.slv_addr = slv_addr;
- s_state->sensor.xclk_freq_hz = config->xclk_freq_hz;
-
-#if (CONFIG_OV3660_SUPPORT || CONFIG_OV5640_SUPPORT || CONFIG_NT99141_SUPPORT)
- if(s_state->sensor.slv_addr == 0x3c){
- id->PID = SCCB_Read16(s_state->sensor.slv_addr, REG16_CHIDH);
- id->VER = SCCB_Read16(s_state->sensor.slv_addr, REG16_CHIDL);
- vTaskDelay(10 / portTICK_PERIOD_MS);
- ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x", id->PID, id->VER);
- } else if(s_state->sensor.slv_addr == 0x2a){
- id->PID = SCCB_Read16(s_state->sensor.slv_addr, 0x3000);
- id->VER = SCCB_Read16(s_state->sensor.slv_addr, 0x3001);
- vTaskDelay(10 / portTICK_PERIOD_MS);
- ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x", id->PID, id->VER);
- if(config->xclk_freq_hz > 10000000)
- {
- ESP_LOGE(TAG, "NT99141: only XCLK under 10MHz is supported, and XCLK is now set to 10M");
- s_state->sensor.xclk_freq_hz = 10000000;
- }
- } else {
-#endif
- id->PID = SCCB_Read(s_state->sensor.slv_addr, REG_PID);
- id->VER = SCCB_Read(s_state->sensor.slv_addr, REG_VER);
- id->MIDL = SCCB_Read(s_state->sensor.slv_addr, REG_MIDL);
- id->MIDH = SCCB_Read(s_state->sensor.slv_addr, REG_MIDH);
- vTaskDelay(10 / portTICK_PERIOD_MS);
- ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x",
- id->PID, id->VER, id->MIDH, id->MIDL);
-
-#if (CONFIG_OV3660_SUPPORT || CONFIG_OV5640_SUPPORT || CONFIG_NT99141_SUPPORT)
- }
-#endif
-
-
- switch (id->PID) {
-#if CONFIG_OV2640_SUPPORT
- case OV2640_PID:
- *out_camera_model = CAMERA_OV2640;
- ov2640_init(&s_state->sensor);
- break;
-#endif
-#if CONFIG_OV7725_SUPPORT
- case OV7725_PID:
- *out_camera_model = CAMERA_OV7725;
- ov7725_init(&s_state->sensor);
- break;
-#endif
-#if CONFIG_OV3660_SUPPORT
- case OV3660_PID:
- *out_camera_model = CAMERA_OV3660;
- ov3660_init(&s_state->sensor);
- break;
-#endif
-#if CONFIG_OV5640_SUPPORT
- case OV5640_PID:
- *out_camera_model = CAMERA_OV5640;
- ov5640_init(&s_state->sensor);
- break;
-#endif
-#if CONFIG_OV7670_SUPPORT
- case OV7670_PID:
- *out_camera_model = CAMERA_OV7670;
- ov7670_init(&s_state->sensor);
- break;
-#endif
-#if CONFIG_NT99141_SUPPORT
- case NT99141_PID:
- *out_camera_model = CAMERA_NT99141;
- NT99141_init(&s_state->sensor);
- break;
-#endif
- default:
- id->PID = 0;
- *out_camera_model = CAMERA_UNKNOWN;
- camera_disable_out_clock();
- ESP_LOGE(TAG, "Detected camera not supported.");
- return ESP_ERR_CAMERA_NOT_SUPPORTED;
- }
-
- ESP_LOGD(TAG, "Doing SW reset of sensor");
- s_state->sensor.reset(&s_state->sensor);
-
- return ESP_OK;
-}
-
-esp_err_t camera_init(const camera_config_t* config)
-{
- if (!s_state) {
- return ESP_ERR_INVALID_STATE;
- }
- if (s_state->sensor.id.PID == 0) {
- return ESP_ERR_CAMERA_NOT_SUPPORTED;
- }
- memcpy(&s_state->config, config, sizeof(*config));
- esp_err_t err = ESP_OK;
- framesize_t frame_size = (framesize_t) config->frame_size;
- pixformat_t pix_format = (pixformat_t) config->pixel_format;
-
- switch (s_state->sensor.id.PID) {
-#if CONFIG_OV2640_SUPPORT
- case OV2640_PID:
- if (frame_size > FRAMESIZE_UXGA) {
- frame_size = FRAMESIZE_UXGA;
- }
- break;
-#endif
-#if CONFIG_OV7725_SUPPORT
- case OV7725_PID:
- if (frame_size > FRAMESIZE_VGA) {
- frame_size = FRAMESIZE_VGA;
- }
- break;
-#endif
-#if CONFIG_OV3660_SUPPORT
- case OV3660_PID:
- if (frame_size > FRAMESIZE_QXGA) {
- frame_size = FRAMESIZE_QXGA;
- }
- break;
-#endif
-#if CONFIG_OV5640_SUPPORT
- case OV5640_PID:
- if (frame_size > FRAMESIZE_QSXGA) {
- frame_size = FRAMESIZE_QSXGA;
- }
- break;
-#endif
-#if CONFIG_OV7670_SUPPORT
- case OV7670_PID:
- if (frame_size > FRAMESIZE_VGA) {
- frame_size = FRAMESIZE_VGA;
- }
- break;
-#endif
-#if CONFIG_NT99141_SUPPORT
- case NT99141_PID:
- if (frame_size > FRAMESIZE_HD) {
- frame_size = FRAMESIZE_HD;
- }
- break;
-#endif
- default:
- return ESP_ERR_CAMERA_NOT_SUPPORTED;
- }
-
- s_state->width = resolution[frame_size].width;
- s_state->height = resolution[frame_size].height;
-
- if (pix_format == PIXFORMAT_GRAYSCALE) {
- s_state->fb_size = s_state->width * s_state->height;
- if (s_state->sensor.id.PID == OV3660_PID || s_state->sensor.id.PID == OV5640_PID || s_state->sensor.id.PID == NT99141_PID) {
- if (is_hs_mode()) {
- s_state->sampling_mode = SM_0A00_0B00;
- s_state->dma_filter = &dma_filter_yuyv_highspeed;
- } else {
- s_state->sampling_mode = SM_0A0B_0C0D;
- s_state->dma_filter = &dma_filter_yuyv;
- }
- s_state->in_bytes_per_pixel = 1; // camera sends Y8
- } else {
- if (is_hs_mode() && s_state->sensor.id.PID != OV7725_PID) {
- s_state->sampling_mode = SM_0A00_0B00;
- s_state->dma_filter = &dma_filter_grayscale_highspeed;
- } else {
- s_state->sampling_mode = SM_0A0B_0C0D;
- s_state->dma_filter = &dma_filter_grayscale;
- }
- s_state->in_bytes_per_pixel = 2; // camera sends YU/YV
- }
- s_state->fb_bytes_per_pixel = 1; // frame buffer stores Y8
- } else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
- s_state->fb_size = s_state->width * s_state->height * 2;
- if (is_hs_mode() && s_state->sensor.id.PID != OV7725_PID) {
- if(s_state->sensor.id.PID == OV7670_PID) {
- s_state->sampling_mode = SM_0A0B_0B0C;
- }else{
- s_state->sampling_mode = SM_0A00_0B00;
- }
- s_state->dma_filter = &dma_filter_yuyv_highspeed;
- } else {
- s_state->sampling_mode = SM_0A0B_0C0D;
- s_state->dma_filter = &dma_filter_yuyv;
- }
- s_state->in_bytes_per_pixel = 2; // camera sends YU/YV
- s_state->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
- } else if (pix_format == PIXFORMAT_RGB888) {
- s_state->fb_size = s_state->width * s_state->height * 3;
- if (is_hs_mode()) {
- if(s_state->sensor.id.PID == OV7670_PID) {
- s_state->sampling_mode = SM_0A0B_0B0C;
- }else{
- s_state->sampling_mode = SM_0A00_0B00;
- }
- s_state->dma_filter = &dma_filter_rgb888_highspeed;
- } else {
- s_state->sampling_mode = SM_0A0B_0C0D;
- s_state->dma_filter = &dma_filter_rgb888;
- }
- s_state->in_bytes_per_pixel = 2; // camera sends RGB565
- s_state->fb_bytes_per_pixel = 3; // frame buffer stores RGB888
- } else if (pix_format == PIXFORMAT_JPEG) {
- if (s_state->sensor.id.PID != OV2640_PID && s_state->sensor.id.PID != OV3660_PID && s_state->sensor.id.PID != OV5640_PID && s_state->sensor.id.PID != NT99141_PID) {
- ESP_LOGE(TAG, "JPEG format is only supported for ov2640, ov3660 and ov5640");
- err = ESP_ERR_NOT_SUPPORTED;
- goto fail;
- }
- int qp = config->jpeg_quality;
- int compression_ratio_bound = 1;
- if (qp > 10) {
- compression_ratio_bound = 16;
- } else if (qp > 5) {
- compression_ratio_bound = 10;
- } else {
- compression_ratio_bound = 4;
- }
- (*s_state->sensor.set_quality)(&s_state->sensor, qp);
- s_state->in_bytes_per_pixel = 2;
- s_state->fb_bytes_per_pixel = 2;
- s_state->fb_size = (s_state->width * s_state->height * s_state->fb_bytes_per_pixel) / compression_ratio_bound;
- s_state->dma_filter = &dma_filter_jpeg;
- s_state->sampling_mode = SM_0A00_0B00;
- } else {
- ESP_LOGE(TAG, "Requested format is not supported");
- err = ESP_ERR_NOT_SUPPORTED;
- goto fail;
- }
-
- ESP_LOGD(TAG, "in_bpp: %d, fb_bpp: %d, fb_size: %d, mode: %d, width: %d height: %d",
- s_state->in_bytes_per_pixel, s_state->fb_bytes_per_pixel,
- s_state->fb_size, s_state->sampling_mode,
- s_state->width, s_state->height);
-
- i2s_init();
-
- err = dma_desc_init();
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "Failed to initialize I2S and DMA");
- goto fail;
- }
-
- //s_state->fb_size = 75 * 1024;
- err = camera_fb_init(s_state->config.fb_count);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "Failed to allocate frame buffer");
- goto fail;
- }
-
- s_state->data_ready = xQueueCreate(16, sizeof(size_t));
- if (s_state->data_ready == NULL) {
- ESP_LOGE(TAG, "Failed to dma queue");
- err = ESP_ERR_NO_MEM;
- goto fail;
- }
-
- if(s_state->config.fb_count == 1) {
- s_state->frame_ready = xSemaphoreCreateBinary();
- if (s_state->frame_ready == NULL) {
- ESP_LOGE(TAG, "Failed to create semaphore");
- err = ESP_ERR_NO_MEM;
- goto fail;
- }
- } else {
- s_state->fb_in = xQueueCreate(s_state->config.fb_count, sizeof(camera_fb_t *));
- s_state->fb_out = xQueueCreate(1, sizeof(camera_fb_t *));
- if (s_state->fb_in == NULL || s_state->fb_out == NULL) {
- ESP_LOGE(TAG, "Failed to fb queues");
- err = ESP_ERR_NO_MEM;
- goto fail;
- }
- }
-
- //ToDo: core affinity?
-#if CONFIG_CAMERA_CORE0
- if (!xTaskCreatePinnedToCore(&dma_filter_task, "dma_filter", 4096, NULL, 10, &s_state->dma_filter_task, 0))
-#elif CONFIG_CAMERA_CORE1
- if (!xTaskCreatePinnedToCore(&dma_filter_task, "dma_filter", 4096, NULL, 10, &s_state->dma_filter_task, 1))
-#else
- if (!xTaskCreate(&dma_filter_task, "dma_filter", 4096, NULL, 10, &s_state->dma_filter_task))
-#endif
- {
- ESP_LOGE(TAG, "Failed to create DMA filter task");
- err = ESP_ERR_NO_MEM;
- goto fail;
- }
-
- vsync_intr_disable();
- err = gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM);
- if (err != ESP_OK) {
- if (err != ESP_ERR_INVALID_STATE) {
- ESP_LOGE(TAG, "gpio_install_isr_service failed (%x)", err);
- goto fail;
- }
- else {
- ESP_LOGW(TAG, "gpio_install_isr_service already installed");
- }
- }
- err = gpio_isr_handler_add(s_state->config.pin_vsync, &vsync_isr, NULL);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "vsync_isr_handler_add failed (%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", s_state->width, s_state->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 (skip_frame()) {
- err = ESP_ERR_CAMERA_FAILED_TO_SET_OUT_FORMAT;
- goto fail;
- }
- //todo: for some reason the first set of the quality does not work.
- if (pix_format == PIXFORMAT_JPEG) {
- (*s_state->sensor.set_quality)(&s_state->sensor, config->jpeg_quality);
- }
- s_state->sensor.init_status(&s_state->sensor);
- return ESP_OK;
-
-fail:
- esp_camera_deinit();
- return err;
-}
-
-esp_err_t esp_camera_init(const camera_config_t* config)
-{
- camera_model_t camera_model = CAMERA_NONE;
- i2s_gpio_init(config);
- esp_err_t err = camera_probe(config, &camera_model);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "Camera probe failed with error 0x%x", err);
- goto fail;
- }
- if (camera_model == CAMERA_OV7725) {
- ESP_LOGI(TAG, "Detected OV7725 camera");
- if(config->pixel_format == PIXFORMAT_JPEG) {
- ESP_LOGE(TAG, "Camera does not support JPEG");
- err = ESP_ERR_CAMERA_NOT_SUPPORTED;
- goto fail;
- }
- } else if (camera_model == CAMERA_OV2640) {
- ESP_LOGI(TAG, "Detected OV2640 camera");
- } else if (camera_model == CAMERA_OV3660) {
- ESP_LOGI(TAG, "Detected OV3660 camera");
- } else if (camera_model == CAMERA_OV5640) {
- ESP_LOGI(TAG, "Detected OV5640 camera");
- } else if (camera_model == CAMERA_OV7670) {
- ESP_LOGI(TAG, "Detected OV7670 camera");
- } else if (camera_model == CAMERA_NT99141) {
- ESP_LOGI(TAG, "Detected NT99141 camera");
- } else {
- ESP_LOGI(TAG, "Camera not supported");
- err = ESP_ERR_CAMERA_NOT_SUPPORTED;
- goto fail;
- }
- err = camera_init(config);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
- return err;
- }
- return ESP_OK;
-
-fail:
- free(s_state);
- s_state = NULL;
- camera_disable_out_clock();
- return err;
-}
-
-esp_err_t esp_camera_deinit()
-{
- if (s_state == NULL) {
- return ESP_ERR_INVALID_STATE;
- }
- if (s_state->dma_filter_task) {
- vTaskDelete(s_state->dma_filter_task);
- }
- if (s_state->data_ready) {
- vQueueDelete(s_state->data_ready);
- }
- if (s_state->fb_in) {
- vQueueDelete(s_state->fb_in);
- }
- if (s_state->fb_out) {
- vQueueDelete(s_state->fb_out);
- }
- if (s_state->frame_ready) {
- vSemaphoreDelete(s_state->frame_ready);
- }
- gpio_isr_handler_remove(s_state->config.pin_vsync);
- if (s_state->i2s_intr_handle) {
- esp_intr_disable(s_state->i2s_intr_handle);
- esp_intr_free(s_state->i2s_intr_handle);
- }
- dma_desc_deinit();
- camera_fb_deinit();
-
- if(s_state->config.pin_xclk >= 0) {
- camera_disable_out_clock();
- }
- free(s_state);
- s_state = NULL;
- periph_module_disable(PERIPH_I2S0_MODULE);
- return ESP_OK;
-}
-
-#define FB_GET_TIMEOUT (4000 / portTICK_PERIOD_MS)
-
-camera_fb_t* esp_camera_fb_get()
-{
- if (s_state == NULL) {
- return NULL;
- }
- if(!I2S0.conf.rx_start) {
- if(s_state->config.fb_count > 1) {
- ESP_LOGD(TAG, "i2s_run");
- }
- if (i2s_run() != 0) {
- return NULL;
- }
- }
- bool need_yield = false;
- if (s_state->config.fb_count == 1) {
- if (xSemaphoreTake(s_state->frame_ready, FB_GET_TIMEOUT) != pdTRUE){
- i2s_stop(&need_yield);
- ESP_LOGE(TAG, "Failed to get the frame on time!");
- return NULL;
- }
- return (camera_fb_t*)s_state->fb;
- }
- camera_fb_int_t * fb = NULL;
- if(s_state->fb_out) {
- if (xQueueReceive(s_state->fb_out, &fb, FB_GET_TIMEOUT) != pdTRUE) {
- i2s_stop(&need_yield);
- ESP_LOGE(TAG, "Failed to get the frame on time!");
- return NULL;
- }
- }
- return (camera_fb_t*)fb;
-}
-
-void esp_camera_fb_return(camera_fb_t * fb)
-{
- if(fb == NULL || s_state == NULL || s_state->config.fb_count == 1 || s_state->fb_in == NULL) {
- return;
- }
- xQueueSend(s_state->fb_in, &fb, portMAX_DELAY);
-}
-
-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;
- }
-}
diff --git a/code/components/esp32-camera-master/driver/esp_camera.c b/code/components/esp32-camera-master/driver/esp_camera.c
new file mode 100644
index 00000000..9ae1b56c
--- /dev/null
+++ b/code/components/esp32-camera-master/driver/esp_camera.c
@@ -0,0 +1,416 @@
+// 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
+#include
+#include
+#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 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
+};
+
+static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
+{
+ *out_camera_model = CAMERA_NONE;
+ if (s_state != NULL) {
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ s_state = (camera_state_t *) calloc(sizeof(camera_state_t), 1);
+ if (!s_state) {
+ return ESP_ERR_NO_MEM;
+ }
+
+ if (config->pin_xclk >= 0) {
+ ESP_LOGD(TAG, "Enabling XCLK output");
+ CAMERA_ENABLE_OUT_CLOCK(config);
+ }
+
+ if (config->pin_sscb_sda != -1) {
+ ESP_LOGD(TAG, "Initializing SSCB");
+ SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl);
+ }
+
+ if (config->pin_pwdn >= 0) {
+ ESP_LOGD(TAG, "Resetting camera by power down line");
+ gpio_config_t conf = { 0 };
+ conf.pin_bit_mask = 1LL << config->pin_pwdn;
+ conf.mode = GPIO_MODE_OUTPUT;
+ gpio_config(&conf);
+
+ // carefull, logic is inverted compared to reset pin
+ gpio_set_level(config->pin_pwdn, 1);
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+ gpio_set_level(config->pin_pwdn, 0);
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+ }
+
+ if (config->pin_reset >= 0) {
+ ESP_LOGD(TAG, "Resetting camera");
+ gpio_config_t conf = { 0 };
+ conf.pin_bit_mask = 1LL << config->pin_reset;
+ conf.mode = GPIO_MODE_OUTPUT;
+ gpio_config(&conf);
+
+ gpio_set_level(config->pin_reset, 0);
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+ gpio_set_level(config->pin_reset, 1);
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+ }
+
+
+ ESP_LOGD(TAG, "Searching for camera address");
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+
+ uint8_t slv_addr = SCCB_Probe();
+
+ if (slv_addr == 0) {
+ CAMERA_DISABLE_OUT_CLOCK();
+ return ESP_ERR_NOT_FOUND;
+ }
+
+ ESP_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr);
+ s_state->sensor.slv_addr = slv_addr;
+ s_state->sensor.xclk_freq_hz = config->xclk_freq_hz;
+
+ /**
+ * Read sensor ID and then initialize sensor
+ * Attention: Some sensors have the same SCCB address. Therefore, several attempts may be made in the detection process
+ */
+ sensor_id_t *id = &s_state->sensor.id;
+ for (size_t i = 0; i < sizeof(g_sensors) / sizeof(sensor_func_t); i++) {
+ if (g_sensors[i].detect(slv_addr, id)) {
+ camera_sensor_info_t *info = esp_camera_sensor_get_info(id);
+ if (NULL != info) {
+ *out_camera_model = info->model;
+ ESP_LOGI(TAG, "Detected %s camera", info->name);
+ g_sensors[i].init(&s_state->sensor);
+ break;
+ }
+ }
+ }
+
+ if (CAMERA_NONE == *out_camera_model) { //If no supported sensors are detected
+ CAMERA_DISABLE_OUT_CLOCK();
+ ESP_LOGE(TAG, "Detected camera not supported.");
+ return ESP_ERR_NOT_SUPPORTED;
+ }
+
+ ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x",
+ id->PID, id->VER, id->MIDH, id->MIDL);
+
+ ESP_LOGD(TAG, "Doing SW reset of sensor");
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+ s_state->sensor.reset(&s_state->sensor);
+
+ return ESP_OK;
+}
+
+esp_err_t esp_camera_init(const camera_config_t *config)
+{
+ esp_err_t err;
+ err = cam_init(config);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
+ return err;
+ }
+
+ camera_model_t camera_model = CAMERA_NONE;
+ err = camera_probe(config, &camera_model);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Camera probe failed with error 0x%x(%s)", err, esp_err_to_name(err));
+ goto fail;
+ }
+
+ framesize_t frame_size = (framesize_t) config->frame_size;
+ pixformat_t pix_format = (pixformat_t) config->pixel_format;
+
+ if (PIXFORMAT_JPEG == pix_format && (!camera_sensor[camera_model].support_jpeg)) {
+ ESP_LOGE(TAG, "JPEG format is not supported on this sensor");
+ err = ESP_ERR_NOT_SUPPORTED;
+ goto fail;
+ }
+
+ if (frame_size > camera_sensor[camera_model].max_size) {
+ ESP_LOGW(TAG, "The frame size exceeds the maximum for this sensor, it will be forced to the maximum possible value");
+ frame_size = camera_sensor[camera_model].max_size;
+ }
+
+ err = cam_config(config, frame_size, s_state->sensor.id.PID);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Camera config failed with error 0x%x", err);
+ goto fail;
+ }
+
+ s_state->sensor.status.framesize = frame_size;
+ s_state->sensor.pixformat = pix_format;
+ ESP_LOGD(TAG, "Setting frame size to %dx%d", resolution[frame_size].width, resolution[frame_size].height);
+ if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
+ ESP_LOGE(TAG, "Failed to set frame size");
+ err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE;
+ goto fail;
+ }
+ s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
+
+ if (s_state->sensor.id.PID == OV2640_PID) {
+ s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);
+ s_state->sensor.set_bpc(&s_state->sensor, false);
+ s_state->sensor.set_wpc(&s_state->sensor, true);
+ s_state->sensor.set_lenc(&s_state->sensor, true);
+ }
+
+ if (pix_format == PIXFORMAT_JPEG) {
+ s_state->sensor.set_quality(&s_state->sensor, config->jpeg_quality);
+ }
+ s_state->sensor.init_status(&s_state->sensor);
+
+ cam_start();
+
+ return ESP_OK;
+
+fail:
+ esp_camera_deinit();
+ return err;
+}
+
+esp_err_t esp_camera_deinit()
+{
+ esp_err_t ret = cam_deinit();
+ CAMERA_DISABLE_OUT_CLOCK();
+ if (s_state) {
+ SCCB_Deinit();
+
+ free(s_state);
+ s_state = NULL;
+ }
+
+ return ret;
+}
+
+#define FB_GET_TIMEOUT (4000 / portTICK_PERIOD_MS)
+
+camera_fb_t *esp_camera_fb_get()
+{
+ if (s_state == NULL) {
+ return NULL;
+ }
+ camera_fb_t *fb = cam_take(FB_GET_TIMEOUT);
+ //set the frame properties
+ if (fb) {
+ fb->width = resolution[s_state->sensor.status.framesize].width;
+ fb->height = resolution[s_state->sensor.status.framesize].height;
+ fb->format = s_state->sensor.pixformat;
+ }
+ return fb;
+}
+
+void esp_camera_fb_return(camera_fb_t *fb)
+{
+ if (s_state == NULL) {
+ return;
+ }
+ cam_give(fb);
+}
+
+sensor_t *esp_camera_sensor_get()
+{
+ if (s_state == NULL) {
+ return NULL;
+ }
+ return &s_state->sensor;
+}
+
+esp_err_t esp_camera_save_to_nvs(const char *key)
+{
+#if ESP_IDF_VERSION_MAJOR > 3
+ nvs_handle_t handle;
+#else
+ nvs_handle handle;
+#endif
+ esp_err_t ret = nvs_open(key, NVS_READWRITE, &handle);
+
+ if (ret == ESP_OK) {
+ sensor_t *s = esp_camera_sensor_get();
+ if (s != NULL) {
+ ret = nvs_set_blob(handle, CAMERA_SENSOR_NVS_KEY, &s->status, sizeof(camera_status_t));
+ if (ret == ESP_OK) {
+ uint8_t pf = s->pixformat;
+ ret = nvs_set_u8(handle, CAMERA_PIXFORMAT_NVS_KEY, pf);
+ }
+ return ret;
+ } else {
+ return ESP_ERR_CAMERA_NOT_DETECTED;
+ }
+ nvs_close(handle);
+ return ret;
+ } else {
+ return ret;
+ }
+}
+
+esp_err_t esp_camera_load_from_nvs(const char *key)
+{
+#if ESP_IDF_VERSION_MAJOR > 3
+ nvs_handle_t handle;
+#else
+ nvs_handle handle;
+#endif
+ uint8_t pf;
+
+ esp_err_t ret = nvs_open(key, NVS_READWRITE, &handle);
+
+ if (ret == ESP_OK) {
+ sensor_t *s = esp_camera_sensor_get();
+ camera_status_t st;
+ if (s != NULL) {
+ size_t size = sizeof(camera_status_t);
+ ret = nvs_get_blob(handle, CAMERA_SENSOR_NVS_KEY, &st, &size);
+ if (ret == ESP_OK) {
+ s->set_ae_level(s, st.ae_level);
+ s->set_aec2(s, st.aec2);
+ s->set_aec_value(s, st.aec_value);
+ s->set_agc_gain(s, st.agc_gain);
+ s->set_awb_gain(s, st.awb_gain);
+ s->set_bpc(s, st.bpc);
+ s->set_brightness(s, st.brightness);
+ s->set_colorbar(s, st.colorbar);
+ s->set_contrast(s, st.contrast);
+ s->set_dcw(s, st.dcw);
+ s->set_denoise(s, st.denoise);
+ s->set_exposure_ctrl(s, st.aec);
+ s->set_framesize(s, st.framesize);
+ s->set_gain_ctrl(s, st.agc);
+ s->set_gainceiling(s, st.gainceiling);
+ s->set_hmirror(s, st.hmirror);
+ s->set_lenc(s, st.lenc);
+ s->set_quality(s, st.quality);
+ s->set_raw_gma(s, st.raw_gma);
+ s->set_saturation(s, st.saturation);
+ s->set_sharpness(s, st.sharpness);
+ s->set_special_effect(s, st.special_effect);
+ s->set_vflip(s, st.vflip);
+ s->set_wb_mode(s, st.wb_mode);
+ s->set_whitebal(s, st.awb);
+ s->set_wpc(s, st.wpc);
+ }
+ ret = nvs_get_u8(handle, CAMERA_PIXFORMAT_NVS_KEY, &pf);
+ if (ret == ESP_OK) {
+ s->set_pixformat(s, pf);
+ }
+ } else {
+ return ESP_ERR_CAMERA_NOT_DETECTED;
+ }
+ nvs_close(handle);
+ return ret;
+ } else {
+ ESP_LOGW(TAG, "Error (%d) opening nvs key \"%s\"", ret, key);
+ return ret;
+ }
+}
diff --git a/code/components/esp32-camera-master/driver/include/esp_camera.h b/code/components/esp32-camera-master/driver/include/esp_camera.h
index dadd0c09..b6047d31 100644
--- a/code/components/esp32-camera-master/driver/include/esp_camera.h
+++ b/code/components/esp32-camera-master/driver/include/esp_camera.h
@@ -38,7 +38,8 @@
.pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_SVGA,
.jpeg_quality = 10,
- .fb_count = 2
+ .fb_count = 2,
+ .grab_mode = CAMERA_GRAB_WHEN_EMPTY
};
esp_err_t camera_example_init(){
@@ -74,6 +75,22 @@
extern "C" {
#endif
+/**
+ * @brief Configuration structure for camera initialization
+ */
+typedef enum {
+ CAMERA_GRAB_WHEN_EMPTY, /*!< Fills buffers when they are empty. Less resources but first 'fb_count' frames might be old */
+ CAMERA_GRAB_LATEST /*!< Except when 1 frame buffer is used, queue will always contain the last 'fb_count' frames */
+} camera_grab_mode_t;
+
+/**
+ * @brief Camera frame buffer location
+ */
+typedef enum {
+ CAMERA_FB_IN_PSRAM, /*!< Frame buffer is placed in external PSRAM */
+ CAMERA_FB_IN_DRAM /*!< Frame buffer is placed in internal DRAM */
+} camera_fb_location_t;
+
/**
* @brief Configuration structure for camera initialization
*/
@@ -95,7 +112,7 @@ typedef struct {
int pin_href; /*!< GPIO pin for camera HREF line */
int pin_pclk; /*!< GPIO pin for camera PCLK line */
- int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental) */
+ int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode */
ledc_timer_t ledc_timer; /*!< LEDC timer to be used for generating XCLK */
ledc_channel_t ledc_channel; /*!< LEDC channel to be used for generating XCLK */
@@ -105,6 +122,8 @@ typedef struct {
int jpeg_quality; /*!< Quality of JPEG output. 0-63 lower means higher quality */
size_t fb_count; /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) */
+ camera_fb_location_t fb_location; /*!< The location where the frame buffer will be allocated */
+ camera_grab_mode_t grab_mode; /*!< When buffers should be filled */
} camera_config_t;
/**
diff --git a/code/components/esp32-camera-master/driver/include/sensor.h b/code/components/esp32-camera-master/driver/include/sensor.h
index ad6cd890..1f99c154 100644
--- a/code/components/esp32-camera-master/driver/include/sensor.h
+++ b/code/components/esp32-camera-master/driver/include/sensor.h
@@ -11,13 +11,48 @@
#include
#include
-#define NT99141_PID (0x14)
-#define OV9650_PID (0x96)
-#define OV7725_PID (0x77)
-#define OV2640_PID (0x26)
-#define OV3660_PID (0x36)
-#define OV5640_PID (0x56)
-#define OV7670_PID (0x76)
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ OV9650_PID = 0x96,
+ OV7725_PID = 0x77,
+ OV2640_PID = 0x26,
+ OV3660_PID = 0x3660,
+ OV5640_PID = 0x5640,
+ OV7670_PID = 0x76,
+ NT99141_PID = 0x1410,
+ GC2145_PID = 0x2145,
+ GC032A_PID = 0x232a,
+ GC0308_PID = 0x9b,
+} camera_pid_t;
+
+typedef enum {
+ CAMERA_OV7725,
+ CAMERA_OV2640,
+ CAMERA_OV3660,
+ CAMERA_OV5640,
+ CAMERA_OV7670,
+ CAMERA_NT99141,
+ CAMERA_GC2145,
+ CAMERA_GC032A,
+ CAMERA_GC0308,
+ CAMERA_MODEL_MAX,
+ CAMERA_NONE,
+} camera_model_t;
+
+typedef enum {
+ OV2640_SCCB_ADDR = 0x30,// 0x60 >> 1
+ OV5640_SCCB_ADDR = 0x3C,// 0x78 >> 1
+ OV3660_SCCB_ADDR = 0x3C,// 0x78 >> 1
+ OV7725_SCCB_ADDR = 0x21,// 0x42 >> 1
+ OV7670_SCCB_ADDR = 0x21,// 0x42 >> 1
+ NT99141_SCCB_ADDR = 0x2A,// 0x54 >> 1
+ GC2145_SCCB_ADDR = 0x3C,// 0x78 >> 1
+ GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
+ GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
+} camera_sccb_addr_t;
typedef enum {
PIXFORMAT_RGB565, // 2BPP/RGB565
@@ -58,6 +93,15 @@ typedef enum {
FRAMESIZE_INVALID
} framesize_t;
+typedef struct {
+ const camera_model_t model;
+ const char *name;
+ const camera_sccb_addr_t sccb_addr;
+ const camera_pid_t pid;
+ const framesize_t max_size;
+ const bool support_jpeg;
+} camera_sensor_info_t;
+
typedef enum {
ASPECT_RATIO_4X3,
ASPECT_RATIO_3X2,
@@ -101,11 +145,13 @@ typedef struct {
// Resolution table (in sensor.c)
extern const resolution_info_t resolution[];
+// camera sensor table (in sensor.c)
+extern const camera_sensor_info_t camera_sensor[];
typedef struct {
uint8_t MIDH;
uint8_t MIDL;
- uint8_t PID;
+ uint16_t PID;
uint8_t VER;
} sensor_id_t;
@@ -190,4 +236,10 @@ typedef struct _sensor {
int (*set_xclk) (sensor_t *sensor, int timer, int xclk);
} sensor_t;
+camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __SENSOR_H__ */
diff --git a/code/components/esp32-camera-master/driver/private_include/cam_hal.h b/code/components/esp32-camera-master/driver/private_include/cam_hal.h
new file mode 100644
index 00000000..c8e38ed4
--- /dev/null
+++ b/code/components/esp32-camera-master/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
diff --git a/code/components/esp32-camera-master/driver/private_include/camera_common.h b/code/components/esp32-camera-master/driver/private_include/camera_common.h
deleted file mode 100644
index 8a2db5ce..00000000
--- a/code/components/esp32-camera-master/driver/private_include/camera_common.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-#include "esp_err.h"
-#include "esp_intr_alloc.h"
-#include "freertos/FreeRTOS.h"
-#include "freertos/semphr.h"
-#include "freertos/task.h"
-#include "esp_camera.h"
-#include "sensor.h"
-
-#include "esp_system.h"
-#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
-#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
-#include "esp32/rom/lldesc.h"
-#else
-#error Target CONFIG_IDF_TARGET is not supported
-#endif
-#else // ESP32 Before IDF 4.0
-#include "rom/lldesc.h"
-#endif
-
-typedef union {
- struct {
- uint8_t sample2;
- uint8_t unused2;
- uint8_t sample1;
- uint8_t unused1;
- };
- uint32_t val;
-} dma_elem_t;
-
-typedef enum {
- /* camera sends byte sequence: s1, s2, s3, s4, ...
- * fifo receives: 00 s1 00 s2, 00 s2 00 s3, 00 s3 00 s4, ...
- */
- SM_0A0B_0B0C = 0,
- /* camera sends byte sequence: s1, s2, s3, s4, ...
- * fifo receives: 00 s1 00 s2, 00 s3 00 s4, ...
- */
- SM_0A0B_0C0D = 1,
- /* camera sends byte sequence: s1, s2, s3, s4, ...
- * fifo receives: 00 s1 00 00, 00 s2 00 00, 00 s3 00 00, ...
- */
- SM_0A00_0B00 = 3,
-} i2s_sampling_mode_t;
-
diff --git a/code/components/esp32-camera-master/driver/private_include/sccb.h b/code/components/esp32-camera-master/driver/private_include/sccb.h
index 4d5b5b42..ace081a4 100644
--- a/code/components/esp32-camera-master/driver/private_include/sccb.h
+++ b/code/components/esp32-camera-master/driver/private_include/sccb.h
@@ -10,6 +10,7 @@
#define __SCCB_H__
#include
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);
diff --git a/code/components/esp32-camera-master/driver/private_include/xclk.h b/code/components/esp32-camera-master/driver/private_include/xclk.h
index 15ed736e..3d721a61 100644
--- a/code/components/esp32-camera-master/driver/private_include/xclk.h
+++ b/code/components/esp32-camera-master/driver/private_include/xclk.h
@@ -1,6 +1,8 @@
#pragma once
-#include "camera_common.h"
+#include "esp_system.h"
+
+esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
esp_err_t camera_enable_out_clock();
diff --git a/code/components/esp32-camera-master/driver/sccb.c b/code/components/esp32-camera-master/driver/sccb.c
index cb615bbc..1a2c56e2 100644
--- a/code/components/esp32-camera-master/driver/sccb.c
+++ b/code/components/esp32-camera-master/driver/sccb.c
@@ -11,6 +11,7 @@
#include
#include
#include "sccb.h"
+#include "sensor.h"
#include
#include "sdkconfig.h"
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
@@ -36,12 +37,10 @@ const int SCCB_I2C_PORT = 1;
#else
const int SCCB_I2C_PORT = 0;
#endif
-static uint8_t ESP_SLAVE_ADDR = 0x3c;
int SCCB_Init(int pin_sda, int pin_scl)
{
- ESP_LOGI(TAG, "pin_sda %d pin_scl %d\n", pin_sda, pin_scl);
- //log_i("SCCB_Init start");
+ ESP_LOGI(TAG, "pin_sda %d pin_scl %d", pin_sda, pin_scl);
i2c_config_t conf;
memset(&conf, 0, sizeof(i2c_config_t));
conf.mode = I2C_MODE_MASTER;
@@ -56,10 +55,30 @@ int SCCB_Init(int pin_sda, int pin_scl)
return 0;
}
-uint8_t SCCB_Probe()
+int SCCB_Deinit(void)
+{
+ return i2c_driver_delete(SCCB_I2C_PORT);
+}
+
+uint8_t SCCB_Probe(void)
{
uint8_t slave_addr = 0x0;
- while(slave_addr < 0x7f) {
+ // for (size_t i = 1; i < 0x80; i++) {
+ // i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+ // i2c_master_start(cmd);
+ // i2c_master_write_byte(cmd, ( i << 1 ) | WRITE_BIT, ACK_CHECK_EN);
+ // i2c_master_stop(cmd);
+ // esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+ // i2c_cmd_link_delete(cmd);
+ // if( ret == ESP_OK) {
+ // ESP_LOGW(TAG, "Found I2C Device at 0x%02X", i);
+ // }
+ // }
+ for (size_t i = 0; i < CAMERA_MODEL_MAX; i++) {
+ if (slave_addr == camera_sensor[i].sccb_addr) {
+ continue;
+ }
+ slave_addr = camera_sensor[i].sccb_addr;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( slave_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
@@ -67,12 +86,10 @@ uint8_t SCCB_Probe()
esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if( ret == ESP_OK) {
- ESP_SLAVE_ADDR = slave_addr;
- return ESP_SLAVE_ADDR;
+ return slave_addr;
}
- slave_addr++;
}
- return ESP_SLAVE_ADDR;
+ return 0;
}
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
diff --git a/code/components/esp32-camera-master/driver/sensor.c b/code/components/esp32-camera-master/driver/sensor.c
index 2e6d1110..7ebd7af2 100644
--- a/code/components/esp32-camera-master/driver/sensor.c
+++ b/code/components/esp32-camera-master/driver/sensor.c
@@ -1,5 +1,19 @@
+#include
#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},
+};
+
const resolution_info_t resolution[FRAMESIZE_INVALID] = {
{ 96, 96, ASPECT_RATIO_1X1 }, /* 96x96 */
{ 160, 120, ASPECT_RATIO_4X3 }, /* QQVGA */
@@ -26,3 +40,13 @@ const resolution_info_t resolution[FRAMESIZE_INVALID] = {
{ 1088, 1920, ASPECT_RATIO_9X16 }, /* Portrait FHD */
{ 2560, 1920, ASPECT_RATIO_4X3 }, /* QSXGA */
};
+
+camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id)
+{
+ for (int i = 0; i < CAMERA_MODEL_MAX; i++) {
+ if (id->PID == camera_sensor[i].pid) {
+ return (camera_sensor_info_t *)&camera_sensor[i];
+ }
+ }
+ return NULL;
+}
diff --git a/code/components/esp32-camera-master/examples/CMakeLists.txt b/code/components/esp32-camera-master/examples/CMakeLists.txt
new file mode 100644
index 00000000..0a039688
--- /dev/null
+++ b/code/components/esp32-camera-master/examples/CMakeLists.txt
@@ -0,0 +1,9 @@
+# The following lines of boilerplate have to be in your project's
+# CMakeLists in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.5)
+
+set(EXTRA_COMPONENT_DIRS "../")
+
+add_compile_options(-fdiagnostics-color=always)
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(camera_example)
\ No newline at end of file
diff --git a/code/components/esp32-camera-master/examples/main/CMakeLists.txt b/code/components/esp32-camera-master/examples/main/CMakeLists.txt
new file mode 100644
index 00000000..1735fb18
--- /dev/null
+++ b/code/components/esp32-camera-master/examples/main/CMakeLists.txt
@@ -0,0 +1,3 @@
+set(COMPONENT_SRCS take_picture.c)
+set(COMPONENT_ADD_INCLUDEDIRS .)
+register_component()
\ No newline at end of file
diff --git a/code/components/esp32-camera-master/examples/main/component.mk b/code/components/esp32-camera-master/examples/main/component.mk
new file mode 100644
index 00000000..0b9d7585
--- /dev/null
+++ b/code/components/esp32-camera-master/examples/main/component.mk
@@ -0,0 +1,5 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+
diff --git a/code/components/esp32-camera-master/examples/take_picture.c b/code/components/esp32-camera-master/examples/main/take_picture.c
similarity index 89%
rename from code/components/esp32-camera-master/examples/take_picture.c
rename to code/components/esp32-camera-master/examples/main/take_picture.c
index 8a22e8c3..1cbad908 100644
--- a/code/components/esp32-camera-master/examples/take_picture.c
+++ b/code/components/esp32-camera-master/examples/main/take_picture.c
@@ -29,7 +29,6 @@
// ================================ CODE ======================================
-#include
#include
#include
#include
@@ -41,6 +40,8 @@
#include "esp_camera.h"
+#define BOARD_WROVER_KIT 1
+
// WROVER-KIT PIN Map
#ifdef BOARD_WROVER_KIT
@@ -113,11 +114,12 @@ static camera_config_t camera_config = {
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
- .pixel_format = PIXFORMAT_JPEG, //YUV422,GRAYSCALE,RGB565,JPEG
- .frame_size = FRAMESIZE_VGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
+ .pixel_format = PIXFORMAT_RGB565, //YUV422,GRAYSCALE,RGB565,JPEG
+ .frame_size = FRAMESIZE_QVGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
.jpeg_quality = 12, //0-63 lower number means higher quality
- .fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
+ .fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
+ .grab_mode = CAMERA_GRAB_WHEN_EMPTY,
};
static esp_err_t init_camera()
@@ -135,7 +137,9 @@ static esp_err_t init_camera()
void app_main()
{
- init_camera();
+ if(ESP_OK != init_camera()) {
+ return;
+ }
while (1)
{
@@ -144,7 +148,8 @@ void app_main()
// use pic->buf to access the image
ESP_LOGI(TAG, "Picture taken! Its size was: %zu bytes", pic->len);
+ esp_camera_fb_return(pic);
vTaskDelay(5000 / portTICK_RATE_MS);
}
-}
\ No newline at end of file
+}
diff --git a/code/components/esp32-camera-master/examples/sdkconfig.defaults b/code/components/esp32-camera-master/examples/sdkconfig.defaults
new file mode 100644
index 00000000..e5ac4557
--- /dev/null
+++ b/code/components/esp32-camera-master/examples/sdkconfig.defaults
@@ -0,0 +1,17 @@
+CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
+CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
+CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
+
+CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
+CONFIG_PARTITION_TABLE_OFFSET=0x10000
+
+CONFIG_FREERTOS_HZ=1000
+CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
+CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
+
+CONFIG_SPIRAM_SUPPORT=y
+CONFIG_ESP32_SPIRAM_SUPPORT=y
+CONFIG_ESP32S2_SPIRAM_SUPPORT=y
+CONFIG_ESP32S3_SPIRAM_SUPPORT=y
+CONFIG_SPIRAM_SPEED_80M=y
+
diff --git a/code/components/esp32-camera-master/idf_component.yml b/code/components/esp32-camera-master/idf_component.yml
deleted file mode 100644
index d8dc8e15..00000000
--- a/code/components/esp32-camera-master/idf_component.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-version: "1.0.0"
-description: This package hosts ESP32 compatible driver for OV2640 image sensors. Additionally it provides a few tools, which allow converting the captured frame data to the more common BMP and JPEG formats.
-url: https://github.com/espressif/esp32-camera
diff --git a/code/components/esp32-camera-master/library.json b/code/components/esp32-camera-master/library.json
index 0fbf0265..322e932f 100644
--- a/code/components/esp32-camera-master/library.json
+++ b/code/components/esp32-camera-master/library.json
@@ -16,6 +16,7 @@
"-Idriver/private_include",
"-Iconversions/private_include",
"-Isensors/private_include",
+ "-Itarget/private_include",
"-fno-rtti"
],
"includeDir": ".",
diff --git a/code/components/esp32-camera-master/sensors/gc0308.c b/code/components/esp32-camera-master/sensors/gc0308.c
new file mode 100644
index 00000000..19064d3a
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/gc0308.c
@@ -0,0 +1,465 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include "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;
+
+#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;
+}
diff --git a/code/components/esp32-camera-master/sensors/gc032a.c b/code/components/esp32-camera-master/sensors/gc032a.c
new file mode 100644
index 00000000..612e17b1
--- /dev/null
+++ b/code/components/esp32-camera-master/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
+#include
+#include
+#include
+#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;
+}
diff --git a/code/components/esp32-camera-master/sensors/gc2145.c b/code/components/esp32-camera-master/sensors/gc2145.c
new file mode 100644
index 00000000..31130829
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/gc2145.c
@@ -0,0 +1,475 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include "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;
+
+#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;
+}
diff --git a/code/components/esp32-camera-master/sensors/nt99141.c b/code/components/esp32-camera-master/sensors/nt99141.c
index 07a9cc4a..86a8b8a0 100644
--- a/code/components/esp32-camera-master/sensors/nt99141.c
+++ b/code/components/esp32-camera-master/sensors/nt99141.c
@@ -10,6 +10,7 @@
#include
#include
#include "sccb.h"
+#include "xclk.h"
#include "nt99141.h"
#include "nt99141_regs.h"
#include "nt99141_settings.h"
@@ -144,28 +145,6 @@ static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value
#define write_reg_bits(slv_addr, reg, mask, enable) set_reg_bits(slv_addr, reg, 0, mask, enable?mask:0)
-static int calc_sysclk(int xclk, bool pll_bypass, int pll_multiplier, int pll_sys_div, int pll_pre_div, bool pll_root_2x, int pll_seld5, bool pclk_manual, int pclk_div)
-{
- const int pll_pre_div2x_map[] = { 2, 3, 4, 6 };//values are multiplied by two to avoid floats
- const int pll_seld52x_map[] = { 2, 2, 4, 5 };
-
- if (!pll_sys_div) {
- pll_sys_div = 1;
- }
-
- int pll_pre_div2x = pll_pre_div2x_map[pll_pre_div];
- int pll_root_div = pll_root_2x ? 2 : 1;
- int pll_seld52x = pll_seld52x_map[pll_seld5];
-
- int VCO = (xclk / 1000) * pll_multiplier * pll_root_div * 2 / pll_pre_div2x;
- int PLLCLK = pll_bypass ? (xclk) : (VCO * 1000 * 2 / pll_sys_div / pll_seld52x);
- int PCLK = PLLCLK / 2 / ((pclk_manual && pclk_div) ? pclk_div : 1);
- int SYSCLK = PLLCLK / 4;
-
- ESP_LOGD(TAG, "Calculated VCO: %d Hz, PLLCLK: %d Hz, SYSCLK: %d Hz, PCLK: %d Hz", VCO * 1000, PLLCLK, SYSCLK, PCLK);
- return SYSCLK;
-}
-
static int set_pll(sensor_t *sensor, bool bypass, uint8_t multiplier, uint8_t sys_div, uint8_t pre_div, bool root_2x, uint8_t seld5, bool pclk_manual, uint8_t pclk_div)
{
return -1;
@@ -309,7 +288,7 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
ret = write_regs(sensor->slv_addr, sensor_framesize_VGA);
}
- return 0;
+ return ret;
}
static int set_hmirror(sensor_t *sensor, int enable)
@@ -682,7 +661,6 @@ static int set_brightness(sensor_t *sensor, int level)
{
int ret = 0;
uint8_t value = 0;
- bool negative = false;
switch (level) {
case 3:
@@ -699,17 +677,14 @@ static int set_brightness(sensor_t *sensor, int level)
case -1:
value = 0x78;
- negative = true;
break;
case -2:
value = 0x70;
- negative = true;
break;
case -3:
value = 0x60;
- negative = true;
break;
default: // 0
@@ -730,7 +705,6 @@ static int set_contrast(sensor_t *sensor, int level)
{
int ret = 0;
uint8_t value1 = 0, value2 = 0 ;
- bool negative = false;
switch (level) {
case 3:
@@ -947,7 +921,6 @@ static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, i
return set_pll(sensor, bypass > 0, multiplier, sys_div, pre_div, root_2x > 0, seld5, pclk_manual > 0, pclk_div);
}
-esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
@@ -961,6 +934,23 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk)
return ret;
}
+int nt99141_detect(int slv_addr, sensor_id_t *id)
+{
+ if (NT99141_SCCB_ADDR == slv_addr) {
+ SCCB_Write16(slv_addr, 0x3008, 0x01);//bank sensor
+ uint16_t h = SCCB_Read16(slv_addr, 0x3000);
+ uint16_t l = SCCB_Read16(slv_addr, 0x3001);
+ uint16_t PID = (h<<8) | l;
+ if (NT99141_PID == PID) {
+ id->PID = PID;
+ return PID;
+ } else {
+ ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+ }
+ }
+ return 0;
+}
+
static int init_status(sensor_t *sensor)
{
sensor->status.brightness = 0;
@@ -991,7 +981,7 @@ static int init_status(sensor_t *sensor)
return 0;
}
-int NT99141_init(sensor_t *sensor)
+int nt99141_init(sensor_t *sensor)
{
sensor->reset = reset;
sensor->set_pixformat = set_pixformat;
diff --git a/code/components/esp32-camera-master/sensors/ov2640.c b/code/components/esp32-camera-master/sensors/ov2640.c
index 811023c0..7e3d7717 100644
--- a/code/components/esp32-camera-master/sensors/ov2640.c
+++ b/code/components/esp32-camera-master/sensors/ov2640.c
@@ -10,6 +10,7 @@
#include
#include
#include "sccb.h"
+#include "xclk.h"
#include "ov2640.h"
#include "ov2640_regs.h"
#include "ov2640_settings.h"
@@ -149,7 +150,7 @@ static int set_window(sensor_t *sensor, ov2640_sensor_mode_t mode, int offset_x,
{VSIZE, max_y & 0xFF},
{XOFFL, offset_x & 0xFF},
{YOFFL, offset_y & 0xFF},
- {VHYX, ((max_y >> 1) & 0X80) | ((offset_y >> 4) & 0X70) | ((max_x >> 5) & 0X08) | ((offset_y >> 8) & 0X07)},
+ {VHYX, ((max_y >> 1) & 0X80) | ((offset_y >> 4) & 0X70) | ((max_x >> 5) & 0X08) | ((offset_x >> 8) & 0X07)},
{TEST, (max_x >> 2) & 0X80},
{ZMOW, (w)&0xFF},
{ZMOH, (h)&0xFF},
@@ -157,26 +158,40 @@ static int set_window(sensor_t *sensor, ov2640_sensor_mode_t mode, int offset_x,
{0, 0}
};
- c.pclk_auto = 0;
- c.pclk_div = 8;
- c.clk_2x = 0;
- c.clk_div = 0;
-
- if(sensor->pixformat != PIXFORMAT_JPEG){
- c.pclk_auto = 1;
+ if (sensor->pixformat == PIXFORMAT_JPEG) {
+ c.clk_2x = 0;
+ c.clk_div = 0;
+ c.pclk_auto = 0;
+ c.pclk_div = 8;
+ if(mode == OV2640_MODE_UXGA) {
+ c.pclk_div = 12;
+ }
+ // if (sensor->xclk_freq_hz == 16000000) {
+ // c.pclk_div = c.pclk_div / 2;
+ // }
+ } else {
+#if CONFIG_IDF_TARGET_ESP32
+ c.clk_2x = 0;
+#else
+ c.clk_2x = 1;
+#endif
c.clk_div = 7;
+ c.pclk_auto = 1;
+ c.pclk_div = 8;
+ if (mode == OV2640_MODE_CIF) {
+ c.clk_div = 3;
+ } else if(mode == OV2640_MODE_UXGA) {
+ c.pclk_div = 12;
+ }
}
+ ESP_LOGI(TAG, "Set PLL: clk_2x: %u, clk_div: %u, pclk_auto: %u, pclk_div: %u", c.clk_2x, c.clk_div, c.pclk_auto, c.pclk_div);
if (mode == OV2640_MODE_CIF) {
regs = ov2640_settings_to_cif;
- if(sensor->pixformat != PIXFORMAT_JPEG){
- c.clk_div = 3;
- }
} else if (mode == OV2640_MODE_SVGA) {
regs = ov2640_settings_to_svga;
} else {
regs = ov2640_settings_to_uxga;
- c.pclk_div = 12;
}
WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_BYPAS);
@@ -480,7 +495,6 @@ static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, i
return -1;
}
-esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
@@ -531,6 +545,24 @@ static int init_status(sensor_t *sensor){
return 0;
}
+int ov2640_detect(int slv_addr, sensor_id_t *id)
+{
+ if (OV2640_SCCB_ADDR == slv_addr) {
+ SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
+ uint16_t PID = SCCB_Read(slv_addr, 0x0A);
+ if (OV2640_PID == PID) {
+ id->PID = PID;
+ id->VER = SCCB_Read(slv_addr, REG_VER);
+ id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
+ id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
+ return PID;
+ } else {
+ ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+ }
+ }
+ return 0;
+}
+
int ov2640_init(sensor_t *sensor)
{
sensor->reset = reset;
diff --git a/code/components/esp32-camera-master/sensors/ov3660.c b/code/components/esp32-camera-master/sensors/ov3660.c
index 723ec5c6..b9ebdba3 100644
--- a/code/components/esp32-camera-master/sensors/ov3660.c
+++ b/code/components/esp32-camera-master/sensors/ov3660.c
@@ -10,6 +10,7 @@
#include
#include
#include "sccb.h"
+#include "xclk.h"
#include "ov3660.h"
#include "ov3660_regs.h"
#include "ov3660_settings.h"
@@ -142,7 +143,7 @@ static int calc_sysclk(int xclk, bool pll_bypass, int pll_multiplier, int pll_sy
int PCLK = PLLCLK / 2 / ((pclk_manual && pclk_div)?pclk_div:1);
int SYSCLK = PLLCLK / 4;
- ESP_LOGD(TAG, "Calculated VCO: %d Hz, PLLCLK: %d Hz, SYSCLK: %d Hz, PCLK: %d Hz", VCO*1000, PLLCLK, SYSCLK, PCLK);
+ ESP_LOGI(TAG, "Calculated VCO: %d Hz, PLLCLK: %d Hz, SYSCLK: %d Hz, PCLK: %d Hz", VCO*1000, PLLCLK, SYSCLK, PCLK);
return SYSCLK;
}
@@ -310,13 +311,13 @@ static int set_image_options(sensor_t *sensor)
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret = 0;
- framesize_t old_framesize = sensor->status.framesize;
- sensor->status.framesize = framesize;
if(framesize > FRAMESIZE_QXGA){
- ESP_LOGE(TAG, "Invalid framesize: %u", framesize);
- return -1;
+ ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
+ framesize = FRAMESIZE_QXGA;
}
+ framesize_t old_framesize = sensor->status.framesize;
+ sensor->status.framesize = framesize;
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
aspect_ratio_t ratio = resolution[sensor->status.framesize].aspect_ratio;
@@ -355,7 +356,7 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
}
if (sensor->pixformat == PIXFORMAT_JPEG) {
- if (framesize == FRAMESIZE_QXGA) {
+ if (framesize == FRAMESIZE_QXGA || sensor->xclk_freq_hz == 16000000) {
//40MHz SYSCLK and 10MHz PCLK
ret = set_pll(sensor, false, 24, 1, 3, false, 0, true, 8);
} else {
@@ -363,12 +364,16 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
ret = set_pll(sensor, false, 30, 1, 3, false, 0, true, 10);
}
} else {
- if (framesize > FRAMESIZE_CIF) {
- //10MHz SYSCLK and 10MHz PCLK (6.19 FPS)
- ret = set_pll(sensor, false, 2, 1, 0, false, 0, true, 2);
+ //tuned for 16MHz XCLK and 8MHz PCLK
+ if (framesize > FRAMESIZE_HVGA) {
+ //8MHz SYSCLK and 8MHz PCLK (4.44 FPS)
+ ret = set_pll(sensor, false, 4, 1, 0, false, 2, true, 2);
+ } else if (framesize >= FRAMESIZE_QVGA) {
+ //16MHz SYSCLK and 8MHz PCLK (10.25 FPS)
+ ret = set_pll(sensor, false, 8, 1, 0, false, 2, true, 4);
} else {
- //25MHz SYSCLK and 10MHz PCLK (15.45 FPS)
- ret = set_pll(sensor, false, 5, 1, 0, false, 0, true, 5);
+ //32MHz SYSCLK and 8MHz PCLK (17.77 FPS)
+ ret = set_pll(sensor, false, 8, 1, 0, false, 0, true, 8);
}
}
@@ -953,7 +958,6 @@ static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, i
return set_pll(sensor, bypass > 0, multiplier, sys_div, pre_div, root_2x > 0, seld5, pclk_manual > 0, pclk_div);
}
-esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
@@ -992,6 +996,22 @@ static int init_status(sensor_t *sensor)
return 0;
}
+int ov3660_detect(int slv_addr, sensor_id_t *id)
+{
+ if (OV3660_SCCB_ADDR == slv_addr) {
+ uint8_t h = SCCB_Read16(slv_addr, 0x300A);
+ uint8_t l = SCCB_Read16(slv_addr, 0x300B);
+ uint16_t PID = (h<<8) | l;
+ if (OV3660_PID == PID) {
+ id->PID = PID;
+ return PID;
+ } else {
+ ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+ }
+ }
+ return 0;
+}
+
int ov3660_init(sensor_t *sensor)
{
sensor->reset = reset;
diff --git a/code/components/esp32-camera-master/sensors/ov5640.c b/code/components/esp32-camera-master/sensors/ov5640.c
index e7adcf46..a32b374f 100644
--- a/code/components/esp32-camera-master/sensors/ov5640.c
+++ b/code/components/esp32-camera-master/sensors/ov5640.c
@@ -10,6 +10,7 @@
#include
#include
#include "sccb.h"
+#include "xclk.h"
#include "ov5640.h"
#include "ov5640_regs.h"
#include "ov5640_settings.h"
@@ -196,7 +197,7 @@ static int calc_sysclk(int xclk, bool pll_bypass, int pll_multiplier, int pll_sy
unsigned int SYSCLK = PLL_CLK / 4;
- ESP_LOGD(TAG, "Calculated XVCLK: %d Hz, REFIN: %u Hz, VCO: %u Hz, PLL_CLK: %u Hz, SYSCLK: %u Hz, PCLK: %u Hz", xclk, REFIN, VCO, PLL_CLK, SYSCLK, PCLK);
+ ESP_LOGI(TAG, "Calculated XVCLK: %d Hz, REFIN: %u Hz, VCO: %u Hz, PLL_CLK: %u Hz, SYSCLK: %u Hz, PCLK: %u Hz", xclk, REFIN, VCO, PLL_CLK, SYSCLK, PCLK);
return SYSCLK;
}
@@ -209,6 +210,7 @@ static int set_pll(sensor_t *sensor, bool bypass, uint8_t multiplier, uint8_t sy
if(multiplier > 127){
multiplier &= 0xFE;//only even integers above 127
}
+ ESP_LOGI(TAG, "Set PLL: bypass: %u, multiplier: %u, sys_div: %u, pre_div: %u, root_2x: %u, pclk_root_div: %u, pclk_manual: %u, pclk_div: %u", bypass, multiplier, sys_div, pre_div, root_2x, pclk_root_div, pclk_manual, pclk_div);
calc_sysclk(sensor->xclk_freq_hz, bypass, multiplier, sys_div, pre_div, root_2x, pclk_root_div, pclk_manual, pclk_div);
@@ -432,14 +434,22 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
if (sensor->pixformat == PIXFORMAT_JPEG) {
//10MHz PCLK
uint8_t sys_mul = 200;
- if(framesize < FRAMESIZE_QVGA){
+ if(framesize < FRAMESIZE_QVGA || sensor->xclk_freq_hz == 16000000){
sys_mul = 160;
} else if(framesize < FRAMESIZE_XGA){
sys_mul = 180;
}
ret = set_pll(sensor, false, sys_mul, 4, 2, false, 2, true, 4);
+ //Set PLL: bypass: 0, multiplier: sys_mul, sys_div: 4, pre_div: 2, root_2x: 0, pclk_root_div: 2, pclk_manual: 1, pclk_div: 4
} else {
- ret = set_pll(sensor, false, 10, 1, 1, false, 1, true, 4);
+ //ret = set_pll(sensor, false, 8, 1, 1, false, 1, true, 4);
+ if (framesize > FRAMESIZE_HVGA) {
+ ret = set_pll(sensor, false, 10, 1, 2, false, 1, true, 2);
+ } else if (framesize >= FRAMESIZE_QVGA) {
+ ret = set_pll(sensor, false, 8, 1, 1, false, 1, true, 4);
+ } else {
+ ret = set_pll(sensor, false, 20, 1, 1, false, 1, true, 8);
+ }
}
if (ret == 0) {
@@ -1025,7 +1035,6 @@ static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, i
return ret;
}
-esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
@@ -1064,6 +1073,22 @@ static int init_status(sensor_t *sensor)
return 0;
}
+int ov5640_detect(int slv_addr, sensor_id_t *id)
+{
+ if (OV5640_SCCB_ADDR == slv_addr) {
+ uint8_t h = SCCB_Read16(slv_addr, 0x300A);
+ uint8_t l = SCCB_Read16(slv_addr, 0x300B);
+ uint16_t PID = (h<<8) | l;
+ if (OV5640_PID == PID) {
+ id->PID = PID;
+ return PID;
+ } else {
+ ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+ }
+ }
+ return 0;
+}
+
int ov5640_init(sensor_t *sensor)
{
sensor->reset = reset;
diff --git a/code/components/esp32-camera-master/sensors/ov7670.c b/code/components/esp32-camera-master/sensors/ov7670.c
index 285fe134..ee64de27 100644
--- a/code/components/esp32-camera-master/sensors/ov7670.c
+++ b/code/components/esp32-camera-master/sensors/ov7670.c
@@ -45,7 +45,7 @@ static struct regval_list ov7670_default_regs[] = {
{CLKRC, 0x00},
{DBLV, 0x4A},
- {COM10, COM10_VSYNC_NEG | COM10_PCLK_MASK},
+ {COM10, COM10_VSYNC_NEG | COM10_PCLK_FREE},
/* Improve white balance */
{COM4, 0x40},
@@ -393,6 +393,24 @@ static int init_status(sensor_t *sensor)
static int set_dummy(sensor_t *sensor, int val){ return -1; }
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
+int ov7670_detect(int slv_addr, sensor_id_t *id)
+{
+ if (OV7670_SCCB_ADDR == slv_addr) {
+ SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
+ uint16_t PID = SCCB_Read(slv_addr, 0x0A);
+ if (OV7670_PID == PID) {
+ id->PID = PID;
+ id->VER = SCCB_Read(slv_addr, REG_VER);
+ id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
+ id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
+ return PID;
+ } else {
+ ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+ }
+ }
+ return 0;
+}
+
int ov7670_init(sensor_t *sensor)
{
// Set function pointers
diff --git a/code/components/esp32-camera-master/sensors/ov7725.c b/code/components/esp32-camera-master/sensors/ov7725.c
index bb315739..9418a82f 100644
--- a/code/components/esp32-camera-master/sensors/ov7725.c
+++ b/code/components/esp32-camera-master/sensors/ov7725.c
@@ -11,6 +11,7 @@
#include
#include
#include "sccb.h"
+#include "xclk.h"
#include "ov7725.h"
#include "ov7725_regs.h"
#include "freertos/FreeRTOS.h"
@@ -58,10 +59,10 @@ static const uint8_t default_regs[][2] = {
{COM8, 0xF0},
{COM6, 0xC5},
{COM9, 0x11},
- {COM10, COM10_VSYNC_NEG | COM10_PCLK_MASK}, //Invert VSYNC and MASK PCLK
+ {COM10, COM10_VSYNC_NEG | COM10_PCLK_FREE}, //Invert VSYNC and MASK PCLK
{BDBASE, 0x7F},
{DBSTEP, 0x03},
- {AEW, 0x96},
+ {AEW, 0x75},
{AEB, 0x64},
{VPT, 0xA1},
{EXHCL, 0x00},
@@ -493,7 +494,6 @@ static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning){return -1;}
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div){return -1;}
-esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
@@ -502,6 +502,24 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk)
return ret;
}
+int ov7725_detect(int slv_addr, sensor_id_t *id)
+{
+ if (OV7725_SCCB_ADDR == slv_addr) {
+ SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
+ uint16_t PID = SCCB_Read(slv_addr, 0x0A);
+ if (OV7725_PID == PID) {
+ id->PID = PID;
+ id->VER = SCCB_Read(slv_addr, REG_VER);
+ id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
+ id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
+ return PID;
+ } else {
+ ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+ }
+ }
+ return 0;
+}
+
int ov7725_init(sensor_t *sensor)
{
// Set function pointers
diff --git a/code/components/esp32-camera-master/sensors/private_include/gc0308.h b/code/components/esp32-camera-master/sensors/private_include/gc0308.h
new file mode 100644
index 00000000..edffca1e
--- /dev/null
+++ b/code/components/esp32-camera-master/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
diff --git a/code/components/esp32-camera-master/sensors/private_include/gc0308_regs.h b/code/components/esp32-camera-master/sensors/private_include/gc0308_regs.h
new file mode 100644
index 00000000..f1cb4532
--- /dev/null
+++ b/code/components/esp32-camera-master/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__
diff --git a/code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h b/code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h
new file mode 100644
index 00000000..32ef3816
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h
@@ -0,0 +1,245 @@
+#ifndef _GC0308_SETTINGS_H_
+#define _GC0308_SETTINGS_H_
+
+#include
+
+#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
diff --git a/code/components/esp32-camera-master/sensors/private_include/gc032a.h b/code/components/esp32-camera-master/sensors/private_include/gc032a.h
new file mode 100644
index 00000000..7679f070
--- /dev/null
+++ b/code/components/esp32-camera-master/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__
diff --git a/code/components/esp32-camera-master/sensors/private_include/gc032a_regs.h b/code/components/esp32-camera-master/sensors/private_include/gc032a_regs.h
new file mode 100644
index 00000000..5de59d1d
--- /dev/null
+++ b/code/components/esp32-camera-master/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__
diff --git a/code/components/esp32-camera-master/sensors/private_include/gc032a_settings.h b/code/components/esp32-camera-master/sensors/private_include/gc032a_settings.h
new file mode 100644
index 00000000..a19ffc7c
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/private_include/gc032a_settings.h
@@ -0,0 +1,401 @@
+#ifndef _GC032A_SETTINGS_H_
+#define _GC032A_SETTINGS_H_
+
+#include
+#include
+#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
diff --git a/code/components/esp32-camera-master/sensors/private_include/gc2145.h b/code/components/esp32-camera-master/sensors/private_include/gc2145.h
new file mode 100644
index 00000000..6c5b60f7
--- /dev/null
+++ b/code/components/esp32-camera-master/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__
diff --git a/code/components/esp32-camera-master/sensors/private_include/gc2145_regs.h b/code/components/esp32-camera-master/sensors/private_include/gc2145_regs.h
new file mode 100644
index 00000000..b034a168
--- /dev/null
+++ b/code/components/esp32-camera-master/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__
diff --git a/code/components/esp32-camera-master/sensors/private_include/gc2145_settings.h b/code/components/esp32-camera-master/sensors/private_include/gc2145_settings.h
new file mode 100644
index 00000000..879fd53b
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/private_include/gc2145_settings.h
@@ -0,0 +1,719 @@
+
+#include
+
+#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},
+};
diff --git a/code/components/esp32-camera-master/sensors/private_include/nt99141.h b/code/components/esp32-camera-master/sensors/private_include/nt99141.h
index 287a7429..8b0c562b 100644
--- a/code/components/esp32-camera-master/sensors/private_include/nt99141.h
+++ b/code/components/esp32-camera-master/sensors/private_include/nt99141.h
@@ -11,6 +11,24 @@
#include "sensor.h"
-int NT99141_init(sensor_t *sensor);
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ * 0: Can't detect this sensor
+ * Nonzero: This sensor has been detected
+ */
+int nt99141_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ * Always 0
+ */
+int nt99141_init(sensor_t *sensor);
#endif // __NT99141_H__
diff --git a/code/components/esp32-camera-master/sensors/private_include/ov2640.h b/code/components/esp32-camera-master/sensors/private_include/ov2640.h
index a8904992..342ab213 100644
--- a/code/components/esp32-camera-master/sensors/private_include/ov2640.h
+++ b/code/components/esp32-camera-master/sensors/private_include/ov2640.h
@@ -9,5 +9,24 @@
#ifndef __OV2640_H__
#define __OV2640_H__
#include "sensor.h"
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ * 0: Can't detect this sensor
+ * Nonzero: This sensor has been detected
+ */
+int ov2640_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ * Always 0
+ */
int ov2640_init(sensor_t *sensor);
+
#endif // __OV2640_H__
diff --git a/code/components/esp32-camera-master/sensors/private_include/ov2640_regs.h b/code/components/esp32-camera-master/sensors/private_include/ov2640_regs.h
index eb096b4b..8f47333f 100644
--- a/code/components/esp32-camera-master/sensors/private_include/ov2640_regs.h
+++ b/code/components/esp32-camera-master/sensors/private_include/ov2640_regs.h
@@ -120,8 +120,8 @@ typedef enum {
#define HSTOP 0x18
#define VSTART 0x19
#define VSTOP 0x1A
-#define MIDH 0x1C
-#define MIDL 0x1D
+#define REG_MIDH 0x1C
+#define REG_MIDL 0x1D
#define AEW 0x24
#define AEB 0x25
#define VV 0x26
diff --git a/code/components/esp32-camera-master/sensors/private_include/ov3660.h b/code/components/esp32-camera-master/sensors/private_include/ov3660.h
index 8e5ae3cb..341d6886 100644
--- a/code/components/esp32-camera-master/sensors/private_include/ov3660.h
+++ b/code/components/esp32-camera-master/sensors/private_include/ov3660.h
@@ -11,6 +11,24 @@
#include "sensor.h"
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ * 0: Can't detect this sensor
+ * Nonzero: This sensor has been detected
+ */
+int ov3660_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ * Always 0
+ */
int ov3660_init(sensor_t *sensor);
#endif // __OV3660_H__
diff --git a/code/components/esp32-camera-master/sensors/private_include/ov5640.h b/code/components/esp32-camera-master/sensors/private_include/ov5640.h
index 7b572ad3..120ae720 100644
--- a/code/components/esp32-camera-master/sensors/private_include/ov5640.h
+++ b/code/components/esp32-camera-master/sensors/private_include/ov5640.h
@@ -4,6 +4,24 @@
#include "sensor.h"
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ * 0: Can't detect this sensor
+ * Nonzero: This sensor has been detected
+ */
+int ov5640_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ * Always 0
+ */
int ov5640_init(sensor_t *sensor);
#endif // __OV5640_H__
diff --git a/code/components/esp32-camera-master/sensors/private_include/ov7670.h b/code/components/esp32-camera-master/sensors/private_include/ov7670.h
index cdf845cf..b3a645a7 100644
--- a/code/components/esp32-camera-master/sensors/private_include/ov7670.h
+++ b/code/components/esp32-camera-master/sensors/private_include/ov7670.h
@@ -10,5 +10,24 @@
#define __OV7670_H__
#include "sensor.h"
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ * 0: Can't detect this sensor
+ * Nonzero: This sensor has been detected
+ */
+int ov7670_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ * Always 0
+ */
int ov7670_init(sensor_t *sensor);
+
#endif // __OV7670_H__
diff --git a/code/components/esp32-camera-master/sensors/private_include/ov7725.h b/code/components/esp32-camera-master/sensors/private_include/ov7725.h
index f8c35165..291b2668 100644
--- a/code/components/esp32-camera-master/sensors/private_include/ov7725.h
+++ b/code/components/esp32-camera-master/sensors/private_include/ov7725.h
@@ -10,5 +10,24 @@
#define __OV7725_H__
#include "sensor.h"
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ * 0: Can't detect this sensor
+ * Nonzero: This sensor has been detected
+ */
+int ov7725_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ * Always 0
+ */
int ov7725_init(sensor_t *sensor);
+
#endif // __OV7725_H__
diff --git a/code/components/esp32-camera-master/target/esp32/ll_cam.c b/code/components/esp32-camera-master/target/esp32/ll_cam.c
new file mode 100644
index 00000000..e513205d
--- /dev/null
+++ b/code/components/esp32-camera-master/target/esp32/ll_cam.c
@@ -0,0 +1,522 @@
+// 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
+#include
+#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"
+
+static const char *TAG = "esp32 ll_cam";
+
+#define I2S_ISR_ENABLE(i) {I2S0.int_clr.i = 1;I2S0.int_ena.i = 1;}
+#define I2S_ISR_DISABLE(i) {I2S0.int_ena.i = 0;I2S0.int_clr.i = 1;}
+
+typedef union {
+ struct {
+ uint32_t sample2:8;
+ uint32_t unused2:8;
+ uint32_t sample1:8;
+ uint32_t unused1:8;
+ };
+ uint32_t val;
+} dma_elem_t;
+
+typedef enum {
+ /* camera sends byte sequence: s1, s2, s3, s4, ...
+ * fifo receives: 00 s1 00 s2, 00 s2 00 s3, 00 s3 00 s4, ...
+ */
+ SM_0A0B_0B0C = 0,
+ /* camera sends byte sequence: s1, s2, s3, s4, ...
+ * fifo receives: 00 s1 00 s2, 00 s3 00 s4, ...
+ */
+ SM_0A0B_0C0D = 1,
+ /* camera sends byte sequence: s1, s2, s3, s4, ...
+ * fifo receives: 00 s1 00 00, 00 s2 00 00, 00 s3 00 00, ...
+ */
+ SM_0A00_0B00 = 3,
+} i2s_sampling_mode_t;
+
+typedef size_t (*dma_filter_t)(uint8_t* dst, const uint8_t* src, size_t len);
+
+static i2s_sampling_mode_t sampling_mode = SM_0A00_0B00;
+
+static size_t ll_cam_bytes_per_sample(i2s_sampling_mode_t mode)
+{
+ switch(mode) {
+ case SM_0A00_0B00:
+ return 4;
+ case SM_0A0B_0B0C:
+ return 4;
+ case SM_0A0B_0C0D:
+ return 2;
+ default:
+ assert(0 && "invalid sampling mode");
+ return 0;
+ }
+}
+
+static size_t IRAM_ATTR ll_cam_dma_filter_jpeg(uint8_t* dst, const uint8_t* src, size_t len)
+{
+ const dma_elem_t* dma_el = (const dma_elem_t*)src;
+ size_t elements = len / sizeof(dma_elem_t);
+ size_t end = elements / 4;
+ // manually unrolling 4 iterations of the loop here
+ for (size_t i = 0; i < end; ++i) {
+ dst[0] = dma_el[0].sample1;
+ dst[1] = dma_el[1].sample1;
+ dst[2] = dma_el[2].sample1;
+ dst[3] = dma_el[3].sample1;
+ dma_el += 4;
+ dst += 4;
+ }
+ return elements;
+}
+
+static size_t IRAM_ATTR ll_cam_dma_filter_grayscale(uint8_t* dst, const uint8_t* src, size_t len)
+{
+ const dma_elem_t* dma_el = (const dma_elem_t*)src;
+ size_t elements = len / sizeof(dma_elem_t);
+ size_t end = elements / 4;
+ for (size_t i = 0; i < end; ++i) {
+ // manually unrolling 4 iterations of the loop here
+ dst[0] = dma_el[0].sample1;
+ dst[1] = dma_el[1].sample1;
+ dst[2] = dma_el[2].sample1;
+ dst[3] = dma_el[3].sample1;
+ dma_el += 4;
+ dst += 4;
+ }
+ return elements;
+}
+
+static size_t IRAM_ATTR ll_cam_dma_filter_grayscale_highspeed(uint8_t* dst, const uint8_t* src, size_t len)
+{
+ const dma_elem_t* dma_el = (const dma_elem_t*)src;
+ size_t elements = len / sizeof(dma_elem_t);
+ size_t end = elements / 8;
+ for (size_t i = 0; i < end; ++i) {
+ // manually unrolling 4 iterations of the loop here
+ dst[0] = dma_el[0].sample1;
+ dst[1] = dma_el[2].sample1;
+ dst[2] = dma_el[4].sample1;
+ dst[3] = dma_el[6].sample1;
+ dma_el += 8;
+ dst += 4;
+ }
+ // the final sample of a line in SM_0A0B_0B0C sampling mode needs special handling
+ if ((elements & 0x7) != 0) {
+ dst[0] = dma_el[0].sample1;
+ dst[1] = dma_el[2].sample1;
+ elements += 1;
+ }
+ return elements / 2;
+}
+
+static size_t IRAM_ATTR ll_cam_dma_filter_yuyv(uint8_t* dst, const uint8_t* src, size_t len)
+{
+ const dma_elem_t* dma_el = (const dma_elem_t*)src;
+ size_t elements = len / sizeof(dma_elem_t);
+ size_t end = elements / 4;
+ for (size_t i = 0; i < end; ++i) {
+ dst[0] = dma_el[0].sample1;//y0
+ dst[1] = dma_el[0].sample2;//u
+ dst[2] = dma_el[1].sample1;//y1
+ dst[3] = dma_el[1].sample2;//v
+
+ dst[4] = dma_el[2].sample1;//y0
+ dst[5] = dma_el[2].sample2;//u
+ dst[6] = dma_el[3].sample1;//y1
+ dst[7] = dma_el[3].sample2;//v
+ dma_el += 4;
+ dst += 8;
+ }
+ return elements * 2;
+}
+
+static size_t IRAM_ATTR ll_cam_dma_filter_yuyv_highspeed(uint8_t* dst, const uint8_t* src, size_t len)
+{
+ const dma_elem_t* dma_el = (const dma_elem_t*)src;
+ size_t elements = len / sizeof(dma_elem_t);
+ size_t end = elements / 8;
+ for (size_t i = 0; i < end; ++i) {
+ dst[0] = dma_el[0].sample1;//y0
+ dst[1] = dma_el[1].sample1;//u
+ dst[2] = dma_el[2].sample1;//y1
+ dst[3] = dma_el[3].sample1;//v
+
+ dst[4] = dma_el[4].sample1;//y0
+ dst[5] = dma_el[5].sample1;//u
+ dst[6] = dma_el[6].sample1;//y1
+ dst[7] = dma_el[7].sample1;//v
+ dma_el += 8;
+ dst += 8;
+ }
+ if ((elements & 0x7) != 0) {
+ dst[0] = dma_el[0].sample1;//y0
+ dst[1] = dma_el[1].sample1;//u
+ dst[2] = dma_el[2].sample1;//y1
+ dst[3] = dma_el[2].sample2;//v
+ elements += 4;
+ }
+ return elements;
+}
+
+static void IRAM_ATTR ll_cam_vsync_isr(void *arg)
+{
+ //DBG_PIN_SET(1);
+ cam_obj_t *cam = (cam_obj_t *)arg;
+ BaseType_t HPTaskAwoken = pdFALSE;
+ // filter
+ ets_delay_us(1);
+ if (gpio_ll_get_level(&GPIO, cam->vsync_pin) == !cam->vsync_invert) {
+ ll_cam_send_event(cam, CAM_VSYNC_EVENT, &HPTaskAwoken);
+ if (HPTaskAwoken == pdTRUE) {
+ portYIELD_FROM_ISR();
+ }
+ }
+ //DBG_PIN_SET(0);
+}
+
+static void IRAM_ATTR ll_cam_dma_isr(void *arg)
+{
+ //DBG_PIN_SET(1);
+ cam_obj_t *cam = (cam_obj_t *)arg;
+ BaseType_t HPTaskAwoken = pdFALSE;
+
+ typeof(I2S0.int_st) status = I2S0.int_st;
+ if (status.val == 0) {
+ return;
+ }
+
+ I2S0.int_clr.val = status.val;
+
+ if (status.in_suc_eof) {
+ ll_cam_send_event(cam, CAM_IN_SUC_EOF_EVENT, &HPTaskAwoken);
+ }
+ if (HPTaskAwoken == pdTRUE) {
+ portYIELD_FROM_ISR();
+ }
+ //DBG_PIN_SET(0);
+}
+
+bool ll_cam_stop(cam_obj_t *cam)
+{
+ I2S0.conf.rx_start = 0;
+ I2S_ISR_DISABLE(in_suc_eof);
+ I2S0.in_link.stop = 1;
+ return true;
+}
+
+esp_err_t ll_cam_deinit(cam_obj_t *cam)
+{
+ gpio_isr_handler_remove(cam->vsync_pin);
+
+ if (cam->cam_intr_handle) {
+ esp_intr_free(cam->cam_intr_handle);
+ cam->cam_intr_handle = NULL;
+ }
+
+ return ESP_OK;
+}
+
+bool ll_cam_start(cam_obj_t *cam, int frame_pos)
+{
+ I2S0.conf.rx_start = 0;
+
+ I2S_ISR_ENABLE(in_suc_eof);
+
+ I2S0.conf.rx_reset = 1;
+ I2S0.conf.rx_reset = 0;
+ I2S0.conf.rx_fifo_reset = 1;
+ I2S0.conf.rx_fifo_reset = 0;
+ I2S0.lc_conf.in_rst = 1;
+ I2S0.lc_conf.in_rst = 0;
+ I2S0.lc_conf.ahbm_fifo_rst = 1;
+ I2S0.lc_conf.ahbm_fifo_rst = 0;
+ I2S0.lc_conf.ahbm_rst = 1;
+ I2S0.lc_conf.ahbm_rst = 0;
+
+ I2S0.rx_eof_num = cam->dma_half_buffer_size / sizeof(dma_elem_t);
+ I2S0.in_link.addr = ((uint32_t)&cam->dma[0]) & 0xfffff;
+
+ I2S0.in_link.start = 1;
+ I2S0.conf.rx_start = 1;
+ return true;
+}
+
+esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
+{
+ // Enable and configure I2S peripheral
+ periph_module_enable(PERIPH_I2S0_MODULE);
+
+ I2S0.conf.rx_reset = 1;
+ I2S0.conf.rx_reset = 0;
+ I2S0.conf.rx_fifo_reset = 1;
+ I2S0.conf.rx_fifo_reset = 0;
+ I2S0.lc_conf.in_rst = 1;
+ I2S0.lc_conf.in_rst = 0;
+ I2S0.lc_conf.ahbm_fifo_rst = 1;
+ I2S0.lc_conf.ahbm_fifo_rst = 0;
+ I2S0.lc_conf.ahbm_rst = 1;
+ I2S0.lc_conf.ahbm_rst = 0;
+
+ I2S0.conf.rx_slave_mod = 1;
+ I2S0.conf.rx_right_first = 0;
+ I2S0.conf.rx_msb_right = 0;
+ I2S0.conf.rx_msb_shift = 0;
+ I2S0.conf.rx_mono = 0;
+ I2S0.conf.rx_short_sync = 0;
+
+ I2S0.conf2.lcd_en = 1;
+ I2S0.conf2.camera_en = 1;
+
+ // Configure clock divider
+ I2S0.clkm_conf.clkm_div_a = 0;
+ I2S0.clkm_conf.clkm_div_b = 0;
+ I2S0.clkm_conf.clkm_div_num = 2;
+
+ I2S0.fifo_conf.dscr_en = 1;
+ I2S0.fifo_conf.rx_fifo_mod = sampling_mode;
+ I2S0.fifo_conf.rx_fifo_mod_force_en = 1;
+
+ I2S0.conf_chan.rx_chan_mod = 1;
+ I2S0.sample_rate_conf.rx_bits_mod = 0;
+ I2S0.timing.val = 0;
+ I2S0.timing.rx_dsync_sw = 1;
+
+ return ESP_OK;
+}
+
+void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
+{
+ if (en) {
+ gpio_intr_enable(cam->vsync_pin);
+ } else {
+ gpio_intr_disable(cam->vsync_pin);
+ }
+}
+
+esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config)
+{
+ gpio_config_t io_conf = {0};
+ io_conf.intr_type = cam->vsync_invert ? GPIO_PIN_INTR_NEGEDGE : GPIO_PIN_INTR_POSEDGE;
+ io_conf.pin_bit_mask = 1ULL << config->pin_vsync;
+ io_conf.mode = GPIO_MODE_INPUT;
+ io_conf.pull_up_en = 1;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+ gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM);
+ gpio_isr_handler_add(config->pin_vsync, ll_cam_vsync_isr, cam);
+ gpio_intr_disable(config->pin_vsync);
+
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_pclk], PIN_FUNC_GPIO);
+ gpio_set_direction(config->pin_pclk, GPIO_MODE_INPUT);
+ gpio_set_pull_mode(config->pin_pclk, GPIO_FLOATING);
+ gpio_matrix_in(config->pin_pclk, I2S0I_WS_IN_IDX, false);
+
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_vsync], PIN_FUNC_GPIO);
+ gpio_set_direction(config->pin_vsync, GPIO_MODE_INPUT);
+ gpio_set_pull_mode(config->pin_vsync, GPIO_FLOATING);
+ gpio_matrix_in(config->pin_vsync, I2S0I_V_SYNC_IDX, false);
+
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_href], PIN_FUNC_GPIO);
+ gpio_set_direction(config->pin_href, GPIO_MODE_INPUT);
+ gpio_set_pull_mode(config->pin_href, GPIO_FLOATING);
+ gpio_matrix_in(config->pin_href, I2S0I_H_SYNC_IDX, false);
+
+ int data_pins[8] = {
+ config->pin_d0, config->pin_d1, config->pin_d2, config->pin_d3, config->pin_d4, config->pin_d5, config->pin_d6, config->pin_d7,
+ };
+ for (int i = 0; i < 8; i++) {
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[data_pins[i]], PIN_FUNC_GPIO);
+ gpio_set_direction(data_pins[i], GPIO_MODE_INPUT);
+ gpio_set_pull_mode(data_pins[i], GPIO_FLOATING);
+ gpio_matrix_in(data_pins[i], I2S0I_DATA_IN0_IDX + i, false);
+ }
+
+ gpio_matrix_in(0x38, I2S0I_H_ENABLE_IDX, false);
+ return ESP_OK;
+}
+
+esp_err_t ll_cam_init_isr(cam_obj_t *cam)
+{
+ return esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, ll_cam_dma_isr, cam, &cam->cam_intr_handle);
+}
+
+void ll_cam_do_vsync(cam_obj_t *cam)
+{
+}
+
+uint8_t ll_cam_get_dma_align(cam_obj_t *cam)
+{
+ return 0;
+}
+
+static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
+ size_t dma_half_buffer_max = CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX / 2 / cam->dma_bytes_per_item;
+ size_t dma_buffer_max = 2 * dma_half_buffer_max;
+ size_t node_max = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE / cam->dma_bytes_per_item;
+
+ size_t line_width = cam->width * cam->in_bytes_per_pixel;
+ size_t image_size = cam->height * line_width;
+ if (image_size > (4 * 1024 * 1024) || (line_width > dma_half_buffer_max)) {
+ ESP_LOGE(TAG, "Resolution too high");
+ return 0;
+ }
+
+ size_t node_size = node_max;
+ size_t nodes_per_line = 1;
+ size_t lines_per_node = 1;
+ size_t lines_per_half_buffer = 1;
+ size_t dma_half_buffer_min = node_max;
+ size_t dma_half_buffer = dma_half_buffer_max;
+ size_t dma_buffer_size = dma_buffer_max;
+
+ // Calculate DMA Node Size so that it's divisable by or divisor of the line width
+ if(line_width >= node_max){
+ // One or more nodes will be requied for one line
+ for(size_t i = node_max; i > 0; i=i-1){
+ if ((line_width % i) == 0) {
+ node_size = i;
+ nodes_per_line = line_width / node_size;
+ break;
+ }
+ }
+ } else {
+ // One or more lines can fit into one node
+ for(size_t i = node_max; i > 0; i=i-1){
+ if ((i % line_width) == 0) {
+ node_size = i;
+ lines_per_node = node_size / line_width;
+ while((cam->height % lines_per_node) != 0){
+ lines_per_node = lines_per_node - 1;
+ node_size = lines_per_node * line_width;
+ }
+ break;
+ }
+ }
+ }
+ // Calculate minimum EOF size = max(mode_size, line_size)
+ dma_half_buffer_min = node_size * nodes_per_line;
+ // Calculate max EOF size divisable by node size
+ dma_half_buffer = (dma_half_buffer_max / dma_half_buffer_min) * dma_half_buffer_min;
+ // Adjust EOF size so that height will be divisable by the number of lines in each EOF
+ lines_per_half_buffer = dma_half_buffer / line_width;
+ while((cam->height % lines_per_half_buffer) != 0){
+ dma_half_buffer = dma_half_buffer - dma_half_buffer_min;
+ lines_per_half_buffer = dma_half_buffer / line_width;
+ }
+ // Calculate DMA size
+ dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
+
+ ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u, dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u, image_size: %u",
+ node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node, dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item, image_size);
+
+ cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
+ cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
+ cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
+ cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
+ return 1;
+}
+
+bool ll_cam_dma_sizes(cam_obj_t *cam)
+{
+ cam->dma_bytes_per_item = ll_cam_bytes_per_sample(sampling_mode);
+ if (cam->jpeg_mode) {
+ cam->dma_half_buffer_cnt = 8;
+ cam->dma_node_buffer_size = 2048;
+ cam->dma_half_buffer_size = cam->dma_node_buffer_size * 2;
+ cam->dma_buffer_size = cam->dma_half_buffer_cnt * cam->dma_half_buffer_size;
+ } else {
+ return ll_cam_calc_rgb_dma(cam);
+ }
+ return 1;
+}
+
+static dma_filter_t dma_filter = ll_cam_dma_filter_jpeg;
+
+size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len)
+{
+ //DBG_PIN_SET(1);
+ size_t r = dma_filter(out, in, len);
+ //DBG_PIN_SET(0);
+ return r;
+}
+
+esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid)
+{
+ if (pix_format == PIXFORMAT_GRAYSCALE) {
+ if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID) {
+ if (xclk_freq_hz > 10000000) {
+ sampling_mode = SM_0A00_0B00;
+ dma_filter = ll_cam_dma_filter_yuyv_highspeed;
+ } else {
+ sampling_mode = SM_0A0B_0C0D;
+ dma_filter = ll_cam_dma_filter_yuyv;
+ }
+ cam->in_bytes_per_pixel = 1; // camera sends Y8
+ } else {
+ if (xclk_freq_hz > 10000000 && sensor_pid != OV7725_PID) {
+ sampling_mode = SM_0A00_0B00;
+ dma_filter = ll_cam_dma_filter_grayscale_highspeed;
+ } else {
+ sampling_mode = SM_0A0B_0C0D;
+ dma_filter = ll_cam_dma_filter_grayscale;
+ }
+ cam->in_bytes_per_pixel = 2; // camera sends YU/YV
+ }
+ cam->fb_bytes_per_pixel = 1; // frame buffer stores Y8
+ } else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
+ if (xclk_freq_hz > 10000000 && sensor_pid != OV7725_PID) {
+ if (sensor_pid == OV7670_PID) {
+ sampling_mode = SM_0A0B_0B0C;
+ } else {
+ sampling_mode = SM_0A00_0B00;
+ }
+ dma_filter = ll_cam_dma_filter_yuyv_highspeed;
+ } else {
+ sampling_mode = SM_0A0B_0C0D;
+ dma_filter = ll_cam_dma_filter_yuyv;
+ }
+ cam->in_bytes_per_pixel = 2; // camera sends YU/YV
+ cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
+ } else if (pix_format == PIXFORMAT_JPEG) {
+ cam->in_bytes_per_pixel = 1;
+ cam->fb_bytes_per_pixel = 1;
+ dma_filter = ll_cam_dma_filter_jpeg;
+ sampling_mode = SM_0A00_0B00;
+ } else {
+ ESP_LOGE(TAG, "Requested format is not supported");
+ return ESP_ERR_NOT_SUPPORTED;
+ }
+ I2S0.fifo_conf.rx_fifo_mod = sampling_mode;
+ return ESP_OK;
+}
diff --git a/code/components/esp32-camera-master/target/esp32s2/ll_cam.c b/code/components/esp32-camera-master/target/esp32s2/ll_cam.c
new file mode 100644
index 00000000..d3cb5353
--- /dev/null
+++ b/code/components/esp32-camera-master/target/esp32s2/ll_cam.c
@@ -0,0 +1,402 @@
+// 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
+#include
+#include "soc/system_reg.h"
+#include "soc/i2s_struct.h"
+#include "hal/gpio_ll.h"
+#include "ll_cam.h"
+#include "xclk.h"
+#include "cam_hal.h"
+
+static const char *TAG = "s2 ll_cam";
+
+#define I2S_ISR_ENABLE(i) {I2S0.int_clr.i = 1;I2S0.int_ena.i = 1;}
+#define I2S_ISR_DISABLE(i) {I2S0.int_ena.i = 0;I2S0.int_clr.i = 1;}
+
+static void IRAM_ATTR ll_cam_vsync_isr(void *arg)
+{
+ //DBG_PIN_SET(1);
+ cam_obj_t *cam = (cam_obj_t *)arg;
+ BaseType_t HPTaskAwoken = pdFALSE;
+ // filter
+ ets_delay_us(1);
+ if (gpio_ll_get_level(&GPIO, cam->vsync_pin) == !cam->vsync_invert) {
+ ll_cam_send_event(cam, CAM_VSYNC_EVENT, &HPTaskAwoken);
+ }
+
+ if (HPTaskAwoken == pdTRUE) {
+ portYIELD_FROM_ISR();
+ }
+ //DBG_PIN_SET(0);
+}
+
+static void IRAM_ATTR ll_cam_dma_isr(void *arg)
+{
+ cam_obj_t *cam = (cam_obj_t *)arg;
+ BaseType_t HPTaskAwoken = pdFALSE;
+
+ typeof(I2S0.int_st) status = I2S0.int_st;
+ if (status.val == 0) {
+ return;
+ }
+
+ I2S0.int_clr.val = status.val;
+
+ if (status.in_suc_eof) {
+ ll_cam_send_event(cam, CAM_IN_SUC_EOF_EVENT, &HPTaskAwoken);
+ }
+
+ if (HPTaskAwoken == pdTRUE) {
+ portYIELD_FROM_ISR();
+ }
+}
+
+bool ll_cam_stop(cam_obj_t *cam)
+{
+ I2S0.conf.rx_start = 0;
+
+ if (cam->jpeg_mode || !cam->psram_mode) {
+ I2S_ISR_DISABLE(in_suc_eof);
+ }
+
+ I2S0.in_link.stop = 1;
+ return true;
+}
+
+esp_err_t ll_cam_deinit(cam_obj_t *cam)
+{
+ gpio_isr_handler_remove(cam->vsync_pin);
+
+ if (cam->cam_intr_handle) {
+ esp_intr_free(cam->cam_intr_handle);
+ cam->cam_intr_handle = NULL;
+ }
+
+ return ESP_OK;
+}
+
+bool ll_cam_start(cam_obj_t *cam, int frame_pos)
+{
+ I2S0.conf.rx_start = 0;
+
+ if (cam->jpeg_mode || !cam->psram_mode) {
+ I2S_ISR_ENABLE(in_suc_eof);
+ }
+
+ I2S0.conf.rx_reset = 1;
+ I2S0.conf.rx_reset = 0;
+ I2S0.conf.rx_fifo_reset = 1;
+ I2S0.conf.rx_fifo_reset = 0;
+ I2S0.lc_conf.in_rst = 1;
+ I2S0.lc_conf.in_rst = 0;
+ I2S0.lc_conf.ahbm_fifo_rst = 1;
+ I2S0.lc_conf.ahbm_fifo_rst = 0;
+ I2S0.lc_conf.ahbm_rst = 1;
+ I2S0.lc_conf.ahbm_rst = 0;
+
+ I2S0.rx_eof_num = cam->dma_half_buffer_size; // Ping pong operation
+ if (!cam->psram_mode) {
+ I2S0.in_link.addr = ((uint32_t)&cam->dma[0]) & 0xfffff;
+ } else {
+ I2S0.in_link.addr = ((uint32_t)&cam->frames[frame_pos].dma[0]) & 0xfffff;
+ }
+
+ I2S0.in_link.start = 1;
+ I2S0.conf.rx_start = 1;
+ return true;
+}
+
+esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
+{
+ esp_err_t err = camera_enable_out_clock(config);
+ if(err != ESP_OK) {
+ return err;
+ }
+ periph_module_enable(PERIPH_I2S0_MODULE);
+ // Configure the clock
+ I2S0.clkm_conf.clkm_div_num = 2; // 160MHz / 2 = 80MHz
+ I2S0.clkm_conf.clkm_div_b = 0;
+ I2S0.clkm_conf.clkm_div_a = 0;
+ I2S0.clkm_conf.clk_sel = 2;
+ I2S0.clkm_conf.clk_en = 1;
+
+
+ I2S0.conf.val = 0;
+ I2S0.fifo_conf.val = 0;
+ I2S0.fifo_conf.dscr_en = 1;
+
+ I2S0.lc_conf.ahbm_fifo_rst = 1;
+ I2S0.lc_conf.ahbm_fifo_rst = 0;
+ I2S0.lc_conf.ahbm_rst = 1;
+ I2S0.lc_conf.ahbm_rst = 0;
+ I2S0.lc_conf.check_owner = 0;
+ //I2S0.lc_conf.indscr_burst_en = 1;
+ //I2S0.lc_conf.ext_mem_bk_size = 0; // DMA access external memory block size. 0: 16 bytes, 1: 32 bytes, 2:64 bytes, 3:reserved
+
+ I2S0.timing.val = 0;
+
+ I2S0.int_ena.val = 0;
+ I2S0.int_clr.val = ~0;
+
+ I2S0.conf2.lcd_en = 1;
+ I2S0.conf2.camera_en = 1;
+
+ // Configuration data format
+ I2S0.conf.rx_slave_mod = 1;
+ I2S0.conf.rx_right_first = 0;
+ I2S0.conf.rx_msb_right = cam->swap_data;
+ I2S0.conf.rx_short_sync = 0;
+ I2S0.conf.rx_mono = 0;
+ I2S0.conf.rx_msb_shift = 0;
+ I2S0.conf.rx_dma_equal = 1;
+
+ // Configure sampling rate
+ I2S0.sample_rate_conf.rx_bck_div_num = 1;
+ I2S0.sample_rate_conf.rx_bits_mod = 8;
+
+ I2S0.conf1.rx_pcm_bypass = 1;
+
+ I2S0.conf2.i_v_sync_filter_en = 1;
+ I2S0.conf2.i_v_sync_filter_thres = 4;
+ I2S0.conf2.cam_sync_fifo_reset = 1;
+ I2S0.conf2.cam_sync_fifo_reset = 0;
+
+ I2S0.conf_chan.rx_chan_mod = 1;
+
+ I2S0.fifo_conf.rx_fifo_mod_force_en = 1;
+ I2S0.fifo_conf.rx_data_num = 32;
+ I2S0.fifo_conf.rx_fifo_mod = 2;
+
+ I2S0.lc_conf.in_rst = 1;
+ I2S0.lc_conf.in_rst = 0;
+
+ I2S0.conf.rx_start = 1;
+
+ return ESP_OK;
+}
+
+void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
+{
+ if (en) {
+ gpio_intr_enable(cam->vsync_pin);
+ } else {
+ gpio_intr_disable(cam->vsync_pin);
+ }
+}
+
+esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config)
+{
+ gpio_config_t io_conf = {0};
+ io_conf.intr_type = cam->vsync_invert ? GPIO_PIN_INTR_NEGEDGE : GPIO_PIN_INTR_POSEDGE;
+ io_conf.pin_bit_mask = 1ULL << config->pin_vsync;
+ io_conf.mode = GPIO_MODE_INPUT;
+ io_conf.pull_up_en = 1;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+ gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM);
+ gpio_isr_handler_add(config->pin_vsync, ll_cam_vsync_isr, cam);
+ gpio_intr_disable(config->pin_vsync);
+
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_pclk], PIN_FUNC_GPIO);
+ gpio_set_direction(config->pin_pclk, GPIO_MODE_INPUT);
+ gpio_set_pull_mode(config->pin_pclk, GPIO_FLOATING);
+ gpio_matrix_in(config->pin_pclk, I2S0I_WS_IN_IDX, false);
+
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_vsync], PIN_FUNC_GPIO);
+ gpio_set_direction(config->pin_vsync, GPIO_MODE_INPUT);
+ gpio_set_pull_mode(config->pin_vsync, GPIO_FLOATING);
+ gpio_matrix_in(config->pin_vsync, I2S0I_V_SYNC_IDX, cam->vsync_invert);
+
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_href], PIN_FUNC_GPIO);
+ gpio_set_direction(config->pin_href, GPIO_MODE_INPUT);
+ gpio_set_pull_mode(config->pin_href, GPIO_FLOATING);
+ gpio_matrix_in(config->pin_href, I2S0I_H_SYNC_IDX, false);
+
+ int data_pins[8] = {
+ config->pin_d0, config->pin_d1, config->pin_d2, config->pin_d3, config->pin_d4, config->pin_d5, config->pin_d6, config->pin_d7,
+ };
+ for (int i = 0; i < 8; i++) {
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[data_pins[i]], PIN_FUNC_GPIO);
+ gpio_set_direction(data_pins[i], GPIO_MODE_INPUT);
+ gpio_set_pull_mode(data_pins[i], GPIO_FLOATING);
+ // High bit alignment, IN16 is always the highest bit
+ // fifo accesses data by bit, when rx_bits_mod is 8, the data needs to be aligned by 8 bits
+ gpio_matrix_in(data_pins[i], I2S0I_DATA_IN0_IDX + 8 + i, false);
+ }
+
+ gpio_matrix_in(0x38, I2S0I_H_ENABLE_IDX, false);
+
+ return ESP_OK;
+}
+
+esp_err_t ll_cam_init_isr(cam_obj_t *cam)
+{
+ return esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, ll_cam_dma_isr, cam, &cam->cam_intr_handle);
+}
+
+void ll_cam_do_vsync(cam_obj_t *cam)
+{
+ ll_cam_vsync_intr_enable(cam, false);
+ gpio_matrix_in(cam->vsync_pin, I2S0I_V_SYNC_IDX, !cam->vsync_invert);
+ ets_delay_us(10);
+ gpio_matrix_in(cam->vsync_pin, I2S0I_V_SYNC_IDX, cam->vsync_invert);
+ ll_cam_vsync_intr_enable(cam, true);
+}
+
+uint8_t ll_cam_get_dma_align(cam_obj_t *cam)
+{
+ return 64;//16 << I2S0.lc_conf.ext_mem_bk_size;
+}
+
+static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
+ size_t node_max = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE / cam->dma_bytes_per_item;
+ size_t line_width = cam->width * cam->in_bytes_per_pixel;
+ size_t node_size = node_max;
+ size_t nodes_per_line = 1;
+ size_t lines_per_node = 1;
+
+ // Calculate DMA Node Size so that it's divisable by or divisor of the line width
+ if(line_width >= node_max){
+ // One or more nodes will be requied for one line
+ for(size_t i = node_max; i > 0; i=i-1){
+ if ((line_width % i) == 0) {
+ node_size = i;
+ nodes_per_line = line_width / node_size;
+ break;
+ }
+ }
+ } else {
+ // One or more lines can fit into one node
+ for(size_t i = node_max; i > 0; i=i-1){
+ if ((i % line_width) == 0) {
+ node_size = i;
+ lines_per_node = node_size / line_width;
+ while((cam->height % lines_per_node) != 0){
+ lines_per_node = lines_per_node - 1;
+ node_size = lines_per_node * line_width;
+ }
+ break;
+ }
+ }
+ }
+
+ ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u",
+ node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node);
+
+ cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
+
+ if (cam->psram_mode) {
+ cam->dma_buffer_size = cam->recv_size * cam->dma_bytes_per_item;
+ cam->dma_half_buffer_cnt = 2;
+ cam->dma_half_buffer_size = cam->dma_buffer_size / cam->dma_half_buffer_cnt;
+ } else {
+ size_t dma_half_buffer_max = CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX / 2 / cam->dma_bytes_per_item;
+ if (line_width > dma_half_buffer_max) {
+ ESP_LOGE(TAG, "Resolution too high");
+ return 0;
+ }
+
+ // Calculate minimum EOF size = max(mode_size, line_size)
+ size_t dma_half_buffer_min = node_size * nodes_per_line;
+
+ // Calculate max EOF size divisable by node size
+ size_t dma_half_buffer = (dma_half_buffer_max / dma_half_buffer_min) * dma_half_buffer_min;
+
+ // Adjust EOF size so that height will be divisable by the number of lines in each EOF
+ size_t lines_per_half_buffer = dma_half_buffer / line_width;
+ while((cam->height % lines_per_half_buffer) != 0){
+ dma_half_buffer = dma_half_buffer - dma_half_buffer_min;
+ lines_per_half_buffer = dma_half_buffer / line_width;
+ }
+
+ // Calculate DMA size
+ size_t dma_buffer_max = 2 * dma_half_buffer_max;
+ size_t dma_buffer_size = dma_buffer_max;
+ dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
+
+ ESP_LOGI(TAG, "dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u",
+ dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item);
+
+ cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
+ cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
+ cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
+ }
+ return 1;
+}
+
+bool ll_cam_dma_sizes(cam_obj_t *cam)
+{
+ cam->dma_bytes_per_item = 1;
+ if (cam->jpeg_mode) {
+ if (cam->psram_mode) {
+ cam->dma_buffer_size = cam->recv_size;
+ cam->dma_half_buffer_size = 1024;
+ cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
+ cam->dma_node_buffer_size = cam->dma_half_buffer_size;
+ } else {
+ cam->dma_half_buffer_cnt = 16;
+ cam->dma_buffer_size = cam->dma_half_buffer_cnt * 1024;
+ cam->dma_half_buffer_size = cam->dma_buffer_size / cam->dma_half_buffer_cnt;
+ cam->dma_node_buffer_size = cam->dma_half_buffer_size;
+ }
+ } else {
+ return ll_cam_calc_rgb_dma(cam);
+ }
+ return 1;
+}
+
+size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len)
+{
+ // YUV to Grayscale
+ if (cam->in_bytes_per_pixel == 2 && cam->fb_bytes_per_pixel == 1) {
+ size_t end = len / 8;
+ for (size_t i = 0; i < end; ++i) {
+ out[0] = in[0];
+ out[1] = in[2];
+ out[2] = in[4];
+ out[3] = in[6];
+ out += 4;
+ in += 8;
+ }
+ return len / 2;
+ }
+
+ // just memcpy
+ memcpy(out, in, len);
+ return len;
+}
+
+esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid)
+{
+ if (pix_format == PIXFORMAT_GRAYSCALE) {
+ if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID) {
+ cam->in_bytes_per_pixel = 1; // camera sends Y8
+ } else {
+ cam->in_bytes_per_pixel = 2; // camera sends YU/YV
+ }
+ cam->fb_bytes_per_pixel = 1; // frame buffer stores Y8
+ } else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
+ cam->in_bytes_per_pixel = 2; // camera sends YU/YV
+ cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
+ } else if (pix_format == PIXFORMAT_JPEG) {
+ cam->in_bytes_per_pixel = 1;
+ cam->fb_bytes_per_pixel = 1;
+ } else {
+ ESP_LOGE(TAG, "Requested format is not supported");
+ return ESP_ERR_NOT_SUPPORTED;
+ }
+ return ESP_OK;
+}
diff --git a/code/components/esp32-camera-master/target/esp32s2/private_include/tjpgd.h b/code/components/esp32-camera-master/target/esp32s2/private_include/tjpgd.h
new file mode 100644
index 00000000..31fbc97c
--- /dev/null
+++ b/code/components/esp32-camera-master/target/esp32s2/private_include/tjpgd.h
@@ -0,0 +1,99 @@
+/*----------------------------------------------------------------------------/
+/ TJpgDec - Tiny JPEG Decompressor include file (C)ChaN, 2012
+/----------------------------------------------------------------------------*/
+#ifndef _TJPGDEC
+#define _TJPGDEC
+/*---------------------------------------------------------------------------*/
+/* System Configurations */
+
+#define JD_SZBUF 512 /* Size of stream input buffer */
+#define JD_FORMAT 0 /* Output pixel format 0:RGB888 (3 BYTE/pix), 1:RGB565 (1 WORD/pix) */
+#define JD_USE_SCALE 1 /* Use descaling feature for output */
+#define JD_TBLCLIP 1 /* Use table for saturation (might be a bit faster but increases 1K bytes of code size) */
+
+/*---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* These types must be 16-bit, 32-bit or larger integer */
+typedef int INT;
+typedef unsigned int UINT;
+
+/* These types must be 8-bit integer */
+typedef char CHAR;
+typedef unsigned char UCHAR;
+typedef unsigned char BYTE;
+
+/* These types must be 16-bit integer */
+typedef short SHORT;
+typedef unsigned short USHORT;
+typedef unsigned short WORD;
+typedef unsigned short WCHAR;
+
+/* These types must be 32-bit integer */
+typedef long LONG;
+typedef unsigned long ULONG;
+typedef unsigned long DWORD;
+
+
+/* Error code */
+typedef enum {
+ JDR_OK = 0, /* 0: Succeeded */
+ JDR_INTR, /* 1: Interrupted by output function */
+ JDR_INP, /* 2: Device error or wrong termination of input stream */
+ JDR_MEM1, /* 3: Insufficient memory pool for the image */
+ JDR_MEM2, /* 4: Insufficient stream input buffer */
+ JDR_PAR, /* 5: Parameter error */
+ JDR_FMT1, /* 6: Data format error (may be damaged data) */
+ JDR_FMT2, /* 7: Right format but not supported */
+ JDR_FMT3 /* 8: Not supported JPEG standard */
+} JRESULT;
+
+
+
+/* Rectangular structure */
+typedef struct {
+ WORD left, right, top, bottom;
+} JRECT;
+
+
+
+/* Decompressor object structure */
+typedef struct JDEC JDEC;
+struct JDEC {
+ UINT dctr; /* Number of bytes available in the input buffer */
+ BYTE* dptr; /* Current data read ptr */
+ BYTE* inbuf; /* Bit stream input buffer */
+ BYTE dmsk; /* Current bit in the current read byte */
+ BYTE scale; /* Output scaling ratio */
+ BYTE msx, msy; /* MCU size in unit of block (width, height) */
+ BYTE qtid[3]; /* Quantization table ID of each component */
+ SHORT dcv[3]; /* Previous DC element of each component */
+ WORD nrst; /* Restart inverval */
+ UINT width, height; /* Size of the input image (pixel) */
+ BYTE* huffbits[2][2]; /* Huffman bit distribution tables [id][dcac] */
+ WORD* huffcode[2][2]; /* Huffman code word tables [id][dcac] */
+ BYTE* huffdata[2][2]; /* Huffman decoded data tables [id][dcac] */
+ LONG* qttbl[4]; /* Dequaitizer tables [id] */
+ void* workbuf; /* Working buffer for IDCT and RGB output */
+ BYTE* mcubuf; /* Working buffer for the MCU */
+ void* pool; /* Pointer to available memory pool */
+ UINT sz_pool; /* Size of momory pool (bytes available) */
+ UINT (*infunc)(JDEC*, BYTE*, UINT);/* Pointer to jpeg stream input function */
+ void* device; /* Pointer to I/O device identifiler for the session */
+};
+
+
+
+/* TJpgDec API functions */
+JRESULT jd_prepare (JDEC*, UINT(*)(JDEC*,BYTE*,UINT), void*, UINT, void*);
+JRESULT jd_decomp (JDEC*, UINT(*)(JDEC*,void*,JRECT*), BYTE);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TJPGDEC */
diff --git a/code/components/esp32-camera-master/target/esp32s2/tjpgd.c b/code/components/esp32-camera-master/target/esp32s2/tjpgd.c
new file mode 100644
index 00000000..5a983c4c
--- /dev/null
+++ b/code/components/esp32-camera-master/target/esp32s2/tjpgd.c
@@ -0,0 +1,970 @@
+/*----------------------------------------------------------------------------/
+/ TJpgDec - Tiny JPEG Decompressor R0.01b (C)ChaN, 2012
+/-----------------------------------------------------------------------------/
+/ The TJpgDec is a generic JPEG decompressor module for tiny embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following terms.
+/
+/ Copyright (C) 2012, ChaN, all right reserved.
+/
+/ * The TJpgDec module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/-----------------------------------------------------------------------------/
+/ Oct 04,'11 R0.01 First release.
+/ Feb 19,'12 R0.01a Fixed decompression fails when scan starts with an escape seq.
+/ Sep 03,'12 R0.01b Added JD_TBLCLIP option.
+/----------------------------------------------------------------------------*/
+
+#include "tjpgd.h"
+
+#define SUPPORT_JPEG 1
+
+#ifdef SUPPORT_JPEG
+/*-----------------------------------------------*/
+/* Zigzag-order to raster-order conversion table */
+/*-----------------------------------------------*/
+
+#define ZIG(n) Zig[n]
+
+static
+const BYTE Zig[64] = { /* Zigzag-order to raster-order conversion table */
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
+};
+
+
+
+/*-------------------------------------------------*/
+/* Input scale factor of Arai algorithm */
+/* (scaled up 16 bits for fixed point operations) */
+/*-------------------------------------------------*/
+
+#define IPSF(n) Ipsf[n]
+
+static
+const WORD Ipsf[64] = { /* See also aa_idct.png */
+ (WORD)(1.00000*8192), (WORD)(1.38704*8192), (WORD)(1.30656*8192), (WORD)(1.17588*8192), (WORD)(1.00000*8192), (WORD)(0.78570*8192), (WORD)(0.54120*8192), (WORD)(0.27590*8192),
+ (WORD)(1.38704*8192), (WORD)(1.92388*8192), (WORD)(1.81226*8192), (WORD)(1.63099*8192), (WORD)(1.38704*8192), (WORD)(1.08979*8192), (WORD)(0.75066*8192), (WORD)(0.38268*8192),
+ (WORD)(1.30656*8192), (WORD)(1.81226*8192), (WORD)(1.70711*8192), (WORD)(1.53636*8192), (WORD)(1.30656*8192), (WORD)(1.02656*8192), (WORD)(0.70711*8192), (WORD)(0.36048*8192),
+ (WORD)(1.17588*8192), (WORD)(1.63099*8192), (WORD)(1.53636*8192), (WORD)(1.38268*8192), (WORD)(1.17588*8192), (WORD)(0.92388*8192), (WORD)(0.63638*8192), (WORD)(0.32442*8192),
+ (WORD)(1.00000*8192), (WORD)(1.38704*8192), (WORD)(1.30656*8192), (WORD)(1.17588*8192), (WORD)(1.00000*8192), (WORD)(0.78570*8192), (WORD)(0.54120*8192), (WORD)(0.27590*8192),
+ (WORD)(0.78570*8192), (WORD)(1.08979*8192), (WORD)(1.02656*8192), (WORD)(0.92388*8192), (WORD)(0.78570*8192), (WORD)(0.61732*8192), (WORD)(0.42522*8192), (WORD)(0.21677*8192),
+ (WORD)(0.54120*8192), (WORD)(0.75066*8192), (WORD)(0.70711*8192), (WORD)(0.63638*8192), (WORD)(0.54120*8192), (WORD)(0.42522*8192), (WORD)(0.29290*8192), (WORD)(0.14932*8192),
+ (WORD)(0.27590*8192), (WORD)(0.38268*8192), (WORD)(0.36048*8192), (WORD)(0.32442*8192), (WORD)(0.27590*8192), (WORD)(0.21678*8192), (WORD)(0.14932*8192), (WORD)(0.07612*8192)
+};
+
+
+
+/*---------------------------------------------*/
+/* Conversion table for fast clipping process */
+/*---------------------------------------------*/
+
+#if JD_TBLCLIP
+
+#define BYTECLIP(v) Clip8[(UINT)(v) & 0x3FF]
+
+static
+const BYTE Clip8[1024] = {
+ /* 0..255 */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
+ /* 256..511 */
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ /* -512..-257 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* -256..-1 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+#else /* JD_TBLCLIP */
+
+inline
+BYTE BYTECLIP (
+ INT val
+)
+{
+ if (val < 0) val = 0;
+ if (val > 255) val = 255;
+
+ return (BYTE)val;
+}
+
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Allocate a memory block from memory pool */
+/*-----------------------------------------------------------------------*/
+
+static
+void* alloc_pool ( /* Pointer to allocated memory block (NULL:no memory available) */
+ JDEC* jd, /* Pointer to the decompressor object */
+ UINT nd /* Number of bytes to allocate */
+)
+{
+ char *rp = 0;
+
+
+ nd = (nd + 3) & ~3; /* Align block size to the word boundary */
+
+ if (jd->sz_pool >= nd) {
+ jd->sz_pool -= nd;
+ rp = (char*)jd->pool; /* Get start of available memory pool */
+ jd->pool = (void*)(rp + nd); /* Allocate requierd bytes */
+ }
+
+ return (void*)rp; /* Return allocated memory block (NULL:no memory to allocate) */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create de-quantization and prescaling tables with a DQT segment */
+/*-----------------------------------------------------------------------*/
+
+static
+UINT create_qt_tbl ( /* 0:OK, !0:Failed */
+ JDEC* jd, /* Pointer to the decompressor object */
+ const BYTE* data, /* Pointer to the quantizer tables */
+ UINT ndata /* Size of input data */
+)
+{
+ UINT i;
+ BYTE d, z;
+ LONG *pb;
+
+
+ while (ndata) { /* Process all tables in the segment */
+ if (ndata < 65) return JDR_FMT1; /* Err: table size is unaligned */
+ ndata -= 65;
+ d = *data++; /* Get table property */
+ if (d & 0xF0) return JDR_FMT1; /* Err: not 8-bit resolution */
+ i = d & 3; /* Get table ID */
+ pb = alloc_pool(jd, 64 * sizeof (LONG));/* Allocate a memory block for the table */
+ if (!pb) return JDR_MEM1; /* Err: not enough memory */
+ jd->qttbl[i] = pb; /* Register the table */
+ for (i = 0; i < 64; i++) { /* Load the table */
+ z = ZIG(i); /* Zigzag-order to raster-order conversion */
+ pb[z] = (LONG)((DWORD)*data++ * IPSF(z)); /* Apply scale factor of Arai algorithm to the de-quantizers */
+ }
+ }
+
+ return JDR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create huffman code tables with a DHT segment */
+/*-----------------------------------------------------------------------*/
+
+static
+UINT create_huffman_tbl ( /* 0:OK, !0:Failed */
+ JDEC* jd, /* Pointer to the decompressor object */
+ const BYTE* data, /* Pointer to the packed huffman tables */
+ UINT ndata /* Size of input data */
+)
+{
+ UINT i, j, b, np, cls, num;
+ BYTE d, *pb, *pd;
+ WORD hc, *ph;
+
+
+ while (ndata) { /* Process all tables in the segment */
+ if (ndata < 17) return JDR_FMT1; /* Err: wrong data size */
+ ndata -= 17;
+ d = *data++; /* Get table number and class */
+ cls = (d >> 4); num = d & 0x0F; /* class = dc(0)/ac(1), table number = 0/1 */
+ if (d & 0xEE) return JDR_FMT1; /* Err: invalid class/number */
+ pb = alloc_pool(jd, 16); /* Allocate a memory block for the bit distribution table */
+ if (!pb) return JDR_MEM1; /* Err: not enough memory */
+ jd->huffbits[num][cls] = pb;
+ for (np = i = 0; i < 16; i++) { /* Load number of patterns for 1 to 16-bit code */
+ pb[i] = b = *data++;
+ np += b; /* Get sum of code words for each code */
+ }
+
+ ph = alloc_pool(jd, np * sizeof (WORD));/* Allocate a memory block for the code word table */
+ if (!ph) return JDR_MEM1; /* Err: not enough memory */
+ jd->huffcode[num][cls] = ph;
+ hc = 0;
+ for (j = i = 0; i < 16; i++) { /* Re-build huffman code word table */
+ b = pb[i];
+ while (b--) ph[j++] = hc++;
+ hc <<= 1;
+ }
+
+ if (ndata < np) return JDR_FMT1; /* Err: wrong data size */
+ ndata -= np;
+ pd = alloc_pool(jd, np); /* Allocate a memory block for the decoded data */
+ if (!pd) return JDR_MEM1; /* Err: not enough memory */
+ jd->huffdata[num][cls] = pd;
+ for (i = 0; i < np; i++) { /* Load decoded data corresponds to each code ward */
+ d = *data++;
+ if (!cls && d > 11) return JDR_FMT1;
+ *pd++ = d;
+ }
+ }
+
+ return JDR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Extract N bits from input stream */
+/*-----------------------------------------------------------------------*/
+
+static
+INT bitext ( /* >=0: extracted data, <0: error code */
+ JDEC* jd, /* Pointer to the decompressor object */
+ UINT nbit /* Number of bits to extract (1 to 11) */
+)
+{
+ BYTE msk, s, *dp;
+ UINT dc, v, f;
+
+
+ msk = jd->dmsk; dc = jd->dctr; dp = jd->dptr; /* Bit mask, number of data available, read ptr */
+ s = *dp; v = f = 0;
+ do {
+ if (!msk) { /* Next byte? */
+ if (!dc) { /* No input data is available, re-fill input buffer */
+ dp = jd->inbuf; /* Top of input buffer */
+ dc = jd->infunc(jd, dp, JD_SZBUF);
+ if (!dc) return 0 - (INT)JDR_INP; /* Err: read error or wrong stream termination */
+ } else {
+ dp++; /* Next data ptr */
+ }
+ dc--; /* Decrement number of available bytes */
+ if (f) { /* In flag sequence? */
+ f = 0; /* Exit flag sequence */
+ if (*dp != 0) return 0 - (INT)JDR_FMT1; /* Err: unexpected flag is detected (may be collapted data) */
+ *dp = s = 0xFF; /* The flag is a data 0xFF */
+ } else {
+ s = *dp; /* Get next data byte */
+ if (s == 0xFF) { /* Is start of flag sequence? */
+ f = 1; continue; /* Enter flag sequence */
+ }
+ }
+ msk = 0x80; /* Read from MSB */
+ }
+ v <<= 1; /* Get a bit */
+ if (s & msk) v++;
+ msk >>= 1;
+ nbit--;
+ } while (nbit);
+ jd->dmsk = msk; jd->dctr = dc; jd->dptr = dp;
+
+ return (INT)v;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Extract a huffman decoded data from input stream */
+/*-----------------------------------------------------------------------*/
+
+static
+INT huffext ( /* >=0: decoded data, <0: error code */
+ JDEC* jd, /* Pointer to the decompressor object */
+ const BYTE* hbits, /* Pointer to the bit distribution table */
+ const WORD* hcode, /* Pointer to the code word table */
+ const BYTE* hdata /* Pointer to the data table */
+)
+{
+ BYTE msk, s, *dp;
+ UINT dc, v, f, bl, nd;
+
+
+ msk = jd->dmsk; dc = jd->dctr; dp = jd->dptr; /* Bit mask, number of data available, read ptr */
+ s = *dp; v = f = 0;
+ bl = 16; /* Max code length */
+ do {
+ if (!msk) { /* Next byte? */
+ if (!dc) { /* No input data is available, re-fill input buffer */
+ dp = jd->inbuf; /* Top of input buffer */
+ dc = jd->infunc(jd, dp, JD_SZBUF);
+ if (!dc) return 0 - (INT)JDR_INP; /* Err: read error or wrong stream termination */
+ } else {
+ dp++; /* Next data ptr */
+ }
+ dc--; /* Decrement number of available bytes */
+ if (f) { /* In flag sequence? */
+ f = 0; /* Exit flag sequence */
+ if (*dp != 0)
+ return 0 - (INT)JDR_FMT1; /* Err: unexpected flag is detected (may be collapted data) */
+ *dp = s = 0xFF; /* The flag is a data 0xFF */
+ } else {
+ s = *dp; /* Get next data byte */
+ if (s == 0xFF) { /* Is start of flag sequence? */
+ f = 1; continue; /* Enter flag sequence, get trailing byte */
+ }
+ }
+ msk = 0x80; /* Read from MSB */
+ }
+ v <<= 1; /* Get a bit */
+ if (s & msk) v++;
+ msk >>= 1;
+
+ for (nd = *hbits++; nd; nd--) { /* Search the code word in this bit length */
+ if (v == *hcode++) { /* Matched? */
+ jd->dmsk = msk; jd->dctr = dc; jd->dptr = dp;
+ return *hdata; /* Return the decoded data */
+ }
+ hdata++;
+ }
+ bl--;
+ } while (bl);
+
+ return 0 - (INT)JDR_FMT1; /* Err: code not found (may be collapted data) */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Apply Inverse-DCT in Arai Algorithm (see also aa_idct.png) */
+/*-----------------------------------------------------------------------*/
+
+static
+void block_idct (
+ LONG* src, /* Input block data (de-quantized and pre-scaled for Arai Algorithm) */
+ BYTE* dst /* Pointer to the destination to store the block as byte array */
+)
+{
+ const LONG M13 = (LONG)(1.41421*4096), M2 = (LONG)(1.08239*4096), M4 = (LONG)(2.61313*4096), M5 = (LONG)(1.84776*4096);
+ LONG v0, v1, v2, v3, v4, v5, v6, v7;
+ LONG t10, t11, t12, t13;
+ UINT i;
+
+ /* Process columns */
+ for (i = 0; i < 8; i++) {
+ v0 = src[8 * 0]; /* Get even elements */
+ v1 = src[8 * 2];
+ v2 = src[8 * 4];
+ v3 = src[8 * 6];
+
+ t10 = v0 + v2; /* Process the even elements */
+ t12 = v0 - v2;
+ t11 = (v1 - v3) * M13 >> 12;
+ v3 += v1;
+ t11 -= v3;
+ v0 = t10 + v3;
+ v3 = t10 - v3;
+ v1 = t11 + t12;
+ v2 = t12 - t11;
+
+ v4 = src[8 * 7]; /* Get odd elements */
+ v5 = src[8 * 1];
+ v6 = src[8 * 5];
+ v7 = src[8 * 3];
+
+ t10 = v5 - v4; /* Process the odd elements */
+ t11 = v5 + v4;
+ t12 = v6 - v7;
+ v7 += v6;
+ v5 = (t11 - v7) * M13 >> 12;
+ v7 += t11;
+ t13 = (t10 + t12) * M5 >> 12;
+ v4 = t13 - (t10 * M2 >> 12);
+ v6 = t13 - (t12 * M4 >> 12) - v7;
+ v5 -= v6;
+ v4 -= v5;
+
+ src[8 * 0] = v0 + v7; /* Write-back transformed values */
+ src[8 * 7] = v0 - v7;
+ src[8 * 1] = v1 + v6;
+ src[8 * 6] = v1 - v6;
+ src[8 * 2] = v2 + v5;
+ src[8 * 5] = v2 - v5;
+ src[8 * 3] = v3 + v4;
+ src[8 * 4] = v3 - v4;
+
+ src++; /* Next column */
+ }
+
+ /* Process rows */
+ src -= 8;
+ for (i = 0; i < 8; i++) {
+ v0 = src[0] + (128L << 8); /* Get even elements (remove DC offset (-128) here) */
+ v1 = src[2];
+ v2 = src[4];
+ v3 = src[6];
+
+ t10 = v0 + v2; /* Process the even elements */
+ t12 = v0 - v2;
+ t11 = (v1 - v3) * M13 >> 12;
+ v3 += v1;
+ t11 -= v3;
+ v0 = t10 + v3;
+ v3 = t10 - v3;
+ v1 = t11 + t12;
+ v2 = t12 - t11;
+
+ v4 = src[7]; /* Get odd elements */
+ v5 = src[1];
+ v6 = src[5];
+ v7 = src[3];
+
+ t10 = v5 - v4; /* Process the odd elements */
+ t11 = v5 + v4;
+ t12 = v6 - v7;
+ v7 += v6;
+ v5 = (t11 - v7) * M13 >> 12;
+ v7 += t11;
+ t13 = (t10 + t12) * M5 >> 12;
+ v4 = t13 - (t10 * M2 >> 12);
+ v6 = t13 - (t12 * M4 >> 12) - v7;
+ v5 -= v6;
+ v4 -= v5;
+
+ dst[0] = BYTECLIP((v0 + v7) >> 8); /* Descale the transformed values 8 bits and output */
+ dst[7] = BYTECLIP((v0 - v7) >> 8);
+ dst[1] = BYTECLIP((v1 + v6) >> 8);
+ dst[6] = BYTECLIP((v1 - v6) >> 8);
+ dst[2] = BYTECLIP((v2 + v5) >> 8);
+ dst[5] = BYTECLIP((v2 - v5) >> 8);
+ dst[3] = BYTECLIP((v3 + v4) >> 8);
+ dst[4] = BYTECLIP((v3 - v4) >> 8);
+ dst += 8;
+
+ src += 8; /* Next row */
+ }
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load all blocks in the MCU into working buffer */
+/*-----------------------------------------------------------------------*/
+
+static
+JRESULT mcu_load (
+ JDEC* jd /* Pointer to the decompressor object */
+)
+{
+ LONG *tmp = (LONG*)jd->workbuf; /* Block working buffer for de-quantize and IDCT */
+ UINT blk, nby, nbc, i, z, id, cmp;
+ INT b, d, e;
+ BYTE *bp;
+ const BYTE *hb, *hd;
+ const WORD *hc;
+ const LONG *dqf;
+
+
+ nby = jd->msx * jd->msy; /* Number of Y blocks (1, 2 or 4) */
+ nbc = 2; /* Number of C blocks (2) */
+ bp = jd->mcubuf; /* Pointer to the first block */
+
+ for (blk = 0; blk < nby + nbc; blk++) {
+ cmp = (blk < nby) ? 0 : blk - nby + 1; /* Component number 0:Y, 1:Cb, 2:Cr */
+ id = cmp ? 1 : 0; /* Huffman table ID of the component */
+
+ /* Extract a DC element from input stream */
+ hb = jd->huffbits[id][0]; /* Huffman table for the DC element */
+ hc = jd->huffcode[id][0];
+ hd = jd->huffdata[id][0];
+ b = huffext(jd, hb, hc, hd); /* Extract a huffman coded data (bit length) */
+ if (b < 0) return 0 - b; /* Err: invalid code or input */
+ d = jd->dcv[cmp]; /* DC value of previous block */
+ if (b) { /* If there is any difference from previous block */
+ e = bitext(jd, b); /* Extract data bits */
+ if (e < 0) return 0 - e; /* Err: input */
+ b = 1 << (b - 1); /* MSB position */
+ if (!(e & b)) e -= (b << 1) - 1; /* Restore sign if needed */
+ d += e; /* Get current value */
+ jd->dcv[cmp] = (SHORT)d; /* Save current DC value for next block */
+ }
+ dqf = jd->qttbl[jd->qtid[cmp]]; /* De-quantizer table ID for this component */
+ tmp[0] = d * dqf[0] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */
+
+ /* Extract following 63 AC elements from input stream */
+ for (i = 1; i < 64; i++) tmp[i] = 0; /* Clear rest of elements */
+ hb = jd->huffbits[id][1]; /* Huffman table for the AC elements */
+ hc = jd->huffcode[id][1];
+ hd = jd->huffdata[id][1];
+ i = 1; /* Top of the AC elements */
+ do {
+ b = huffext(jd, hb, hc, hd); /* Extract a huffman coded value (zero runs and bit length) */
+ if (b == 0) break; /* EOB? */
+ if (b < 0) return 0 - b; /* Err: invalid code or input error */
+ z = (UINT)b >> 4; /* Number of leading zero elements */
+ if (z) {
+ i += z; /* Skip zero elements */
+ if (i >= 64) return JDR_FMT1; /* Too long zero run */
+ }
+ if (b &= 0x0F) { /* Bit length */
+ d = bitext(jd, b); /* Extract data bits */
+ if (d < 0) return 0 - d; /* Err: input device */
+ b = 1 << (b - 1); /* MSB position */
+ if (!(d & b)) d -= (b << 1) - 1;/* Restore negative value if needed */
+ z = ZIG(i); /* Zigzag-order to raster-order converted index */
+ tmp[z] = d * dqf[z] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */
+ }
+ } while (++i < 64); /* Next AC element */
+
+ if (JD_USE_SCALE && jd->scale == 3)
+ *bp = (*tmp / 256) + 128; /* If scale ratio is 1/8, IDCT can be ommited and only DC element is used */
+ else
+ block_idct(tmp, bp); /* Apply IDCT and store the block to the MCU buffer */
+
+ bp += 64; /* Next block */
+ }
+
+ return JDR_OK; /* All blocks have been loaded successfully */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Output an MCU: Convert YCrCb to RGB and output it in RGB form */
+/*-----------------------------------------------------------------------*/
+
+static
+JRESULT mcu_output (
+ JDEC* jd, /* Pointer to the decompressor object */
+ UINT (*outfunc)(JDEC*, void*, JRECT*), /* RGB output function */
+ UINT x, /* MCU position in the image (left of the MCU) */
+ UINT y /* MCU position in the image (top of the MCU) */
+)
+{
+ const INT CVACC = (sizeof (INT) > 2) ? 1024 : 128;
+ UINT ix, iy, mx, my, rx, ry;
+ INT yy, cb, cr;
+ BYTE *py, *pc, *rgb24;
+ JRECT rect;
+
+
+ mx = jd->msx * 8; my = jd->msy * 8; /* MCU size (pixel) */
+ rx = (x + mx <= jd->width) ? mx : jd->width - x; /* Output rectangular size (it may be clipped at right/bottom end) */
+ ry = (y + my <= jd->height) ? my : jd->height - y;
+ if (JD_USE_SCALE) {
+ rx >>= jd->scale; ry >>= jd->scale;
+ if (!rx || !ry) return JDR_OK; /* Skip this MCU if all pixel is to be rounded off */
+ x >>= jd->scale; y >>= jd->scale;
+ }
+ rect.left = x; rect.right = x + rx - 1; /* Rectangular area in the frame buffer */
+ rect.top = y; rect.bottom = y + ry - 1;
+
+
+ if (!JD_USE_SCALE || jd->scale != 3) { /* Not for 1/8 scaling */
+
+ /* Build an RGB MCU from discrete comopnents */
+ rgb24 = (BYTE*)jd->workbuf;
+ for (iy = 0; iy < my; iy++) {
+ pc = jd->mcubuf;
+ py = pc + iy * 8;
+ if (my == 16) { /* Double block height? */
+ pc += 64 * 4 + (iy >> 1) * 8;
+ if (iy >= 8) py += 64;
+ } else { /* Single block height */
+ pc += mx * 8 + iy * 8;
+ }
+ for (ix = 0; ix < mx; ix++) {
+ cb = pc[0] - 128; /* Get Cb/Cr component and restore right level */
+ cr = pc[64] - 128;
+ if (mx == 16) { /* Double block width? */
+ if (ix == 8) py += 64 - 8; /* Jump to next block if double block heigt */
+ pc += ix & 1; /* Increase chroma pointer every two pixels */
+ } else { /* Single block width */
+ pc++; /* Increase chroma pointer every pixel */
+ }
+ yy = *py++; /* Get Y component */
+
+ /* Convert YCbCr to RGB */
+ *rgb24++ = /* R */ BYTECLIP(yy + ((INT)(1.402 * CVACC) * cr) / CVACC);
+ *rgb24++ = /* G */ BYTECLIP(yy - ((INT)(0.344 * CVACC) * cb + (INT)(0.714 * CVACC) * cr) / CVACC);
+ *rgb24++ = /* B */ BYTECLIP(yy + ((INT)(1.772 * CVACC) * cb) / CVACC);
+ }
+ }
+
+ /* Descale the MCU rectangular if needed */
+ if (JD_USE_SCALE && jd->scale) {
+ UINT x, y, r, g, b, s, w, a;
+ BYTE *op;
+
+ /* Get averaged RGB value of each square correcponds to a pixel */
+ s = jd->scale * 2; /* Bumber of shifts for averaging */
+ w = 1 << jd->scale; /* Width of square */
+ a = (mx - w) * 3; /* Bytes to skip for next line in the square */
+ op = (BYTE*)jd->workbuf;
+ for (iy = 0; iy < my; iy += w) {
+ for (ix = 0; ix < mx; ix += w) {
+ rgb24 = (BYTE*)jd->workbuf + (iy * mx + ix) * 3;
+ r = g = b = 0;
+ for (y = 0; y < w; y++) { /* Accumulate RGB value in the square */
+ for (x = 0; x < w; x++) {
+ r += *rgb24++;
+ g += *rgb24++;
+ b += *rgb24++;
+ }
+ rgb24 += a;
+ } /* Put the averaged RGB value as a pixel */
+ *op++ = (BYTE)(r >> s);
+ *op++ = (BYTE)(g >> s);
+ *op++ = (BYTE)(b >> s);
+ }
+ }
+ }
+
+ } else { /* For only 1/8 scaling (left-top pixel in each block are the DC value of the block) */
+
+ /* Build a 1/8 descaled RGB MCU from discrete comopnents */
+ rgb24 = (BYTE*)jd->workbuf;
+ pc = jd->mcubuf + mx * my;
+ cb = pc[0] - 128; /* Get Cb/Cr component and restore right level */
+ cr = pc[64] - 128;
+ for (iy = 0; iy < my; iy += 8) {
+ py = jd->mcubuf;
+ if (iy == 8) py += 64 * 2;
+ for (ix = 0; ix < mx; ix += 8) {
+ yy = *py; /* Get Y component */
+ py += 64;
+
+ /* Convert YCbCr to RGB */
+ *rgb24++ = /* R */ BYTECLIP(yy + ((INT)(1.402 * CVACC) * cr / CVACC));
+ *rgb24++ = /* G */ BYTECLIP(yy - ((INT)(0.344 * CVACC) * cb + (INT)(0.714 * CVACC) * cr) / CVACC);
+ *rgb24++ = /* B */ BYTECLIP(yy + ((INT)(1.772 * CVACC) * cb / CVACC));
+ }
+ }
+ }
+
+ /* Squeeze up pixel table if a part of MCU is to be truncated */
+ mx >>= jd->scale;
+ if (rx < mx) {
+ BYTE *s, *d;
+ UINT x, y;
+
+ s = d = (BYTE*)jd->workbuf;
+ for (y = 0; y < ry; y++) {
+ for (x = 0; x < rx; x++) { /* Copy effective pixels */
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+ }
+ s += (mx - rx) * 3; /* Skip truncated pixels */
+ }
+ }
+
+ /* Convert RGB888 to RGB565 if needed */
+ if (JD_FORMAT == 1) {
+ BYTE *s = (BYTE*)jd->workbuf;
+ WORD w, *d = (WORD*)s;
+ UINT n = rx * ry;
+
+ do {
+ w = (*s++ & 0xF8) << 8; /* RRRRR----------- */
+ w |= (*s++ & 0xFC) << 3; /* -----GGGGGG----- */
+ w |= *s++ >> 3; /* -----------BBBBB */
+ *d++ = w;
+ } while (--n);
+ }
+
+ /* Output the RGB rectangular */
+ return outfunc(jd, jd->workbuf, &rect) ? JDR_OK : JDR_INTR;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Process restart interval */
+/*-----------------------------------------------------------------------*/
+
+static
+JRESULT restart (
+ JDEC* jd, /* Pointer to the decompressor object */
+ WORD rstn /* Expected restert sequense number */
+)
+{
+ UINT i, dc;
+ WORD d;
+ BYTE *dp;
+
+
+ /* Discard padding bits and get two bytes from the input stream */
+ dp = jd->dptr; dc = jd->dctr;
+ d = 0;
+ for (i = 0; i < 2; i++) {
+ if (!dc) { /* No input data is available, re-fill input buffer */
+ dp = jd->inbuf;
+ dc = jd->infunc(jd, dp, JD_SZBUF);
+ if (!dc) return JDR_INP;
+ } else {
+ dp++;
+ }
+ dc--;
+ d = (d << 8) | *dp; /* Get a byte */
+ }
+ jd->dptr = dp; jd->dctr = dc; jd->dmsk = 0;
+
+ /* Check the marker */
+ if ((d & 0xFFD8) != 0xFFD0 || (d & 7) != (rstn & 7))
+ return JDR_FMT1; /* Err: expected RSTn marker is not detected (may be collapted data) */
+
+ /* Reset DC offset */
+ jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0;
+
+ return JDR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Analyze the JPEG image and Initialize decompressor object */
+/*-----------------------------------------------------------------------*/
+
+#define LDB_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr))<<8)|(WORD)*(BYTE*)((ptr)+1))
+
+
+JRESULT jd_prepare (
+ JDEC* jd, /* Blank decompressor object */
+ UINT (*infunc)(JDEC*, BYTE*, UINT), /* JPEG strem input function */
+ void* pool, /* Working buffer for the decompression session */
+ UINT sz_pool, /* Size of working buffer */
+ void* dev /* I/O device identifier for the session */
+)
+{
+ BYTE *seg, b;
+ WORD marker;
+ DWORD ofs;
+ UINT n, i, j, len;
+ JRESULT rc;
+
+
+ if (!pool) return JDR_PAR;
+
+ jd->pool = pool; /* Work memroy */
+ jd->sz_pool = sz_pool; /* Size of given work memory */
+ jd->infunc = infunc; /* Stream input function */
+ jd->device = dev; /* I/O device identifier */
+ jd->nrst = 0; /* No restart interval (default) */
+
+ for (i = 0; i < 2; i++) { /* Nulls pointers */
+ for (j = 0; j < 2; j++) {
+ jd->huffbits[i][j] = 0;
+ jd->huffcode[i][j] = 0;
+ jd->huffdata[i][j] = 0;
+ }
+ }
+ for (i = 0; i < 4; i++) jd->qttbl[i] = 0;
+
+ jd->inbuf = seg = alloc_pool(jd, JD_SZBUF); /* Allocate stream input buffer */
+ if (!seg) return JDR_MEM1;
+
+ if (jd->infunc(jd, seg, 2) != 2) return JDR_INP;/* Check SOI marker */
+ if (LDB_WORD(seg) != 0xFFD8) return JDR_FMT1; /* Err: SOI is not detected */
+ ofs = 2;
+
+ for (;;) {
+ /* Get a JPEG marker */
+ if (jd->infunc(jd, seg, 4) != 4) return JDR_INP;
+ marker = LDB_WORD(seg); /* Marker */
+ len = LDB_WORD(seg + 2); /* Length field */
+ if (len <= 2 || (marker >> 8) != 0xFF) return JDR_FMT1;
+ len -= 2; /* Content size excluding length field */
+ ofs += 4 + len; /* Number of bytes loaded */
+
+ switch (marker & 0xFF) {
+ case 0xC0: /* SOF0 (baseline JPEG) */
+ /* Load segment data */
+ if (len > JD_SZBUF) return JDR_MEM2;
+ if (jd->infunc(jd, seg, len) != len) return JDR_INP;
+
+ jd->width = LDB_WORD(seg+3); /* Image width in unit of pixel */
+ jd->height = LDB_WORD(seg+1); /* Image height in unit of pixel */
+ if (seg[5] != 3) return JDR_FMT3; /* Err: Supports only Y/Cb/Cr format */
+
+ /* Check three image components */
+ for (i = 0; i < 3; i++) {
+ b = seg[7 + 3 * i]; /* Get sampling factor */
+ if (!i) { /* Y component */
+ if (b != 0x11 && b != 0x22 && b != 0x21)/* Check sampling factor */
+ return JDR_FMT3; /* Err: Supports only 4:4:4, 4:2:0 or 4:2:2 */
+ jd->msx = b >> 4; jd->msy = b & 15; /* Size of MCU [blocks] */
+ } else { /* Cb/Cr component */
+ if (b != 0x11) return JDR_FMT3; /* Err: Sampling factor of Cr/Cb must be 1 */
+ }
+ b = seg[8 + 3 * i]; /* Get dequantizer table ID for this component */
+ if (b > 3) return JDR_FMT3; /* Err: Invalid ID */
+ jd->qtid[i] = b;
+ }
+ break;
+
+ case 0xDD: /* DRI */
+ /* Load segment data */
+ if (len > JD_SZBUF) return JDR_MEM2;
+ if (jd->infunc(jd, seg, len) != len) return JDR_INP;
+
+ /* Get restart interval (MCUs) */
+ jd->nrst = LDB_WORD(seg);
+ break;
+
+ case 0xC4: /* DHT */
+ /* Load segment data */
+ if (len > JD_SZBUF) return JDR_MEM2;
+ if (jd->infunc(jd, seg, len) != len) return JDR_INP;
+
+ /* Create huffman tables */
+ rc = create_huffman_tbl(jd, seg, len);
+ if (rc) return rc;
+ break;
+
+ case 0xDB: /* DQT */
+ /* Load segment data */
+ if (len > JD_SZBUF) return JDR_MEM2;
+ if (jd->infunc(jd, seg, len) != len) return JDR_INP;
+
+ /* Create de-quantizer tables */
+ rc = create_qt_tbl(jd, seg, len);
+ if (rc) return rc;
+ break;
+
+ case 0xDA: /* SOS */
+ /* Load segment data */
+ if (len > JD_SZBUF) return JDR_MEM2;
+ if (jd->infunc(jd, seg, len) != len) return JDR_INP;
+
+ if (!jd->width || !jd->height) return JDR_FMT1; /* Err: Invalid image size */
+
+ if (seg[0] != 3) return JDR_FMT3; /* Err: Supports only three color components format */
+
+ /* Check if all tables corresponding to each components have been loaded */
+ for (i = 0; i < 3; i++) {
+ b = seg[2 + 2 * i]; /* Get huffman table ID */
+ if (b != 0x00 && b != 0x11) return JDR_FMT3; /* Err: Different table number for DC/AC element */
+ b = i ? 1 : 0;
+ if (!jd->huffbits[b][0] || !jd->huffbits[b][1]) /* Check huffman table for this component */
+ return JDR_FMT1; /* Err: Huffman table not loaded */
+ if (!jd->qttbl[jd->qtid[i]]) return JDR_FMT1; /* Err: Dequantizer table not loaded */
+ }
+
+ /* Allocate working buffer for MCU and RGB */
+ n = jd->msy * jd->msx; /* Number of Y blocks in the MCU */
+ if (!n) return JDR_FMT1; /* Err: SOF0 has not been loaded */
+ len = n * 64 * 2 + 64; /* Allocate buffer for IDCT and RGB output */
+ if (len < 256) len = 256; /* but at least 256 byte is required for IDCT */
+ jd->workbuf = alloc_pool(jd, len); /* and it may occupy a part of following MCU working buffer for RGB output */
+ if (!jd->workbuf) return JDR_MEM1; /* Err: not enough memory */
+ jd->mcubuf = alloc_pool(jd, (n + 2) * 64); /* Allocate MCU working buffer */
+ if (!jd->mcubuf) return JDR_MEM1; /* Err: not enough memory */
+
+ /* Pre-load the JPEG data to extract it from the bit stream */
+ jd->dptr = seg; jd->dctr = 0; jd->dmsk = 0; /* Prepare to read bit stream */
+ if (ofs %= JD_SZBUF) { /* Align read offset to JD_SZBUF */
+ jd->dctr = jd->infunc(jd, seg + ofs, JD_SZBUF - (UINT)ofs);
+ jd->dptr = seg + ofs - 1;
+ }
+
+ return JDR_OK; /* Initialization succeeded. Ready to decompress the JPEG image. */
+
+ case 0xC1: /* SOF1 */
+ case 0xC2: /* SOF2 */
+ case 0xC3: /* SOF3 */
+ case 0xC5: /* SOF5 */
+ case 0xC6: /* SOF6 */
+ case 0xC7: /* SOF7 */
+ case 0xC9: /* SOF9 */
+ case 0xCA: /* SOF10 */
+ case 0xCB: /* SOF11 */
+ case 0xCD: /* SOF13 */
+ case 0xCE: /* SOF14 */
+ case 0xCF: /* SOF15 */
+ case 0xD9: /* EOI */
+ return JDR_FMT3; /* Unsuppoted JPEG standard (may be progressive JPEG) */
+
+ default: /* Unknown segment (comment, exif or etc..) */
+ /* Skip segment data */
+ if (jd->infunc(jd, 0, len) != len) /* Null pointer specifies to skip bytes of stream */
+ return JDR_INP;
+ }
+ }
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Start to decompress the JPEG picture */
+/*-----------------------------------------------------------------------*/
+
+JRESULT jd_decomp (
+ JDEC* jd, /* Initialized decompression object */
+ UINT (*outfunc)(JDEC*, void*, JRECT*), /* RGB output function */
+ BYTE scale /* Output de-scaling factor (0 to 3) */
+)
+{
+ UINT x, y, mx, my;
+ WORD rst, rsc;
+ JRESULT rc;
+
+
+ if (scale > (JD_USE_SCALE ? 3 : 0)) return JDR_PAR;
+ jd->scale = scale;
+
+ mx = jd->msx * 8; my = jd->msy * 8; /* Size of the MCU (pixel) */
+
+ jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0; /* Initialize DC values */
+ rst = rsc = 0;
+
+ rc = JDR_OK;
+ for (y = 0; y < jd->height; y += my) { /* Vertical loop of MCUs */
+ for (x = 0; x < jd->width; x += mx) { /* Horizontal loop of MCUs */
+ if (jd->nrst && rst++ == jd->nrst) { /* Process restart interval if enabled */
+ rc = restart(jd, rsc++);
+ if (rc != JDR_OK) return rc;
+ rst = 1;
+ }
+ rc = mcu_load(jd); /* Load an MCU (decompress huffman coded stream and apply IDCT) */
+ if (rc != JDR_OK) return rc;
+ rc = mcu_output(jd, outfunc, x, y); /* Output the MCU (color space conversion, scaling and output) */
+ if (rc != JDR_OK) return rc;
+ }
+ }
+
+ return rc;
+}
+#endif//SUPPORT_JPEG
+
+
diff --git a/code/components/esp32-camera-master/target/esp32s3/ll_cam.c b/code/components/esp32-camera-master/target/esp32s3/ll_cam.c
new file mode 100644
index 00000000..9a1f185c
--- /dev/null
+++ b/code/components/esp32-camera-master/target/esp32s3/ll_cam.c
@@ -0,0 +1,452 @@
+// 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
+#include
+#include "soc/system_reg.h"
+#include "soc/lcd_cam_struct.h"
+#include "soc/lcd_cam_reg.h"
+#include "soc/gdma_struct.h"
+#include "soc/gdma_periph.h"
+#include "soc/gdma_reg.h"
+#include "ll_cam.h"
+#include "cam_hal.h"
+
+static const char *TAG = "s3 ll_cam";
+
+static void IRAM_ATTR ll_cam_vsync_isr(void *arg)
+{
+ //DBG_PIN_SET(1);
+ cam_obj_t *cam = (cam_obj_t *)arg;
+ BaseType_t HPTaskAwoken = pdFALSE;
+
+ typeof(LCD_CAM.lc_dma_int_st) status = LCD_CAM.lc_dma_int_st;
+ if (status.val == 0) {
+ return;
+ }
+
+ LCD_CAM.lc_dma_int_clr.val = status.val;
+
+ if (status.cam_vsync_int_st) {
+ ll_cam_send_event(cam, CAM_VSYNC_EVENT, &HPTaskAwoken);
+ }
+
+ if (HPTaskAwoken == pdTRUE) {
+ portYIELD_FROM_ISR();
+ }
+ //DBG_PIN_SET(0);
+}
+
+static void IRAM_ATTR ll_cam_dma_isr(void *arg)
+{
+ cam_obj_t *cam = (cam_obj_t *)arg;
+ BaseType_t HPTaskAwoken = pdFALSE;
+
+ typeof(GDMA.channel[cam->dma_num].in.int_st) status = GDMA.channel[cam->dma_num].in.int_st;
+ if (status.val == 0) {
+ return;
+ }
+
+ GDMA.channel[cam->dma_num].in.int_clr.val = status.val;
+
+ if (status.in_suc_eof) {
+ ll_cam_send_event(cam, CAM_IN_SUC_EOF_EVENT, &HPTaskAwoken);
+ }
+
+ if (HPTaskAwoken == pdTRUE) {
+ portYIELD_FROM_ISR();
+ }
+}
+
+bool ll_cam_stop(cam_obj_t *cam)
+{
+ if (cam->jpeg_mode || !cam->psram_mode) {
+ GDMA.channel[cam->dma_num].in.int_ena.in_suc_eof = 0;
+ GDMA.channel[cam->dma_num].in.int_clr.in_suc_eof = 1;
+ }
+ GDMA.channel[cam->dma_num].in.link.stop = 1;
+ return true;
+}
+
+esp_err_t ll_cam_deinit(cam_obj_t *cam)
+{
+ if (cam->cam_intr_handle) {
+ esp_intr_free(cam->cam_intr_handle);
+ cam->cam_intr_handle = NULL;
+ }
+
+ if (cam->dma_intr_handle) {
+ esp_intr_free(cam->dma_intr_handle);
+ cam->dma_intr_handle = NULL;
+ }
+ GDMA.channel[cam->dma_num].in.link.addr = 0x0;
+
+ LCD_CAM.cam_ctrl1.cam_start = 0;
+ LCD_CAM.cam_ctrl1.cam_reset = 1;
+ LCD_CAM.cam_ctrl1.cam_reset = 0;
+ return ESP_OK;
+}
+
+bool ll_cam_start(cam_obj_t *cam, int frame_pos)
+{
+ LCD_CAM.cam_ctrl1.cam_start = 0;
+
+ if (cam->jpeg_mode || !cam->psram_mode) {
+ GDMA.channel[cam->dma_num].in.int_clr.in_suc_eof = 1;
+ GDMA.channel[cam->dma_num].in.int_ena.in_suc_eof = 1;
+ }
+
+ LCD_CAM.cam_ctrl1.cam_reset = 1;
+ LCD_CAM.cam_ctrl1.cam_reset = 0;
+ LCD_CAM.cam_ctrl1.cam_afifo_reset = 1;
+ LCD_CAM.cam_ctrl1.cam_afifo_reset = 0;
+ GDMA.channel[cam->dma_num].in.conf0.in_rst = 1;
+ GDMA.channel[cam->dma_num].in.conf0.in_rst = 0;
+
+ LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = cam->dma_half_buffer_size - 1; // Ping pong operation
+
+ if (!cam->psram_mode) {
+ GDMA.channel[cam->dma_num].in.link.addr = ((uint32_t)&cam->dma[0]) & 0xfffff;
+ } else {
+ GDMA.channel[cam->dma_num].in.link.addr = ((uint32_t)&cam->frames[frame_pos].dma[0]) & 0xfffff;
+ }
+
+ GDMA.channel[cam->dma_num].in.link.start = 1;
+
+ LCD_CAM.cam_ctrl.cam_update = 1;
+ LCD_CAM.cam_ctrl1.cam_start = 1;
+ return true;
+}
+
+static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
+{
+ for (int x = (SOC_GDMA_PAIRS_PER_GROUP - 1); x >= 0; x--) {
+ if (GDMA.channel[x].in.link.addr == 0x0) {
+ cam->dma_num = x;
+ ESP_LOGI(TAG, "DMA Channel=%d", cam->dma_num);
+ break;
+ }
+ if (x == 0) {
+ cam_deinit();
+ ESP_LOGE(TAG, "Can't found available GDMA channel");
+ return ESP_FAIL;
+ }
+ }
+
+ if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN) == 0) {
+ REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN);
+ REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN);
+ REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST);
+ REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST);
+ }
+
+ GDMA.channel[cam->dma_num].in.int_clr.val = ~0;
+ GDMA.channel[cam->dma_num].in.int_ena.val = 0;
+
+ GDMA.channel[cam->dma_num].in.conf0.val = 0;
+ GDMA.channel[cam->dma_num].in.conf0.in_rst = 1;
+ GDMA.channel[cam->dma_num].in.conf0.in_rst = 0;
+
+ //internal SRAM only
+ if (!cam->psram_mode) {
+ GDMA.channel[cam->dma_num].in.conf0.indscr_burst_en = 1;
+ GDMA.channel[cam->dma_num].in.conf0.in_data_burst_en = 1;
+ }
+
+ GDMA.channel[cam->dma_num].in.conf1.in_check_owner = 0;
+
+ GDMA.channel[cam->dma_num].in.peri_sel.sel = 5;
+ //GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15
+ //GDMA.channel[cam->dma_num].in.sram_size.in_size = 6;//This register is used to configure the size of L2 Tx FIFO for Rx channel. 0:16 bytes, 1:24 bytes, 2:32 bytes, 3: 40 bytes, 4: 48 bytes, 5:56 bytes, 6: 64 bytes, 7: 72 bytes, 8: 80 bytes.
+ //GDMA.channel[cam->dma_num].in.wight.rx_weight = 7;//The weight of Rx channel 0-15
+ return ESP_OK;
+}
+
+esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
+{
+ if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0) {
+ REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
+ REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
+ REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);
+ REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);
+ }
+
+ LCD_CAM.cam_ctrl.val = 0;
+
+ LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;
+ LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;
+ LCD_CAM.cam_ctrl.cam_clkm_div_num = 160000000 / config->xclk_freq_hz;
+ LCD_CAM.cam_ctrl.cam_clk_sel = 3;//Select Camera module source clock. 0: no clock. 1: APLL. 2: CLK160. 3: no clock.
+
+ LCD_CAM.cam_ctrl.cam_stop_en = 0;
+ LCD_CAM.cam_ctrl.cam_vsync_filter_thres = 4; // Filter by LCD_CAM clock
+ LCD_CAM.cam_ctrl.cam_update = 0;
+ LCD_CAM.cam_ctrl.cam_byte_order = cam->swap_data;
+ LCD_CAM.cam_ctrl.cam_bit_order = 0;
+ LCD_CAM.cam_ctrl.cam_line_int_en = 0;
+ LCD_CAM.cam_ctrl.cam_vs_eof_en = 0; //1: CAM_VSYNC to generate in_suc_eof. 0: in_suc_eof is controlled by reg_cam_rec_data_cyclelen
+
+ LCD_CAM.cam_ctrl1.val = 0;
+ LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE - 1; // Cannot be assigned to 0, and it is easy to overflow
+ LCD_CAM.cam_ctrl1.cam_line_int_num = 0; // The number of hsyncs that generate hs interrupts
+ LCD_CAM.cam_ctrl1.cam_clk_inv = 0;
+ LCD_CAM.cam_ctrl1.cam_vsync_filter_en = 1;
+ LCD_CAM.cam_ctrl1.cam_2byte_en = 0;
+ LCD_CAM.cam_ctrl1.cam_de_inv = 0;
+ LCD_CAM.cam_ctrl1.cam_hsync_inv = 0;
+ LCD_CAM.cam_ctrl1.cam_vsync_inv = 0;
+ LCD_CAM.cam_ctrl1.cam_vh_de_mode_en = 0;
+
+ LCD_CAM.cam_rgb_yuv.val = 0;
+
+ LCD_CAM.cam_ctrl.cam_update = 1;
+ LCD_CAM.cam_ctrl1.cam_start = 1;
+
+ esp_err_t err = ll_cam_dma_init(cam);
+ if(err != ESP_OK) {
+ return err;
+ }
+
+ return ESP_OK;
+}
+
+void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
+{
+ LCD_CAM.lc_dma_int_clr.cam_vsync_int_clr = 1;
+ if (en) {
+ LCD_CAM.lc_dma_int_ena.cam_vsync_int_ena = 1;
+ } else {
+ LCD_CAM.lc_dma_int_ena.cam_vsync_int_ena = 0;
+ }
+}
+
+esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config)
+{
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_pclk], PIN_FUNC_GPIO);
+ gpio_set_direction(config->pin_pclk, GPIO_MODE_INPUT);
+ gpio_set_pull_mode(config->pin_pclk, GPIO_FLOATING);
+ gpio_matrix_in(config->pin_pclk, CAM_PCLK_IDX, false);
+
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_vsync], PIN_FUNC_GPIO);
+ gpio_set_direction(config->pin_vsync, GPIO_MODE_INPUT);
+ gpio_set_pull_mode(config->pin_vsync, GPIO_FLOATING);
+ gpio_matrix_in(config->pin_vsync, CAM_V_SYNC_IDX, cam->vsync_invert);
+
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_href], PIN_FUNC_GPIO);
+ gpio_set_direction(config->pin_href, GPIO_MODE_INPUT);
+ gpio_set_pull_mode(config->pin_href, GPIO_FLOATING);
+ gpio_matrix_in(config->pin_href, CAM_H_ENABLE_IDX, false);
+
+ int data_pins[8] = {
+ config->pin_d0, config->pin_d1, config->pin_d2, config->pin_d3, config->pin_d4, config->pin_d5, config->pin_d6, config->pin_d7,
+ };
+ for (int i = 0; i < 8; i++) {
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[data_pins[i]], PIN_FUNC_GPIO);
+ gpio_set_direction(data_pins[i], GPIO_MODE_INPUT);
+ gpio_set_pull_mode(data_pins[i], GPIO_FLOATING);
+ gpio_matrix_in(data_pins[i], CAM_DATA_IN0_IDX + i, false);
+ }
+
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_xclk], PIN_FUNC_GPIO);
+ gpio_set_direction(config->pin_xclk, GPIO_MODE_OUTPUT);
+ gpio_set_pull_mode(config->pin_xclk, GPIO_FLOATING);
+ gpio_matrix_out(config->pin_xclk, CAM_CLK_IDX, false, false);
+
+ return ESP_OK;
+}
+
+esp_err_t ll_cam_init_isr(cam_obj_t *cam)
+{
+ esp_err_t ret = ESP_OK;
+ ret = esp_intr_alloc_intrstatus(gdma_periph_signals.groups[0].pairs[cam->dma_num].rx_irq_id,
+ ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM,
+ (uint32_t)&GDMA.channel[cam->dma_num].in.int_st, GDMA_IN_SUC_EOF_CH0_INT_ST_M,
+ ll_cam_dma_isr, cam, &cam->dma_intr_handle);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "DMA interrupt allocation of camera failed");
+ return ret;
+ }
+
+ ret = esp_intr_alloc_intrstatus(ETS_LCD_CAM_INTR_SOURCE,
+ ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM,
+ (uint32_t)&LCD_CAM.lc_dma_int_st.val, LCD_CAM_CAM_VSYNC_INT_ST_M,
+ ll_cam_vsync_isr, cam, &cam->cam_intr_handle);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "LCD_CAM interrupt allocation of camera failed");
+ return ret;
+ }
+ return ESP_OK;
+}
+
+void ll_cam_do_vsync(cam_obj_t *cam)
+{
+ gpio_matrix_in(cam->vsync_pin, CAM_V_SYNC_IDX, !cam->vsync_invert);
+ ets_delay_us(10);
+ gpio_matrix_in(cam->vsync_pin, CAM_V_SYNC_IDX, cam->vsync_invert);
+}
+
+uint8_t ll_cam_get_dma_align(cam_obj_t *cam)
+{
+ return 16 << GDMA.channel[cam->dma_num].in.conf1.in_ext_mem_bk_size;
+}
+
+static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
+ size_t node_max = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE / cam->dma_bytes_per_item;
+ size_t line_width = cam->width * cam->in_bytes_per_pixel;
+ size_t node_size = node_max;
+ size_t nodes_per_line = 1;
+ size_t lines_per_node = 1;
+
+ // Calculate DMA Node Size so that it's divisable by or divisor of the line width
+ if(line_width >= node_max){
+ // One or more nodes will be requied for one line
+ for(size_t i = node_max; i > 0; i=i-1){
+ if ((line_width % i) == 0) {
+ node_size = i;
+ nodes_per_line = line_width / node_size;
+ break;
+ }
+ }
+ } else {
+ // One or more lines can fit into one node
+ for(size_t i = node_max; i > 0; i=i-1){
+ if ((i % line_width) == 0) {
+ node_size = i;
+ lines_per_node = node_size / line_width;
+ while((cam->height % lines_per_node) != 0){
+ lines_per_node = lines_per_node - 1;
+ node_size = lines_per_node * line_width;
+ }
+ break;
+ }
+ }
+ }
+
+ ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u",
+ node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node);
+
+ cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
+
+ size_t dma_half_buffer_max = CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX / 2 / cam->dma_bytes_per_item;
+ if (line_width > dma_half_buffer_max) {
+ ESP_LOGE(TAG, "Resolution too high");
+ return 0;
+ }
+
+ // Calculate minimum EOF size = max(mode_size, line_size)
+ size_t dma_half_buffer_min = node_size * nodes_per_line;
+
+ // Calculate max EOF size divisable by node size
+ size_t dma_half_buffer = (dma_half_buffer_max / dma_half_buffer_min) * dma_half_buffer_min;
+
+ // Adjust EOF size so that height will be divisable by the number of lines in each EOF
+ size_t lines_per_half_buffer = dma_half_buffer / line_width;
+ while((cam->height % lines_per_half_buffer) != 0){
+ dma_half_buffer = dma_half_buffer - dma_half_buffer_min;
+ lines_per_half_buffer = dma_half_buffer / line_width;
+ }
+
+ // Calculate DMA size
+ size_t dma_buffer_max = 2 * dma_half_buffer_max;
+ if (cam->psram_mode) {
+ dma_buffer_max = cam->recv_size / cam->dma_bytes_per_item;
+ }
+ size_t dma_buffer_size = dma_buffer_max;
+ if (!cam->psram_mode) {
+ dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
+ }
+
+ ESP_LOGI(TAG, "dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u",
+ dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item);
+
+ cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
+ cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
+ cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
+ return 1;
+}
+
+bool ll_cam_dma_sizes(cam_obj_t *cam)
+{
+ cam->dma_bytes_per_item = 1;
+ if (cam->jpeg_mode) {
+ if (cam->psram_mode) {
+ cam->dma_buffer_size = cam->recv_size;
+ cam->dma_half_buffer_size = 1024;
+ cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
+ cam->dma_node_buffer_size = cam->dma_half_buffer_size;
+ } else {
+ cam->dma_half_buffer_cnt = 16;
+ cam->dma_buffer_size = cam->dma_half_buffer_cnt * 1024;
+ cam->dma_half_buffer_size = cam->dma_buffer_size / cam->dma_half_buffer_cnt;
+ cam->dma_node_buffer_size = cam->dma_half_buffer_size;
+ }
+ } else {
+ return ll_cam_calc_rgb_dma(cam);
+ }
+ return 1;
+}
+
+size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len)
+{
+ // YUV to Grayscale
+ if (cam->in_bytes_per_pixel == 2 && cam->fb_bytes_per_pixel == 1) {
+ size_t end = len / 8;
+ for (size_t i = 0; i < end; ++i) {
+ out[0] = in[0];
+ out[1] = in[2];
+ out[2] = in[4];
+ out[3] = in[6];
+ out += 4;
+ in += 8;
+ }
+ return len / 2;
+ }
+
+ // just memcpy
+ memcpy(out, in, len);
+ return len;
+}
+
+esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid)
+{
+ if (pix_format == PIXFORMAT_GRAYSCALE) {
+ if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID) {
+ cam->in_bytes_per_pixel = 1; // camera sends Y8
+ } else {
+ cam->in_bytes_per_pixel = 2; // camera sends YU/YV
+ }
+ cam->fb_bytes_per_pixel = 1; // frame buffer stores Y8
+ } else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
+ cam->in_bytes_per_pixel = 2; // camera sends YU/YV
+ cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
+ } else if (pix_format == PIXFORMAT_JPEG) {
+ cam->in_bytes_per_pixel = 1;
+ cam->fb_bytes_per_pixel = 1;
+ } else {
+ ESP_LOGE(TAG, "Requested format is not supported");
+ return ESP_ERR_NOT_SUPPORTED;
+ }
+ return ESP_OK;
+}
+
+// implements function from xclk.c to allow dynamic XCLK change
+esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz)
+{
+ LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;
+ LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;
+ LCD_CAM.cam_ctrl.cam_clkm_div_num = 160000000 / xclk_freq_hz;
+ LCD_CAM.cam_ctrl.cam_clk_sel = 3;//Select Camera module source clock. 0: no clock. 1: APLL. 2: CLK160. 3: no clock.
+ LCD_CAM.cam_ctrl.cam_update = 1;
+ return ESP_OK;
+}
diff --git a/code/components/esp32-camera-master/target/private_include/ll_cam.h b/code/components/esp32-camera-master/target/private_include/ll_cam.h
new file mode 100644
index 00000000..7d30c370
--- /dev/null
+++ b/code/components/esp32-camera-master/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
+#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);
diff --git a/code/components/esp32-camera-master/driver/xclk.c b/code/components/esp32-camera-master/target/xclk.c
similarity index 81%
rename from code/components/esp32-camera-master/driver/xclk.c
rename to code/components/esp32-camera-master/target/xclk.c
index f58747d5..b5ea53e7 100644
--- a/code/components/esp32-camera-master/driver/xclk.c
+++ b/code/components/esp32-camera-master/target/xclk.c
@@ -4,6 +4,7 @@
#include "esp_log.h"
#include "esp_system.h"
#include "xclk.h"
+#include "esp_camera.h"
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
@@ -12,12 +13,15 @@
static const char* TAG = "camera_xclk";
#endif
+static ledc_channel_t g_ledc_channel = 0;
+
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz)
{
ledc_timer_config_t timer_conf;
- timer_conf.duty_resolution = 2;
+ timer_conf.duty_resolution = LEDC_TIMER_1_BIT;
timer_conf.freq_hz = xclk_freq_hz;
- timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
+ timer_conf.speed_mode = LEDC_LOW_SPEED_MODE;
+
#if ESP_IDF_VERSION_MAJOR >= 4
timer_conf.clk_cfg = LEDC_AUTO_CLK;
#endif
@@ -31,21 +35,20 @@ esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz)
esp_err_t camera_enable_out_clock(camera_config_t* config)
{
- periph_module_enable(PERIPH_LEDC_MODULE);
-
esp_err_t err = xclk_timer_conf(config->ledc_timer, config->xclk_freq_hz);
if (err != ESP_OK) {
ESP_LOGE(TAG, "ledc_timer_config failed, rc=%x", err);
return err;
}
+ g_ledc_channel = config->ledc_channel;
ledc_channel_config_t ch_conf;
ch_conf.gpio_num = config->pin_xclk;
- ch_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
+ ch_conf.speed_mode = LEDC_LOW_SPEED_MODE;
ch_conf.channel = config->ledc_channel;
ch_conf.intr_type = LEDC_INTR_DISABLE;
ch_conf.timer_sel = config->ledc_timer;
- ch_conf.duty = 2;
+ ch_conf.duty = 1;
ch_conf.hpoint = 0;
err = ledc_channel_config(&ch_conf);
if (err != ESP_OK) {
@@ -57,5 +60,5 @@ esp_err_t camera_enable_out_clock(camera_config_t* config)
void camera_disable_out_clock()
{
- periph_module_disable(PERIPH_LEDC_MODULE);
+ ledc_stop(LEDC_LOW_SPEED_MODE, g_ledc_channel, 0);
}
diff --git a/code/components/esp32-camera-master/test/CMakeLists.txt b/code/components/esp32-camera-master/test/CMakeLists.txt
new file mode 100644
index 00000000..a8c3d16b
--- /dev/null
+++ b/code/components/esp32-camera-master/test/CMakeLists.txt
@@ -0,0 +1,4 @@
+idf_component_register(SRC_DIRS .
+ PRIV_INCLUDE_DIRS .
+ PRIV_REQUIRES test_utils esp32-camera nvs_flash
+ EMBED_TXTFILES pictures/testimg.jpeg pictures/test_outside.jpeg pictures/test_inside.jpeg)
diff --git a/code/components/esp32-camera-master/test/component.mk b/code/components/esp32-camera-master/test/component.mk
new file mode 100644
index 00000000..5fb88365
--- /dev/null
+++ b/code/components/esp32-camera-master/test/component.mk
@@ -0,0 +1,8 @@
+#
+#Component Makefile
+#
+
+COMPONENT_SRCDIRS += ./
+COMPONENT_PRIV_INCLUDEDIRS += ./
+
+COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
diff --git a/code/components/esp32-camera-master/test/pictures/test_inside.jpeg b/code/components/esp32-camera-master/test/pictures/test_inside.jpeg
new file mode 100644
index 00000000..92e7bc36
Binary files /dev/null and b/code/components/esp32-camera-master/test/pictures/test_inside.jpeg differ
diff --git a/code/components/esp32-camera-master/test/pictures/test_outside.jpeg b/code/components/esp32-camera-master/test/pictures/test_outside.jpeg
new file mode 100644
index 00000000..dcb4bf1b
Binary files /dev/null and b/code/components/esp32-camera-master/test/pictures/test_outside.jpeg differ
diff --git a/code/components/esp32-camera-master/test/pictures/testimg.jpeg b/code/components/esp32-camera-master/test/pictures/testimg.jpeg
new file mode 100644
index 00000000..a026e486
Binary files /dev/null and b/code/components/esp32-camera-master/test/pictures/testimg.jpeg differ
diff --git a/code/components/esp32-camera-master/test/test_camera.c b/code/components/esp32-camera-master/test/test_camera.c
new file mode 100644
index 00000000..89ad0619
--- /dev/null
+++ b/code/components/esp32-camera-master/test/test_camera.c
@@ -0,0 +1,500 @@
+
+#include
+#include
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "unity.h"
+#include
+#include "esp_log.h"
+
+#include "esp_camera.h"
+
+#ifdef CONFIG_IDF_TARGET_ESP32
+#define BOARD_WROVER_KIT 1
+#elif defined CONFIG_IDF_TARGET_ESP32S2
+#define BOARD_CAMERA_MODEL_ESP32S2 1
+#elif defined CONFIG_IDF_TARGET_ESP32S3
+#define BOARD_CAMERA_MODEL_ESP32_S3_EYE 1
+#endif
+
+// WROVER-KIT PIN Map
+#if BOARD_WROVER_KIT
+
+#define PWDN_GPIO_NUM -1 //power down is not used
+#define RESET_GPIO_NUM -1 //software reset will be performed
+#define XCLK_GPIO_NUM 21
+#define SIOD_GPIO_NUM 26
+#define SIOC_GPIO_NUM 27
+
+#define Y9_GPIO_NUM 35
+#define Y8_GPIO_NUM 34
+#define Y7_GPIO_NUM 39
+#define Y6_GPIO_NUM 36
+#define Y5_GPIO_NUM 19
+#define Y4_GPIO_NUM 18
+#define Y3_GPIO_NUM 5
+#define Y2_GPIO_NUM 4
+#define VSYNC_GPIO_NUM 25
+#define HREF_GPIO_NUM 23
+#define PCLK_GPIO_NUM 22
+
+// ESP32Cam (AiThinker) PIN Map
+#elif BOARD_ESP32CAM_AITHINKER
+
+#define PWDN_GPIO_NUM 32
+#define RESET_GPIO_NUM -1 //software reset will be performed
+#define XCLK_GPIO_NUM 0
+#define SIOD_GPIO_NUM 26
+#define SIOC_GPIO_NUM 27
+
+#define Y9_GPIO_NUM 35
+#define Y8_GPIO_NUM 34
+#define Y7_GPIO_NUM 39
+#define Y6_GPIO_NUM 36
+#define Y5_GPIO_NUM 21
+#define Y4_GPIO_NUM 19
+#define Y3_GPIO_NUM 18
+#define Y2_GPIO_NUM 5
+#define VSYNC_GPIO_NUM 25
+#define HREF_GPIO_NUM 23
+#define PCLK_GPIO_NUM 22
+
+#elif BOARD_CAMERA_MODEL_ESP32S2
+
+#define PWDN_GPIO_NUM -1
+#define RESET_GPIO_NUM -1
+
+#define VSYNC_GPIO_NUM 21
+#define HREF_GPIO_NUM 38
+#define PCLK_GPIO_NUM 11
+#define XCLK_GPIO_NUM 40
+
+#define SIOD_GPIO_NUM 17
+#define SIOC_GPIO_NUM 18
+
+#define Y9_GPIO_NUM 39
+#define Y8_GPIO_NUM 41
+#define Y7_GPIO_NUM 42
+#define Y6_GPIO_NUM 12
+#define Y5_GPIO_NUM 3
+#define Y4_GPIO_NUM 14
+#define Y3_GPIO_NUM 37
+#define Y2_GPIO_NUM 13
+
+#elif BOARD_CAMERA_MODEL_ESP32_S3_EYE
+
+#define PWDN_GPIO_NUM 43
+#define RESET_GPIO_NUM 44
+
+#define VSYNC_GPIO_NUM 6
+#define HREF_GPIO_NUM 7
+#define PCLK_GPIO_NUM 13
+#define XCLK_GPIO_NUM 15
+
+#define SIOD_GPIO_NUM 4
+#define SIOC_GPIO_NUM 5
+
+#define Y9_GPIO_NUM 16
+#define Y8_GPIO_NUM 17
+#define Y7_GPIO_NUM 18
+#define Y6_GPIO_NUM 12
+#define Y5_GPIO_NUM 11
+#define Y4_GPIO_NUM 10
+#define Y3_GPIO_NUM 9
+#define Y2_GPIO_NUM 8
+
+#endif
+
+static const char *TAG = "test camera";
+
+typedef void (*decode_func_t)(uint8_t *jpegbuffer, uint32_t size, uint8_t *outbuffer);
+
+static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, framesize_t frame_size, uint8_t fb_count)
+{
+ framesize_t size_bak = frame_size;
+ if (PIXFORMAT_JPEG == pixel_format && FRAMESIZE_SVGA > frame_size) {
+ frame_size = FRAMESIZE_HD;
+ }
+ camera_config_t camera_config = {
+ .pin_pwdn = PWDN_GPIO_NUM,
+ .pin_reset = RESET_GPIO_NUM,
+ .pin_xclk = XCLK_GPIO_NUM,
+ .pin_sscb_sda = SIOD_GPIO_NUM,
+ .pin_sscb_scl = SIOC_GPIO_NUM,
+
+ .pin_d7 = Y9_GPIO_NUM,
+ .pin_d6 = Y8_GPIO_NUM,
+ .pin_d5 = Y7_GPIO_NUM,
+ .pin_d4 = Y6_GPIO_NUM,
+ .pin_d3 = Y5_GPIO_NUM,
+ .pin_d2 = Y4_GPIO_NUM,
+ .pin_d1 = Y3_GPIO_NUM,
+ .pin_d0 = Y2_GPIO_NUM,
+ .pin_vsync = VSYNC_GPIO_NUM,
+ .pin_href = HREF_GPIO_NUM,
+ .pin_pclk = PCLK_GPIO_NUM,
+
+ //EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode
+ .xclk_freq_hz = xclk_freq_hz,
+ .ledc_timer = LEDC_TIMER_0,
+ .ledc_channel = LEDC_CHANNEL_0,
+
+ .pixel_format = pixel_format, //YUV422,GRAYSCALE,RGB565,JPEG
+ .frame_size = frame_size, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
+
+ .jpeg_quality = 12, //0-63 lower number means higher quality
+ .fb_count = fb_count, //if more than one, i2s runs in continuous mode. Use only with JPEG
+ .grab_mode = CAMERA_GRAB_WHEN_EMPTY
+ };
+
+ //initialize the camera
+ esp_err_t ret = esp_camera_init(&camera_config);
+
+ if (ESP_OK == ret && PIXFORMAT_JPEG == pixel_format && FRAMESIZE_SVGA > size_bak) {
+ sensor_t *s = esp_camera_sensor_get();
+ s->set_framesize(s, size_bak);
+ }
+
+ return ret;
+}
+
+static bool camera_test_fps(uint16_t times, float *fps, uint32_t *size)
+{
+ *fps = 0.0f;
+ *size = 0;
+ uint32_t s = 0;
+ uint32_t num = 0;
+ uint64_t total_time = esp_timer_get_time();
+ for (size_t i = 0; i < times; i++) {
+ camera_fb_t *pic = esp_camera_fb_get();
+ if (NULL == pic) {
+ ESP_LOGW(TAG, "fb get failed");
+ return 0;
+ } else {
+ s += pic->len;
+ num++;
+ }
+ esp_camera_fb_return(pic);
+ }
+ total_time = esp_timer_get_time() - total_time;
+ if (num) {
+ *fps = num * 1000000.0f / total_time ;
+ *size = s / num;
+ }
+ return 1;
+}
+
+static const char *get_cam_format_name(pixformat_t pixel_format)
+{
+ switch (pixel_format) {
+ case PIXFORMAT_JPEG: return "JPEG";
+ case PIXFORMAT_RGB565: return "RGB565";
+ case PIXFORMAT_RGB888: return "RGB888";
+ case PIXFORMAT_YUV422: return "YUV422";
+ default:
+ break;
+ }
+ return "UNKNOW";
+}
+
+static void printf_img_base64(const camera_fb_t *pic)
+{
+ uint8_t *outbuffer = NULL;
+ size_t outsize = 0;
+ if (PIXFORMAT_JPEG != pic->format) {
+ fmt2jpg(pic->buf, pic->width * pic->height * 2, pic->width, pic->height, pic->format, 50, &outbuffer, &outsize);
+ } else {
+ outbuffer = pic->buf;
+ outsize = pic->len;
+ }
+
+ uint8_t *base64_buf = calloc(1, outsize * 4);
+ if (NULL != base64_buf) {
+ size_t out_len = 0;
+ mbedtls_base64_encode(base64_buf, outsize * 4, &out_len, outbuffer, outsize);
+ printf("%s\n", base64_buf);
+ free(base64_buf);
+ if (PIXFORMAT_JPEG != pic->format) {
+ free(outbuffer);
+ }
+ } else {
+ ESP_LOGE(TAG, "malloc for base64 buffer failed");
+ }
+}
+
+static void camera_performance_test(uint32_t xclk_freq, uint32_t pic_num)
+{
+ esp_err_t ret = ESP_OK;
+ //detect sensor information
+ TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
+ sensor_t *s = esp_camera_sensor_get();
+ camera_sensor_info_t *info = esp_camera_sensor_get_info(&s->id);
+ TEST_ASSERT_NOT_NULL(info);
+ TEST_ESP_OK(esp_camera_deinit());
+ vTaskDelay(500 / portTICK_RATE_MS);
+ framesize_t max_size = info->max_size;
+ pixformat_t all_format[] = {PIXFORMAT_JPEG, PIXFORMAT_RGB565, PIXFORMAT_YUV422, };
+ pixformat_t *format_s = &all_format[0];
+ pixformat_t *format_e = &all_format[2];
+ if (false == info->support_jpeg) {
+ format_s++; // skip jpeg
+ }
+
+ struct fps_result {
+ float fps[FRAMESIZE_INVALID];
+ uint32_t size[FRAMESIZE_INVALID];
+ };
+ struct fps_result results[3] = {0};
+
+ for (; format_s <= format_e; format_s++) {
+ for (size_t i = 0; i <= max_size; i++) {
+ ESP_LOGI(TAG, "\n\n===> Testing format:%s resolution: %d x %d <===", get_cam_format_name(*format_s), resolution[i].width, resolution[i].height);
+ ret = init_camera(xclk_freq, *format_s, i, 2);
+ vTaskDelay(100 / portTICK_RATE_MS);
+ if (ESP_OK != ret) {
+ ESP_LOGW(TAG, "Testing init failed :-(, skip this item");
+ vTaskDelay(500 / portTICK_RATE_MS);
+ continue;
+ }
+ camera_test_fps(pic_num, &results[format_s - all_format].fps[i], &results[format_s - all_format].size[i]);
+ TEST_ESP_OK(esp_camera_deinit());
+ }
+ }
+
+ printf("FPS Result\n");
+ printf("resolution , JPEG fps, JPEG size, RGB565 fps, RGB565 size, YUV422 fps, YUV422 size \n");
+ for (size_t i = 0; i <= max_size; i++) {
+ printf("%4d x %4d , %5.2f, %6d, %5.2f, %7d, %5.2f, %7d \n",
+ resolution[i].width, resolution[i].height,
+ results[0].fps[i], results[0].size[i],
+ results[1].fps[i], results[1].size[i],
+ results[2].fps[i], results[2].size[i]);
+ }
+ printf("----------------------------------------------------------------------------------------\n");
+}
+
+TEST_CASE("Camera driver init, deinit test", "[camera]")
+{
+ uint64_t t1 = esp_timer_get_time();
+ TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
+ uint64_t t2 = esp_timer_get_time();
+ ESP_LOGI(TAG, "Camera init time %llu ms", (t2 - t1) / 1000);
+
+ TEST_ESP_OK(esp_camera_deinit());
+}
+
+TEST_CASE("Camera driver take RGB565 picture test", "[camera]")
+{
+ TEST_ESP_OK(init_camera(10000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
+ vTaskDelay(500 / portTICK_RATE_MS);
+ ESP_LOGI(TAG, "Taking picture...");
+ camera_fb_t *pic = esp_camera_fb_get();
+ if (pic) {
+ ESP_LOGI(TAG, "picture: %d x %d, size: %u", pic->width, pic->height, pic->len);
+ printf_img_base64(pic);
+ esp_camera_fb_return(pic);
+ }
+
+ TEST_ESP_OK(esp_camera_deinit());
+ TEST_ASSERT_NOT_NULL(pic);
+}
+
+TEST_CASE("Camera driver take YUV422 picture test", "[camera]")
+{
+ TEST_ESP_OK(init_camera(10000000, PIXFORMAT_YUV422, FRAMESIZE_QVGA, 2));
+ vTaskDelay(500 / portTICK_RATE_MS);
+ ESP_LOGI(TAG, "Taking picture...");
+ camera_fb_t *pic = esp_camera_fb_get();
+ if (pic) {
+ ESP_LOGI(TAG, "picture: %d x %d, size: %u", pic->width, pic->height, pic->len);
+ printf_img_base64(pic);
+ esp_camera_fb_return(pic);
+ }
+
+ TEST_ESP_OK(esp_camera_deinit());
+ TEST_ASSERT_NOT_NULL(pic);
+}
+
+TEST_CASE("Camera driver take JPEG picture test", "[camera]")
+{
+ TEST_ESP_OK(init_camera(20000000, PIXFORMAT_JPEG, FRAMESIZE_QVGA, 2));
+ vTaskDelay(500 / portTICK_RATE_MS);
+ ESP_LOGI(TAG, "Taking picture...");
+ camera_fb_t *pic = esp_camera_fb_get();
+ if (pic) {
+ ESP_LOGI(TAG, "picture: %d x %d, size: %u", pic->width, pic->height, pic->len);
+ printf_img_base64(pic);
+ esp_camera_fb_return(pic);
+ }
+
+ TEST_ESP_OK(esp_camera_deinit());
+ TEST_ASSERT_NOT_NULL(pic);
+}
+
+TEST_CASE("Camera driver performance test", "[camera]")
+{
+ camera_performance_test(20 * 1000000, 16);
+}
+
+
+static void print_rgb565_img(uint8_t *img, int width, int height)
+{
+ uint16_t *p = (uint16_t *)img;
+ const char temp2char[17] = "@MNHQUJ*x7^i;.";
+ for (size_t j = 0; j < height; j++) {
+ for (size_t i = 0; i < width; i++) {
+ uint32_t c = p[j * width + i];
+ uint8_t r = c >> 11;
+ uint8_t g = (c >> 6) & 0x1f;
+ uint8_t b = c & 0x1f;
+ c = (r + g + b) / 3;
+ c >>= 1;
+ printf("%c", temp2char[15 - c]);
+ }
+ printf("\n");
+ }
+}
+
+static void print_rgb888_img(uint8_t *img, int width, int height)
+{
+ uint8_t *p = (uint8_t *)img;
+ const char temp2char[17] = "@MNHQUJ*x7^i;.";
+ for (size_t j = 0; j < height; j++) {
+ for (size_t i = 0; i < width; i++) {
+ uint8_t *c = p + 3 * (j * width + i);
+ uint8_t r = *c++;
+ uint8_t g = *c++;
+ uint8_t b = *c;
+ uint32_t v = (r + g + b) / 3;
+ v >>= 4;
+ printf("%c", temp2char[15 - v]);
+ }
+ printf("\n");
+ }
+}
+
+static void tjpgd_decode_rgb565(uint8_t *mjpegbuffer, uint32_t size, uint8_t *outbuffer)
+{
+ jpg2rgb565(mjpegbuffer, size, outbuffer, JPG_SCALE_NONE);
+}
+
+static void tjpgd_decode_rgb888(uint8_t *mjpegbuffer, uint32_t size, uint8_t *outbuffer)
+{
+ fmt2rgb888(mjpegbuffer, size, PIXFORMAT_JPEG, outbuffer);
+}
+
+typedef enum {
+ DECODE_RGB565,
+ DECODE_RGB888,
+} decode_type_t;
+
+static const decode_func_t g_decode_func[2][2] = {
+ {tjpgd_decode_rgb565,},
+ {tjpgd_decode_rgb888,},
+};
+
+
+static float jpg_decode_test(uint8_t decoder_index, decode_type_t type, const uint8_t *jpg, uint32_t length, uint32_t img_w, uint32_t img_h, uint32_t times)
+{
+ uint8_t *jpg_buf = malloc(length);
+ if (NULL == jpg_buf) {
+ ESP_LOGE(TAG, "malloc for jpg buffer failed");
+ return 0;
+ }
+ memcpy(jpg_buf, jpg, length);
+
+ uint8_t *rgb_buf = heap_caps_malloc(img_w * img_h * 3, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ if (NULL == rgb_buf) {
+ free(jpg_buf);
+ ESP_LOGE(TAG, "malloc for rgb buffer failed");
+ return 0;
+ }
+ decode_func_t decode = g_decode_func[type][decoder_index];
+ decode(jpg_buf, length, rgb_buf);
+ if (DECODE_RGB565 == type) {
+ ESP_LOGI(TAG, "jpeg decode to rgb565");
+ print_rgb565_img(rgb_buf, img_w, img_h);
+ } else {
+ ESP_LOGI(TAG, "jpeg decode to rgb888");
+ print_rgb888_img(rgb_buf, img_w, img_h);
+ }
+
+ uint64_t t_decode[times];
+ for (size_t i = 0; i < times; i++) {
+ uint64_t t1 = esp_timer_get_time();
+ decode(jpg_buf, length, rgb_buf);
+ t_decode[i] = esp_timer_get_time() - t1;
+ }
+
+ printf("resolution , t \n");
+ uint64_t t_total = 0;
+ for (size_t i = 0; i < times; i++) {
+ t_total += t_decode[i];
+ float t = t_decode[i] / 1000.0f;
+ printf("%4d x %4d , %5.2f ms \n", img_w, img_h, t);
+ }
+
+ float fps = times / (t_total / 1000000.0f);
+ printf("Decode FPS Result\n");
+ printf("resolution , fps \n");
+ printf("%4d x %4d , %5.2f \n", img_w, img_h, fps);
+
+ free(jpg_buf);
+ heap_caps_free(rgb_buf);
+ return fps;
+}
+
+static void img_jpeg_decode_test(uint16_t pic_index, uint16_t lib_index)
+{
+ extern const uint8_t img1_start[] asm("_binary_testimg_jpeg_start");
+ extern const uint8_t img1_end[] asm("_binary_testimg_jpeg_end");
+ extern const uint8_t img2_start[] asm("_binary_test_inside_jpeg_start");
+ extern const uint8_t img2_end[] asm("_binary_test_inside_jpeg_end");
+ extern const uint8_t img3_start[] asm("_binary_test_outside_jpeg_start");
+ extern const uint8_t img3_end[] asm("_binary_test_outside_jpeg_end");
+
+ struct img_t {
+ const uint8_t *buf;
+ uint32_t length;
+ uint16_t w, h;
+ };
+ struct img_t imgs[3] = {
+ {
+ .buf = img1_start,
+ .length = img1_end - img1_start,
+ .w = 227,
+ .h = 149,
+ },
+ {
+ .buf = img2_start,
+ .length = img2_end - img2_start,
+ .w = 320,
+ .h = 240,
+ },
+ {
+ .buf = img3_start,
+ .length = img3_end - img3_start,
+ .w = 480,
+ .h = 320,
+ },
+ };
+
+ ESP_LOGI(TAG, "pic_index:%d", pic_index);
+ ESP_LOGI(TAG, "lib_index:%d", lib_index);
+ jpg_decode_test(lib_index, DECODE_RGB565, imgs[pic_index].buf, imgs[pic_index].length, imgs[pic_index].w, imgs[pic_index].h, 16);
+}
+
+TEST_CASE("Conversions image 227x149 jpeg decode test", "[camera]")
+{
+ img_jpeg_decode_test(0, 0);
+}
+
+TEST_CASE("Conversions image 320x240 jpeg decode test", "[camera]")
+{
+ img_jpeg_decode_test(1, 0);
+}
+
+TEST_CASE("Conversions image 480x320 jpeg decode test", "[camera]")
+{
+ img_jpeg_decode_test(2, 0);
+}
diff --git a/code/components/jomjol_controlcamera/ClassControllCamera.cpp b/code/components/jomjol_controlcamera/ClassControllCamera.cpp
index 98b95b49..d692a9dc 100644
--- a/code/components/jomjol_controlcamera/ClassControllCamera.cpp
+++ b/code/components/jomjol_controlcamera/ClassControllCamera.cpp
@@ -73,21 +73,19 @@ static camera_config_t camera_config = {
.pin_pclk = CAM_PIN_PCLK,
//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
-// .xclk_freq_hz = 20000000, // Orginalwert
- .xclk_freq_hz = 5000000, // Test, um die Bildfehler los zu werden !!!!
+ .xclk_freq_hz = 20000000, // Orginalwert
+// .xclk_freq_hz = 5000000, // Test, um die Bildfehler los zu werden !!!!
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG, //YUV422,GRAYSCALE,RGB565,JPEG
.frame_size = FRAMESIZE_VGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
// .frame_size = FRAMESIZE_UXGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
-
-
-
.jpeg_quality = 5, //0-63 lower number means higher quality
- .fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
+ .fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
// .grab_mode = CAMERA_GRAB_WHEN_EMPTY,
-
+ .grab_mode = CAMERA_GRAB_LATEST,
+
};
@@ -231,6 +229,8 @@ void CCamera::EnableAutoExposure(int flashdauer)
vTaskDelay( xDelay );
camera_fb_t * fb = esp_camera_fb_get();
+ esp_camera_fb_return(fb);
+ fb = esp_camera_fb_get();
if (!fb) {
ESP_LOGE(TAGCAMERACLASS, "Camera Capture Failed");
LEDOnOff(false);
@@ -277,6 +277,8 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
#endif
camera_fb_t * fb = esp_camera_fb_get();
+ esp_camera_fb_return(fb);
+ fb = esp_camera_fb_get();
if (!fb) {
ESP_LOGE(TAGCAMERACLASS, "CaptureToBasisImage: Camera Capture Failed");
LEDOnOff(false);
@@ -285,7 +287,7 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
LogFile.SwitchOnOff(true);
LogFile.WriteToFile("Camera is not working anymore - most propably hardware problem (instablility, ...). "
"System will reboot.");
- doReboot();
+ doReboot();
return ESP_FAIL;
}
@@ -374,6 +376,8 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
}
camera_fb_t * fb = esp_camera_fb_get();
+ esp_camera_fb_return(fb);
+ fb = esp_camera_fb_get();
if (!fb) {
ESP_LOGE(TAGCAMERACLASS, "CaptureToFile: Camera Capture Failed");
LEDOnOff(false);
@@ -465,6 +469,8 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
}
+ fb = esp_camera_fb_get();
+ esp_camera_fb_return(fb);
fb = esp_camera_fb_get();
if (!fb) {
ESP_LOGE(TAGCAMERACLASS, "Camera capture failed");
diff --git a/code/components/jomjol_controlcamera/sensor.h b/code/components/jomjol_controlcamera/sensor_old.h
similarity index 100%
rename from code/components/jomjol_controlcamera/sensor.h
rename to code/components/jomjol_controlcamera/sensor_old.h
diff --git a/code/components/jomjol_mqtt/interface_mqtt.cpp b/code/components/jomjol_mqtt/interface_mqtt.cpp
index 3230cb8f..3dcd1cc6 100644
--- a/code/components/jomjol_mqtt/interface_mqtt.cpp
+++ b/code/components/jomjol_mqtt/interface_mqtt.cpp
@@ -111,10 +111,20 @@ void MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, st
};
client = esp_mqtt_client_init(&mqtt_cfg);
- esp_mqtt_client_register_event(client, esp_mmqtt_ID, mqtt_event_handler, client);
- esp_mqtt_client_start(client);
+ if (client)
+ {
+ if (esp_mqtt_client_register_event(client, esp_mmqtt_ID, mqtt_event_handler, client) != ESP_OK)
+ LogFile.WriteToFile("MQTT - Could not register event!");
+ if (esp_mqtt_client_start(client) != ESP_OK)
+ LogFile.WriteToFile("MQTT - Could not start client!");
+
+ MQTTPublish(_LWTContext, "", 1);
+ }
+ else
+ {
+ LogFile.WriteToFile("MQTT - Could not Init MQTT Client!");
+ }
- MQTTPublish(_LWTContext, "", 1);
}
void MQTTdestroy() {
diff --git a/code/components/jomjol_tfliteclass/server_tflite.cpp b/code/components/jomjol_tfliteclass/server_tflite.cpp
index 5f548eb6..5a088109 100644
--- a/code/components/jomjol_tfliteclass/server_tflite.cpp
+++ b/code/components/jomjol_tfliteclass/server_tflite.cpp
@@ -701,7 +701,7 @@ void TFliteDoAutoStart()
printf("task_autodoFlow configMINIMAL_STACK_SIZE: %d\n", _i);
printf("getESPHeapInfo: %s\n", getESPHeapInfo().c_str());
- xReturned = xTaskCreate(&task_autodoFlow, "task_autodoFlow", configMINIMAL_STACK_SIZE * 60, NULL, tskIDLE_PRIORITY+1, &xHandletask_autodoFlow);
+ xReturned = xTaskCreate(&task_autodoFlow, "task_autodoFlow", configMINIMAL_STACK_SIZE * 48, NULL, tskIDLE_PRIORITY+1, &xHandletask_autodoFlow);
if( xReturned != pdPASS )
{
diff --git a/code/components/jomjol_wlan/connect_wlan.cpp b/code/components/jomjol_wlan/connect_wlan.cpp
index 15c0090b..310a0a26 100644
--- a/code/components/jomjol_wlan/connect_wlan.cpp
+++ b/code/components/jomjol_wlan/connect_wlan.cpp
@@ -107,7 +107,31 @@ void LEDBlinkTask(int _dauer, int _anz, bool _off)
}
/////////////////////////////////////////////////////////
+static void event_handler(void* arg, esp_event_base_t event_base,
+ int32_t event_id, void* event_data)
+{
+ if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
+ LEDBlinkTask(200, 1, true);
+ esp_wifi_connect();
+ } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
+// if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
+ esp_wifi_connect();
+ s_retry_num++;
+ ESP_LOGI(TAG, "retry to connect to the AP");
+// } else {
+// xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
+// }
+ ESP_LOGI(TAG,"connect to the AP fail");
+ } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
+ ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
+ ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
+ s_retry_num = 0;
+ xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
+ LEDBlinkTask(1000, 5, true);
+ }
+}
+/*
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
@@ -129,6 +153,7 @@ static void event_handler(void* arg, esp_event_base_t event_base,
LEDBlinkTask(1000, 5, true);
}
}
+*/
void strinttoip4(const char *ip, int &a, int &b, int &c, int &d) {
std::string zw = std::string(ip);
@@ -140,6 +165,94 @@ void strinttoip4(const char *ip, int &a, int &b, int &c, int &d) {
void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostname, const char *_ipadr, const char *_gw, const char *_netmask, const char *_dns)
{
+ s_wifi_event_group = xEventGroupCreate();
+
+ ESP_ERROR_CHECK(esp_netif_init());
+
+ ESP_ERROR_CHECK(esp_event_loop_create_default());
+ esp_netif_t *my_sta = esp_netif_create_default_wifi_sta();
+
+ if ((_ipadr != NULL) && (_gw != NULL) && (_netmask != NULL))
+ {
+ ESP_LOGI(TAG, "set IP %s, GW %s, Netmask %s manual", _ipadr, _gw, _netmask);
+ esp_netif_dhcpc_stop(my_sta);
+ esp_netif_ip_info_t ip_info;
+ int a, b, c, d;
+ strinttoip4(_ipadr, a, b, c, d);
+ IP4_ADDR(&ip_info.ip, a, b, c, d);
+ strinttoip4(_gw, a, b, c, d);
+ IP4_ADDR(&ip_info.gw, a, b, c, d);
+ strinttoip4(_netmask, a, b, c, d);
+ IP4_ADDR(&ip_info.netmask, a, b, c, d);
+
+ esp_netif_set_ip_info(my_sta, &ip_info);
+ }
+
+ wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+ ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+
+ esp_event_handler_instance_t instance_any_id;
+ esp_event_handler_instance_t instance_got_ip;
+ ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
+ ESP_EVENT_ANY_ID,
+ &event_handler,
+ NULL,
+ &instance_any_id));
+ ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
+ IP_EVENT_STA_GOT_IP,
+ &event_handler,
+ NULL,
+ &instance_got_ip));
+ wifi_config_t wifi_config = { };
+
+ strcpy((char*)wifi_config.sta.ssid, (const char*)_ssid);
+ strcpy((char*)wifi_config.sta.password, (const char*)_password);
+
+ ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
+ ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
+ ESP_ERROR_CHECK(esp_wifi_start() );
+
+ if (_hostname != NULL)
+ {
+ esp_err_t ret = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA , _hostname);
+ hostname = std::string(_hostname);
+ if(ret != ESP_OK ){
+ ESP_LOGE(TAG,"failed to set hostname:%d",ret);
+ }
+ else {
+ ESP_LOGE(TAG,"Set Hostname to:%s", _hostname);
+ }
+
+ }
+
+ ESP_LOGI(TAG, "wifi_init_sta finished.");
+
+ /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
+ * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
+ EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
+ WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
+ pdFALSE,
+ pdFALSE,
+ portMAX_DELAY);
+
+ /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
+ * happened. */
+ if (bits & WIFI_CONNECTED_BIT) {
+ ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
+ _ssid, _password);
+ } else if (bits & WIFI_FAIL_BIT) {
+ ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
+ _ssid, _password);
+ } else {
+ ESP_LOGE(TAG, "UNEXPECTED EVENT");
+ }
+
+ /* The event will not be processed after unregister */
+// ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
+// ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
+// vEventGroupDelete(s_wifi_event_group);
+
+/*
ssid = std::string(_ssid);
s_wifi_event_group = xEventGroupCreate();
@@ -208,16 +321,16 @@ void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostna
ESP_LOGI(TAG, "wifi_init_sta finished.");
- /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
- * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
+ // Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
+ // number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above)
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
- /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
- * happened. */
+ // xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
+ // happened.
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
_ssid, _password);
@@ -227,6 +340,7 @@ void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostna
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
+*/
}
diff --git a/code/main/version.cpp b/code/main/version.cpp
index a1a3864a..1a19d5b1 100644
--- a/code/main/version.cpp
+++ b/code/main/version.cpp
@@ -1,4 +1,4 @@
-const char* GIT_REV="24b4615";
+const char* GIT_REV="e79c86c";
const char* GIT_TAG="";
-const char* GIT_BRANCH="master";
-const char* BUILD_TIME="2022-01-09 09:44";
\ No newline at end of file
+const char* GIT_BRANCH="wifi-mod";
+const char* BUILD_TIME="2022-01-14 20:20";
\ No newline at end of file
diff --git a/code/main/version.h b/code/main/version.h
index 5411f815..f9fae1a6 100644
--- a/code/main/version.h
+++ b/code/main/version.h
@@ -13,7 +13,7 @@ extern "C"
#include "Helper.h"
#include
-const char* GIT_BASE_BRANCH = "master - v10.1.0 - 2022-01-09";
+const char* GIT_BASE_BRANCH = "master - v10.2.0 - 2022-01-14";
const char* git_base_branch(void)
diff --git a/code/sdkconfig b/code/sdkconfig
index 6ad1a07f..ba188dc4 100644
--- a/code/sdkconfig
+++ b/code/sdkconfig
@@ -1221,11 +1221,17 @@ CONFIG_NT99141_SUPPORT=y
CONFIG_OV2640_SUPPORT=y
CONFIG_OV3660_SUPPORT=y
CONFIG_OV5640_SUPPORT=y
+CONFIG_GC2145_SUPPORT=y
+CONFIG_GC032A_SUPPORT=y
+CONFIG_GC0308_SUPPORT=y
# CONFIG_SCCB_HARDWARE_I2C_PORT0 is not set
CONFIG_SCCB_HARDWARE_I2C_PORT1=y
+# CONFIG_GC_SENSOR_WINDOWING_MODE is not set
+CONFIG_GC_SENSOR_SUBSAMPLE_MODE=y
CONFIG_CAMERA_CORE0=y
# CONFIG_CAMERA_CORE1 is not set
# CONFIG_CAMERA_NO_AFFINITY is not set
+CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX=32768
# end of Camera configuration
# end of Component config
diff --git a/code/sdkconfig.esp32cam b/code/sdkconfig.esp32cam
index 2e14d848..e9b10a43 100644
--- a/code/sdkconfig.esp32cam
+++ b/code/sdkconfig.esp32cam
@@ -1212,11 +1212,17 @@ CONFIG_WPA_MBEDTLS_CRYPTO=y
CONFIG_OV2640_SUPPORT=y
# CONFIG_OV3660_SUPPORT is not set
# CONFIG_OV5640_SUPPORT is not set
+CONFIG_GC2145_SUPPORT=y
+CONFIG_GC032A_SUPPORT=y
+CONFIG_GC0308_SUPPORT=y
# CONFIG_SCCB_HARDWARE_I2C_PORT0 is not set
CONFIG_SCCB_HARDWARE_I2C_PORT1=y
+# CONFIG_GC_SENSOR_WINDOWING_MODE is not set
+CONFIG_GC_SENSOR_SUBSAMPLE_MODE=y
CONFIG_CAMERA_CORE0=y
# CONFIG_CAMERA_CORE1 is not set
# CONFIG_CAMERA_NO_AFFINITY is not set
+CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX=32768
# end of Camera configuration
# end of Component config
diff --git a/code/version.cpp b/code/version.cpp
index a1a3864a..1a19d5b1 100644
--- a/code/version.cpp
+++ b/code/version.cpp
@@ -1,4 +1,4 @@
-const char* GIT_REV="24b4615";
+const char* GIT_REV="e79c86c";
const char* GIT_TAG="";
-const char* GIT_BRANCH="master";
-const char* BUILD_TIME="2022-01-09 09:44";
\ No newline at end of file
+const char* GIT_BRANCH="wifi-mod";
+const char* BUILD_TIME="2022-01-14 20:20";
\ No newline at end of file
diff --git a/firmware/bootloader.bin b/firmware/bootloader.bin
index a424168a..23925661 100644
Binary files a/firmware/bootloader.bin and b/firmware/bootloader.bin differ
diff --git a/firmware/firmware.bin b/firmware/firmware.bin
index 6152befb..6c1248c9 100644
Binary files a/firmware/firmware.bin and b/firmware/firmware.bin differ
diff --git a/firmware/html.zip b/firmware/html.zip
index 928254b6..7cc1d206 100644
Binary files a/firmware/html.zip and b/firmware/html.zip differ