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] = "@MNHQ&#UJ*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] = "@MNHQ&#UJ*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