From 2182f88230c0ecb7850b8eddb784a67ffaa28c95 Mon Sep 17 00:00:00 2001 From: Wang Yu Xin Date: Tue, 11 Oct 2022 14:53:28 +0800 Subject: [PATCH 01/90] fix: fix description of resolution in docs and examples --- README.md | 6 +++--- examples/main/take_picture.c | 6 +++--- test/test_camera.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index aeae6232d5..081d668cc0 100644 --- a/README.md +++ b/README.md @@ -152,10 +152,10 @@ static camera_config_t camera_config = { .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_JPEG,//YUV422,GRAYSCALE,RGB565,JPEG - .frame_size = FRAMESIZE_UXGA,//QQVGA-QXGA Do not use sizes above QVGA when not JPEG + .frame_size = FRAMESIZE_UXGA,//QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates. - .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 + .jpeg_quality = 12, //0-63, for OV series camera sensors, lower number means higher quality + .fb_count = 1, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode. .grab_mode = CAMERA_GRAB_WHEN_EMPTY//CAMERA_GRAB_LATEST. Sets when buffers should be filled }; diff --git a/examples/main/take_picture.c b/examples/main/take_picture.c index 783fcc0149..f81bea9444 100644 --- a/examples/main/take_picture.c +++ b/examples/main/take_picture.c @@ -120,10 +120,10 @@ static camera_config_t camera_config = { .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_RGB565, //YUV422,GRAYSCALE,RGB565,JPEG - .frame_size = FRAMESIZE_QVGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG + .frame_size = FRAMESIZE_QVGA, //QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates. - .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 + .jpeg_quality = 12, //0-63, for OV series camera sensors, lower number means higher quality + .fb_count = 1, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode. .grab_mode = CAMERA_GRAB_WHEN_EMPTY, }; diff --git a/test/test_camera.c b/test/test_camera.c index f03b7c6a0c..41a89b022f 100644 --- a/test/test_camera.c +++ b/test/test_camera.c @@ -147,10 +147,10 @@ static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, fr .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 + .frame_size = frame_size, //QQVGA-UXGAQQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates. - .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 + .jpeg_quality = 12, //0-63, for OV series camera sensors, lower number means higher quality + .fb_count = fb_count, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode. .grab_mode = CAMERA_GRAB_WHEN_EMPTY }; From ae60e38d57834e752dcd132e70c63bfb4c8c7f57 Mon Sep 17 00:00:00 2001 From: Wang Yu Xin Date: Fri, 14 Oct 2022 20:27:56 +0800 Subject: [PATCH 02/90] feat: add sccb handle addr16_val16 reg type --- driver/private_include/sccb.h | 2 ++ driver/sccb.c | 54 +++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/driver/private_include/sccb.h b/driver/private_include/sccb.h index 9e4453386a..851858c7ef 100755 --- a/driver/private_include/sccb.h +++ b/driver/private_include/sccb.h @@ -17,4 +17,6 @@ uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg); uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data); uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg); uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data); +uint16_t SCCB_Read_Addr16_Val16(uint8_t slv_addr, uint16_t reg); +uint8_t SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data); #endif // __SCCB_H__ diff --git a/driver/sccb.c b/driver/sccb.c index 945bd9bdec..0e23588c58 100755 --- a/driver/sccb.c +++ b/driver/sccb.c @@ -204,3 +204,57 @@ uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) } return ret == ESP_OK ? 0 : -1; } + +uint16_t SCCB_Read_Addr16_Val16(uint8_t slv_addr, uint16_t reg) +{ + uint16_t data = 0; + uint8_t *data_u8 = (uint8_t *)&data; + esp_err_t ret = ESP_FAIL; + uint16_t reg_htons = LITTLETOBIG(reg); + uint8_t *reg_u8 = (uint8_t *)®_htons; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if(ret != ESP_OK) return -1; + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN); + i2c_master_read_byte(cmd, &data_u8[1], ACK_VAL); + i2c_master_read_byte(cmd, &data_u8[0], NACK_VAL); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "W [%04x]=%04x fail\n", reg, data); + } + return data; +} + +uint8_t SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data) +{ + esp_err_t ret = ESP_FAIL; + uint16_t reg_htons = LITTLETOBIG(reg); + uint8_t *reg_u8 = (uint8_t *)®_htons; + uint16_t data_htons = LITTLETOBIG(data); + uint8_t *data_u8 = (uint8_t *)&data_htons; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN); + i2c_master_write_byte(cmd, data_u8[0], ACK_CHECK_EN); + i2c_master_write_byte(cmd, data_u8[1], ACK_CHECK_EN); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "W [%04x]=%04x fail\n", reg, data); + } + return ret == ESP_OK ? 0 : -1; +} From e8727c27473288db5c57c9cd049cb36c84be8e7c Mon Sep 17 00:00:00 2001 From: Wang Yu Xin Date: Thu, 27 Oct 2022 17:07:55 +0800 Subject: [PATCH 03/90] fix: heap corruption caused by dma_buffer in cam_deinit() --- driver/cam_hal.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 27f66b684d..62c27bed24 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -435,6 +435,9 @@ esp_err_t cam_deinit(void) if (cam_obj->frame_buffer_queue) { vQueueDelete(cam_obj->frame_buffer_queue); } + + ll_cam_deinit(cam_obj); + if (cam_obj->dma) { free(cam_obj->dma); } @@ -451,8 +454,6 @@ esp_err_t cam_deinit(void) free(cam_obj->frames); } - ll_cam_deinit(cam_obj); - free(cam_obj); cam_obj = NULL; return ESP_OK; From f3d04ea2c57df778b4c4be2fa194cb4e3ae9add8 Mon Sep 17 00:00:00 2001 From: Wang Yu Xin Date: Wed, 9 Nov 2022 19:19:20 +0800 Subject: [PATCH 04/90] fix: change sccb_write return int value --- driver/private_include/sccb.h | 6 +++--- driver/sccb.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/driver/private_include/sccb.h b/driver/private_include/sccb.h index 851858c7ef..1a29557676 100755 --- a/driver/private_include/sccb.h +++ b/driver/private_include/sccb.h @@ -14,9 +14,9 @@ int SCCB_Use_Port(int sccb_i2c_port); 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); +int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data); uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg); -uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data); +int SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data); uint16_t SCCB_Read_Addr16_Val16(uint8_t slv_addr, uint16_t reg); -uint8_t SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data); +int SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data); #endif // __SCCB_H__ diff --git a/driver/sccb.c b/driver/sccb.c index 0e23588c58..9fff65dbed 100755 --- a/driver/sccb.c +++ b/driver/sccb.c @@ -139,7 +139,7 @@ uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) return data; } -uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data) +int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data) { esp_err_t ret = ESP_FAIL; i2c_cmd_handle_t cmd = i2c_cmd_link_create(); @@ -184,7 +184,7 @@ uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg) return data; } -uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) +int SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) { static uint16_t i = 0; esp_err_t ret = ESP_FAIL; @@ -236,7 +236,7 @@ uint16_t SCCB_Read_Addr16_Val16(uint8_t slv_addr, uint16_t reg) return data; } -uint8_t SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data) +int SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data) { esp_err_t ret = ESP_FAIL; uint16_t reg_htons = LITTLETOBIG(reg); From ae117e9d851aa922ee9464771569aa1afca4a7df Mon Sep 17 00:00:00 2001 From: Wang Yu Xin Date: Tue, 15 Nov 2022 10:56:03 +0800 Subject: [PATCH 05/90] fix: fix bad macro definition on cam stack size --- driver/cam_hal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 62c27bed24..13623b4924 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -32,8 +32,8 @@ #endif // ESP_IDF_VERSION_MAJOR #define ESP_CAMERA_ETS_PRINTF ets_printf -#if CONFIG_CAM_TASK_STACK_SIZE -#define CAM_TASK_STACK CONFIG_CAM_TASK_STACK_SIZE +#if CONFIG_CAMERA_TASK_STACK_SIZE +#define CAM_TASK_STACK CONFIG_CAMERA_TASK_STACK_SIZE #else #define CAM_TASK_STACK (2*1024) #endif From 8bcc835cf72755d64b7c701299f18f45763cd67d Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Thu, 8 Dec 2022 13:18:34 +0000 Subject: [PATCH 06/90] OV7670 fixes (#484) * ov7670: fix ov7670_frame_control() Last value of array (VREF) was overwritten by end marker * OV7670: Adapt QQVGA VSTART value to remove bar on top Remove empty bar at the top of the frame. This is more visible on OV7675, but also present on OV7670 --- sensors/ov7670.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sensors/ov7670.c b/sensors/ov7670.c index ee64de277d..c683eda041 100644 --- a/sensors/ov7670.c +++ b/sensors/ov7670.c @@ -173,7 +173,7 @@ int ret = 0; */ static int ov7670_frame_control(sensor_t *sensor, int hstart, int hstop, int vstart, int vstop) { -struct regval_list frame[7]; + struct regval_list frame[7]; frame[0].reg_num = HSTART; frame[0].value = (hstart >> 3); @@ -194,8 +194,8 @@ struct regval_list frame[7]; frame[5].value = (((vstop & 0x02) << 2) | (vstart & 0x02)); /* End mark */ - frame[5].reg_num = 0xFF; - frame[5].value = 0xFF; + frame[6].reg_num = 0xFF; + frame[6].value = 0xFF; return ov7670_write_array(sensor, frame); } @@ -277,7 +277,7 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) case FRAMESIZE_QQVGA: if( (ret = ov7670_write_array(sensor, ov7670_qqvga)) == 0 ) { /* These values from Omnivision */ - ret = ov7670_frame_control(sensor, 158, 14, 10, 490); + ret = ov7670_frame_control(sensor, 158, 14, 12, 490); } break; From 402b811b835cd348343b567a97fdf984c9d16fb9 Mon Sep 17 00:00:00 2001 From: XDanielPaul <47739921+XDanielPaul@users.noreply.github.com> Date: Mon, 12 Dec 2022 18:53:42 +0100 Subject: [PATCH 07/90] Added badge with version to README (#487) Co-authored-by: Daniel Paul --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 081d668cc0..3c4b5eb9d5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ESP32 Camera Driver -[![Build examples](https://github.com/espressif/esp32-camera/actions/workflows/build.yml/badge.svg)](https://github.com/espressif/esp32-camera/actions/workflows/build.yml) +[![Build examples](https://github.com/espressif/esp32-camera/actions/workflows/build.yml/badge.svg)](https://github.com/espressif/esp32-camera/actions/workflows/build.yml) [![Component Registry](https://components.espressif.com/components/espressif/esp32-camera/badge.svg)](https://components.espressif.com/components/espressif/esp32-camera) ## General Information This repository hosts ESP32 series Soc compatible driver for image sensors. Additionally it provides a few tools, which allow converting the captured frame data to the more common BMP and JPEG formats. From 2478a5fc7aca43137c8b112af5805f9f5b4d0fab Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Thu, 12 Jan 2023 12:54:22 +0200 Subject: [PATCH 08/90] Fix LoadStoreAlignment panic (#491) --- driver/cam_hal.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 13623b4924..107c44b1a9 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -46,19 +46,14 @@ static const uint16_t JPEG_EOI_MARKER = 0xD9FF; // written in little-endian for 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", (int) i); - return i; - } + for (uint32_t i = 0; i < length; i++) { + if (memcmp(&inbuf[i], &JPEG_SOI_MARKER, 3) == 0) { + //ESP_LOGW(TAG, "SOI: %d", (int) i); + return i; } - ESP_LOGW(TAG, "NO-SOI"); - return -1; } - return 0; + ESP_LOGW(TAG, "NO-SOI"); + return -1; } static int cam_verify_jpeg_eoi(const uint8_t *inbuf, uint32_t length) @@ -66,8 +61,7 @@ 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) { + if (memcmp(dptr, &JPEG_EOI_MARKER, 2) == 0) { offset = dptr - inbuf; //ESP_LOGW(TAG, "EOI: %d", length - (offset + 2)); return offset; From 6edafc75013993165cdbf511c9d646ef8f1dcc07 Mon Sep 17 00:00:00 2001 From: Patryk Ludwikowski Date: Thu, 30 Mar 2023 10:42:17 +0200 Subject: [PATCH 09/90] fix: remove unnecessary new line in logs of driver initialization (#500) yes - because it hurt my eyes to see logs with this one, weird empty line... --- driver/sccb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/sccb.c b/driver/sccb.c index 9fff65dbed..307166d0fb 100755 --- a/driver/sccb.c +++ b/driver/sccb.c @@ -56,7 +56,7 @@ int SCCB_Init(int pin_sda, int pin_scl) sccb_i2c_port = SCCB_I2C_PORT_DEFAULT; sccb_owns_i2c_port = true; - ESP_LOGI(TAG, "sccb_i2c_port=%d\n", sccb_i2c_port); + ESP_LOGI(TAG, "sccb_i2c_port=%d", sccb_i2c_port); conf.mode = I2C_MODE_MASTER; conf.sda_io_num = pin_sda; From 2e6a36d57d8c06338a68d34f9744fd7e5ca48dfd Mon Sep 17 00:00:00 2001 From: Sam Washburn Date: Tue, 11 Apr 2023 00:52:11 -0700 Subject: [PATCH 10/90] Fix: Make sure there is a queue size of at least 1. (#527) Co-authored-by: Sam Washburn --- driver/cam_hal.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 107c44b1a9..3045b06407 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -383,7 +383,11 @@ esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint 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)); + size_t queue_size = cam_obj->dma_half_buffer_cnt - 1; + if (queue_size == 0) { + queue_size = 1; + } + cam_obj->event_queue = xQueueCreate(queue_size, 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; From fac950973b29cc8f36d7037175b2b8d50ea9f461 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Wed, 19 Apr 2023 20:06:33 +0200 Subject: [PATCH 11/90] Fix/pedantic build (#529) * Fix: Make esp32-camera function prototypes strict This is required for pedantic builds with -Wstrict-prototypes flag * ci: Enable pedantic example build --- .github/workflows/build.yml | 113 +++++++++++----------------------- .gitignore | 3 +- driver/include/esp_camera.h | 6 +- driver/private_include/sccb.h | 2 +- driver/private_include/xclk.h | 5 +- examples/main/take_picture.c | 4 +- target/xclk.c | 2 +- 7 files changed, 47 insertions(+), 88 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5f4f737489..2859a73d62 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,92 +6,49 @@ on: pull_request: jobs: - build-master: - runs-on: ubuntu-latest - strategy: - matrix: - idf_target: ["esp32", "esp32s2", "esp32s3"] - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - name: esp-idf build - uses: espressif/esp-idf-ci-action@main - with: - target: ${{ matrix.idf_target }} - path: 'examples' - - build-release-v5_0: + build-examples: name: Build for ${{ matrix.idf_target }} on ${{ matrix.idf_ver }} runs-on: ubuntu-latest strategy: matrix: - idf_ver: ["release-v5.0"] - idf_target: ["esp32", "esp32s2", "esp32s3"] + idf_ver: ["release-v4.1", "release-v4.2", "release-v4.3"] + idf_target: ["esp32", "esp32s2"] + exclude: + - idf_ver: "release-v4.1" + idf_target: esp32s2 # ESP32S2 support started with version 4.2 + container: espressif/idf:${{ matrix.idf_ver }} steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - name: esp-idf build - uses: espressif/esp-idf-ci-action@main - with: - esp_idf_version: ${{ matrix.idf_ver }} - target: ${{ matrix.idf_target }} - path: 'examples' - - build-release-v4_4: + - uses: actions/checkout@v1 + with: + submodules: 'true' + - name: esp-idf build + env: + IDF_TARGET: ${{ matrix.idf_target }} + shell: bash + working-directory: examples + run: | + . ${IDF_PATH}/export.sh + idf.py build + build-examples-pedantic: name: Build for ${{ matrix.idf_target }} on ${{ matrix.idf_ver }} runs-on: ubuntu-latest strategy: matrix: - idf_ver: ["v4.4"] + idf_ver: ["release-v4.4", "release-v5.0", "release-v5.1", "latest"] idf_target: ["esp32", "esp32s2", "esp32s3"] + container: espressif/idf:${{ matrix.idf_ver }} steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - name: esp-idf build - uses: espressif/esp-idf-ci-action@main - with: - esp_idf_version: ${{ matrix.idf_ver }} - target: ${{ matrix.idf_target }} - path: 'examples' - - build-release-v4_1: - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - name: esp-idf build - uses: espressif/esp-idf-ci-action@release-v4.1 - with: - path: 'examples' - - build-release-v4_2: - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - name: esp-idf build - uses: espressif/esp-idf-ci-action@release-v4.2 - with: - path: 'examples' - - build-release-v4_3: - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - name: esp-idf build - uses: espressif/esp-idf-ci-action@release-v4.3 - with: - path: 'examples' + - uses: actions/checkout@v1 + with: + submodules: 'true' + - name: esp-idf build + env: + IDF_TARGET: ${{ matrix.idf_target }} + shell: bash + working-directory: examples + run: | + . ${IDF_PATH}/export.sh + export PEDANTIC_FLAGS="-DIDF_CI_BUILD -Werror -Werror=deprecated-declarations -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function" + export EXTRA_CFLAGS="${PEDANTIC_FLAGS} -Wstrict-prototypes" + export EXTRA_CXXFLAGS="${PEDANTIC_FLAGS}" + idf.py build diff --git a/.gitignore b/.gitignore index 32125b791b..f39d5fda1b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .vscode **/build **/sdkconfig -**/sdkconfig.old \ No newline at end of file +**/sdkconfig.old +**/dependencies.lock \ No newline at end of file diff --git a/driver/include/esp_camera.h b/driver/include/esp_camera.h index ee84b307ba..26bd8103e8 100755 --- a/driver/include/esp_camera.h +++ b/driver/include/esp_camera.h @@ -194,14 +194,14 @@ esp_err_t esp_camera_init(const camera_config_t* config); * - ESP_OK on success * - ESP_ERR_INVALID_STATE if the driver hasn't been initialized yet */ -esp_err_t esp_camera_deinit(); +esp_err_t esp_camera_deinit(void); /** * @brief Obtain pointer to a frame buffer. * * @return pointer to the frame buffer */ -camera_fb_t* esp_camera_fb_get(); +camera_fb_t* esp_camera_fb_get(void); /** * @brief Return the frame buffer to be reused again. @@ -215,7 +215,7 @@ void esp_camera_fb_return(camera_fb_t * fb); * * @return pointer to the sensor */ -sensor_t * esp_camera_sensor_get(); +sensor_t * esp_camera_sensor_get(void); /** * @brief Save camera settings to non-volatile-storage (NVS) diff --git a/driver/private_include/sccb.h b/driver/private_include/sccb.h index 1a29557676..f000fba112 100755 --- a/driver/private_include/sccb.h +++ b/driver/private_include/sccb.h @@ -12,7 +12,7 @@ int SCCB_Init(int pin_sda, int pin_scl); int SCCB_Use_Port(int sccb_i2c_port); int SCCB_Deinit(void); -uint8_t SCCB_Probe(); +uint8_t SCCB_Probe(void); uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg); int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data); uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg); diff --git a/driver/private_include/xclk.h b/driver/private_include/xclk.h index 3d721a6132..922cd7029d 100755 --- a/driver/private_include/xclk.h +++ b/driver/private_include/xclk.h @@ -1,9 +1,10 @@ #pragma once #include "esp_system.h" +#include "esp_camera.h" esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz); -esp_err_t camera_enable_out_clock(); +esp_err_t camera_enable_out_clock(const camera_config_t *config); -void camera_disable_out_clock(); +void camera_disable_out_clock(void); diff --git a/examples/main/take_picture.c b/examples/main/take_picture.c index f81bea9444..41abf66e65 100644 --- a/examples/main/take_picture.c +++ b/examples/main/take_picture.c @@ -127,7 +127,7 @@ static camera_config_t camera_config = { .grab_mode = CAMERA_GRAB_WHEN_EMPTY, }; -static esp_err_t init_camera() +static esp_err_t init_camera(void) { //initialize the camera esp_err_t err = esp_camera_init(&camera_config); @@ -140,7 +140,7 @@ static esp_err_t init_camera() return ESP_OK; } -void app_main() +void app_main(void) { if(ESP_OK != init_camera()) { return; diff --git a/target/xclk.c b/target/xclk.c index f51daa9ce9..384fed66f9 100755 --- a/target/xclk.c +++ b/target/xclk.c @@ -34,7 +34,7 @@ esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz) return err; } -esp_err_t camera_enable_out_clock(camera_config_t* config) +esp_err_t camera_enable_out_clock(const camera_config_t* config) { esp_err_t err = xclk_timer_conf(config->ledc_timer, config->xclk_freq_hz); if (err != ESP_OK) { From 1cb6af89d15ee1a0032c814cde5f9149a60908ea Mon Sep 17 00:00:00 2001 From: Wouter de Bie Date: Thu, 20 Apr 2023 08:38:40 -0500 Subject: [PATCH 12/90] Adds esp_camera_return_all() (#519) --- driver/cam_hal.c | 6 ++++++ driver/esp_camera.c | 8 ++++++++ driver/include/esp_camera.h | 6 ++++++ driver/private_include/cam_hal.h | 2 ++ 4 files changed, 22 insertions(+) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 3045b06407..d9bb7cf680 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -506,3 +506,9 @@ void cam_give(camera_fb_t *dma_buffer) } } } + +void cam_give_all(void) { + for (int x = 0; x < cam_obj->frame_cnt; x++) { + cam_obj->frames[x].en = 1; + } +} diff --git a/driver/esp_camera.c b/driver/esp_camera.c index a71b7e1722..47407a0154 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -476,3 +476,11 @@ esp_err_t esp_camera_load_from_nvs(const char *key) return ret; } } + +void esp_camera_return_all(void) { + if (s_state == NULL) { + return; + } + cam_give_all(); +} + diff --git a/driver/include/esp_camera.h b/driver/include/esp_camera.h index 26bd8103e8..2a25a56287 100755 --- a/driver/include/esp_camera.h +++ b/driver/include/esp_camera.h @@ -231,6 +231,12 @@ esp_err_t esp_camera_save_to_nvs(const char *key); */ esp_err_t esp_camera_load_from_nvs(const char *key); +/** + * @brief Return all frame buffers to be reused again. + */ +void esp_camera_return_all(void); + + #ifdef __cplusplus } #endif diff --git a/driver/private_include/cam_hal.h b/driver/private_include/cam_hal.h index c8e38ed476..cd0d3aaeec 100644 --- a/driver/private_include/cam_hal.h +++ b/driver/private_include/cam_hal.h @@ -55,6 +55,8 @@ camera_fb_t *cam_take(TickType_t timeout); void cam_give(camera_fb_t *dma_buffer); +void cam_give_all(void); + #ifdef __cplusplus } #endif From 36b8b4e096fbd5ecd6896eb113a2fb63b4769460 Mon Sep 17 00:00:00 2001 From: Wouter de Bie Date: Thu, 20 Apr 2023 08:39:09 -0500 Subject: [PATCH 13/90] Uninstalls ISR service on cam deinit (#516) Since `gpio_install_isr_service()` is called when initializing the camera, but on de-initiazlization the ISR service is not uninstalled. This causes an error `gpio_install_isr_service(410): GPIO isr service already installed` when the camera is re-initialized. Co-authored-by: Me No Dev --- target/esp32/ll_cam.c | 2 +- target/esp32s2/ll_cam.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/esp32/ll_cam.c b/target/esp32/ll_cam.c index cc4065e48c..566ebb5f59 100644 --- a/target/esp32/ll_cam.c +++ b/target/esp32/ll_cam.c @@ -253,7 +253,7 @@ esp_err_t ll_cam_deinit(cam_obj_t *cam) esp_intr_free(cam->cam_intr_handle); cam->cam_intr_handle = NULL; } - + gpio_uninstall_isr_service(); return ESP_OK; } diff --git a/target/esp32s2/ll_cam.c b/target/esp32s2/ll_cam.c index ef7d582604..667a554336 100644 --- a/target/esp32s2/ll_cam.c +++ b/target/esp32s2/ll_cam.c @@ -95,7 +95,7 @@ esp_err_t ll_cam_deinit(cam_obj_t *cam) esp_intr_free(cam->cam_intr_handle); cam->cam_intr_handle = NULL; } - + gpio_uninstall_isr_service(); return ESP_OK; } From e689c3b082985ee7b90198be32d330ce51ac5367 Mon Sep 17 00:00:00 2001 From: Vikram Dattu Date: Tue, 25 Apr 2023 17:10:46 +0530 Subject: [PATCH 14/90] Support: jpeg decoder on esp32c2 (#525) * Extend s/w jpeg decoder functions for chips missing rom tjpgd Also, disabled camera_init and capture from example for chips not supporting camera Signed-off-by: Vikram * Extend CI build support for C3 and C2 Signed-off-by: Vikram --------- Signed-off-by: Vikram --- .github/workflows/build.yml | 5 +- CMakeLists.txt | 13 ++++- conversions/esp_jpg_decode.c | 8 ++- driver/esp_camera.c | 2 +- driver/include/esp_camera.h | 18 ++++--- examples/main/take_picture.c | 13 +++-- target/jpeg_include/tjpgd.h | 99 ++++++++++++++++++++++++++++++++++++ target/{esp32s2 => }/tjpgd.c | 0 8 files changed, 141 insertions(+), 17 deletions(-) create mode 100644 target/jpeg_include/tjpgd.h rename target/{esp32s2 => }/tjpgd.c (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2859a73d62..bd35672a7d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,7 +35,10 @@ jobs: strategy: matrix: idf_ver: ["release-v4.4", "release-v5.0", "release-v5.1", "latest"] - idf_target: ["esp32", "esp32s2", "esp32s3"] + idf_target: ["esp32", "esp32s2", "esp32s3", "esp32c2", "esp32c3"] + exclude: + - idf_ver: "release-v4.4" + idf_target: esp32c2 # ESP32C2 support started with version 5.0 container: espressif/idf:${{ matrix.idf_ver }} steps: - uses: actions/checkout@v1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a9267ba9a..65e46aef60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST list(APPEND COMPONENT_SRCS target/xclk.c target/esp32s2/ll_cam.c - target/esp32s2/tjpgd.c + target/tjpgd.c ) list(APPEND COMPONENT_PRIV_INCLUDEDIRS @@ -84,4 +84,15 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST endif() +# CONFIG_ESP_ROM_HAS_JPEG_DECODE is available from IDF v4.4 but +# previous IDF supported chips already support JPEG decoder, hence okay to use this +if(idf_version VERSION_GREATER_EQUAL "4.4" AND NOT CONFIG_ESP_ROM_HAS_JPEG_DECODE) + list(APPEND COMPONENT_SRCS + target/tjpgd.c + ) + list(APPEND COMPONENT_PRIV_INCLUDEDIRS + target/jpeg_include/ + ) +endif() + register_component() diff --git a/conversions/esp_jpg_decode.c b/conversions/esp_jpg_decode.c index 52833a733d..168325baf5 100644 --- a/conversions/esp_jpg_decode.c +++ b/conversions/esp_jpg_decode.c @@ -17,16 +17,14 @@ #if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+ #if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 #include "esp32/rom/tjpgd.h" -#elif CONFIG_IDF_TARGET_ESP32S2 -#include "tjpgd.h" #elif CONFIG_IDF_TARGET_ESP32S3 #include "esp32s3/rom/tjpgd.h" #elif CONFIG_IDF_TARGET_ESP32C3 #include "esp32c3/rom/tjpgd.h" -#elif CONFIG_IDF_TARGET_ESP32H2 -#include "esp32h2/rom/tjpgd.h" +#elif CONFIG_ESP_ROM_HAS_JPEG_DECODE // available since IDF 4.4 +#include "rom/tjpgd.h" // latest IDFs have `rom/` includes available #else -#error Target CONFIG_IDF_TARGET is not supported +#include "tjpgd.h" // using software decoder #endif #else // ESP32 Before IDF 4.0 #include "rom/tjpgd.h" diff --git a/driver/esp_camera.c b/driver/esp_camera.c index 47407a0154..4b7e906a57 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -245,7 +245,7 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out ESP_LOGD(TAG, "Doing SW reset of sensor"); vTaskDelay(10 / portTICK_PERIOD_MS); - + return s_state->sensor.reset(&s_state->sensor); err : CAMERA_DISABLE_OUT_CLOCK(); diff --git a/driver/include/esp_camera.h b/driver/include/esp_camera.h index 2a25a56287..ce031c88d4 100755 --- a/driver/include/esp_camera.h +++ b/driver/include/esp_camera.h @@ -72,6 +72,12 @@ #include "sys/time.h" #include "sdkconfig.h" +/** + * @brief define for if chip supports camera + */ +#define ESP_CAMERA_SUPPORTED (CONFIG_IDF_TARGET_ESP32 | CONFIG_IDF_TARGET_ESP32S3 | \ + CONFIG_IDF_TARGET_ESP32S2) + #ifdef __cplusplus extern "C" { #endif @@ -85,7 +91,7 @@ typedef enum { } camera_grab_mode_t; /** - * @brief Camera frame buffer location + * @brief Camera frame buffer location */ typedef enum { CAMERA_FB_IN_PSRAM, /*!< Frame buffer is placed in external PSRAM */ @@ -99,7 +105,7 @@ typedef enum { typedef enum { CONV_DISABLE, RGB565_TO_YUV422, - + YUV422_TO_RGB565, YUV422_TO_YUV420 } camera_conv_mode_t; @@ -219,15 +225,15 @@ sensor_t * esp_camera_sensor_get(void); /** * @brief Save camera settings to non-volatile-storage (NVS) - * - * @param key A unique nvs key name for the camera settings + * + * @param key A unique nvs key name for the camera settings */ esp_err_t esp_camera_save_to_nvs(const char *key); /** * @brief Load camera settings from non-volatile-storage (NVS) - * - * @param key A unique nvs key name for the camera settings + * + * @param key A unique nvs key name for the camera settings */ esp_err_t esp_camera_load_from_nvs(const char *key); diff --git a/examples/main/take_picture.c b/examples/main/take_picture.c index 41abf66e65..3a243d2554 100644 --- a/examples/main/take_picture.c +++ b/examples/main/take_picture.c @@ -10,7 +10,7 @@ /** * 2. Kconfig setup - * + * * If you have a Kconfig file, copy the content from * https://github.com/espressif/esp32-camera/blob/master/Kconfig into it. * In case you haven't, copy and paste this Kconfig file inside the src directory. @@ -20,9 +20,9 @@ /** * 3. Enable PSRAM on sdkconfig: - * + * * CONFIG_ESP32_SPIRAM_SUPPORT=y - * + * * More info on * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#config-esp32-spiram-support */ @@ -95,6 +95,7 @@ static const char *TAG = "example:take_picture"; +#if ESP_CAMERA_SUPPORTED static camera_config_t camera_config = { .pin_pwdn = CAM_PIN_PWDN, .pin_reset = CAM_PIN_RESET, @@ -139,9 +140,11 @@ static esp_err_t init_camera(void) return ESP_OK; } +#endif void app_main(void) { +#if ESP_CAMERA_SUPPORTED if(ESP_OK != init_camera()) { return; } @@ -157,4 +160,8 @@ void app_main(void) vTaskDelay(5000 / portTICK_RATE_MS); } +#else + ESP_LOGE(TAG, "Camera support is not available for this chip"); + return; +#endif } diff --git a/target/jpeg_include/tjpgd.h b/target/jpeg_include/tjpgd.h new file mode 100644 index 0000000000..31fbc97cce --- /dev/null +++ b/target/jpeg_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/target/esp32s2/tjpgd.c b/target/tjpgd.c similarity index 100% rename from target/esp32s2/tjpgd.c rename to target/tjpgd.c From f595abfa0f04ec746ebdc7d21af058a11c00601e Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 18 Jul 2023 12:31:51 +0300 Subject: [PATCH 15/90] Fix SOC_GDMA_PAIRS_PER_GROUP undefined in newer ESP-IDF --- target/esp32s3/ll_cam.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index 2c1e79225b..1280cf9160 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -33,6 +33,10 @@ #define ets_delay_us(a) esp_rom_delay_us(a) #endif +#if !defined(SOC_GDMA_PAIRS_PER_GROUP) && defined(SOC_GDMA_PAIRS_PER_GROUP_MAX) +#define SOC_GDMA_PAIRS_PER_GROUP SOC_GDMA_PAIRS_PER_GROUP_MAX +#endif + static const char *TAG = "s3 ll_cam"; static void IRAM_ATTR ll_cam_vsync_isr(void *arg) From c0c17bd3de5ea575b775db6cbef84befd020d524 Mon Sep 17 00:00:00 2001 From: Juan Schiavoni <55675185+jjsch-dev@users.noreply.github.com> Date: Tue, 18 Jul 2023 06:49:07 -0300 Subject: [PATCH 16/90] Fails with ESP32 (Standard) and not run with ESP-IDF v5.2 (#542) * Fails with ESP32 (standard) and ESP-IDF v5.2 With IDF 5.2 master it works with esp32-s3 but fails with esp32 standard, and has two problems: First is that when compiling it does not find the function: ets_delay_us'. implicit declaration of function 'ets_delay_us'; Second that it fails to initialize xlock. camera_xclk: ledc_timer_config failed, rc=103 The solution is to add #define ets_delay_us esp_rom_delay_us when the idf version is greater than 5 and initialize the variable timer_conf.deconfigure = false; * Build fails with ESP32 (Standard) and does not run with ESP-IDF v5.2 With IDF 5.2 master it works with esp32-s3 but fails with esp32 standard, and has two problems: First is that when compiling it does not find the function: ets_delay_us'. implicit declaration of function 'ets_delay_us'; Second that it fails to initialize xlock. camera_xclk: ledc_timer_config failed, rc=103 The solution is to add #define ets_delay_us esp_rom_delay_us when the idf version is greater than 5 and initialize the variable timer_conf.deconfigure = false; * Fails with ESP32 (Standard) and not run with ESP-IDF v5.2 With IDF 5.2 master it works with esp32-s3 but fails with esp32 standard, and has two problems: First is that when compiling it does not find the function: ets_delay_us'. implicit declaration of function 'ets_delay_us'; Second that it fails to initialize xlock. camera_xclk: ledc_timer_config failed, rc=103 The solution is to add #define ets_delay_us esp_rom_delay_us when the idf version is greater than 5 and initialize the variable timer_conf.deconfigure = false; --------- Co-authored-by: Me No Dev --- target/esp32/ll_cam.c | 4 ++++ target/xclk.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/target/esp32/ll_cam.c b/target/esp32/ll_cam.c index 566ebb5f59..12a9e70315 100644 --- a/target/esp32/ll_cam.c +++ b/target/esp32/ll_cam.c @@ -44,6 +44,10 @@ static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num) #define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c) #endif +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 2) +#define ets_delay_us esp_rom_delay_us +#endif + static const char *TAG = "esp32 ll_cam"; #define I2S_ISR_ENABLE(i) {I2S0.int_clr.i = 1;I2S0.int_ena.i = 1;} diff --git a/target/xclk.c b/target/xclk.c index 384fed66f9..cb67b677a5 100755 --- a/target/xclk.c +++ b/target/xclk.c @@ -23,6 +23,10 @@ esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz) timer_conf.freq_hz = xclk_freq_hz; timer_conf.speed_mode = LEDC_LOW_SPEED_MODE; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) + timer_conf.deconfigure = false; +#endif + #if ESP_IDF_VERSION_MAJOR >= 4 timer_conf.clk_cfg = LEDC_AUTO_CLK; #endif From d1c9c2cdb3fab523e81e8d953305c00ed54c834c Mon Sep 17 00:00:00 2001 From: yuxinwww <67987549+WangYuxin-esp@users.noreply.github.com> Date: Tue, 8 Aug 2023 17:01:58 +0800 Subject: [PATCH 17/90] feat: add bf20a6 output Only Y format support (#557) --- README.md | 2 +- sensors/bf20a6.c | 6 ++++++ target/esp32/ll_cam.c | 2 +- target/esp32s2/ll_cam.c | 2 +- target/esp32s3/ll_cam.c | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3c4b5eb9d5..792f657565 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi | 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" | | BF3005 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/4" | -| BF20A6 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer | 1/10" | +| BF20A6 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
Only Y | 1/10" | | SC101IOT| 1280 x 720 | color | YUV/YCbCr422
Raw RGB | 1/4.2" | | SC030IOT| 640 x 480 | color | YUV/YCbCr422
RAW Bayer | 1/6.5" | | SC031GS | 640 x 480 | monochrome | RAW MONO
Grayscale | 1/6" | diff --git a/sensors/bf20a6.c b/sensors/bf20a6.c index b1179c30f8..f8eb127d54 100644 --- a/sensors/bf20a6.c +++ b/sensors/bf20a6.c @@ -161,6 +161,12 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) case PIXFORMAT_RAW: set_reg_bits(sensor, 0x12, 0, 1, 0x1); break; + case PIXFORMAT_GRAYSCALE: + write_reg(sensor->slv_addr, 0x12, 0x23); + write_reg(sensor->slv_addr, 0x3a, 0x00); + write_reg(sensor->slv_addr, 0xe1, 0x92); + write_reg(sensor->slv_addr, 0xe3, 0x02); + break; default: ESP_LOGW(TAG, "set_pix unsupport format"); ret = -1; diff --git a/target/esp32/ll_cam.c b/target/esp32/ll_cam.c index 12a9e70315..e1a2fee719 100644 --- a/target/esp32/ll_cam.c +++ b/target/esp32/ll_cam.c @@ -491,7 +491,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, 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 || sensor_pid == SC031GS_PID) { + if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID) { if (xclk_freq_hz > 10000000) { sampling_mode = SM_0A00_0B00; dma_filter = ll_cam_dma_filter_yuyv_highspeed; diff --git a/target/esp32s2/ll_cam.c b/target/esp32s2/ll_cam.c index 667a554336..93313e8f38 100644 --- a/target/esp32s2/ll_cam.c +++ b/target/esp32s2/ll_cam.c @@ -394,7 +394,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, 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 || sensor_pid == SC031GS_PID) { + if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID) { cam->in_bytes_per_pixel = 1; // camera sends Y8 } else { cam->in_bytes_per_pixel = 2; // camera sends YU/YV diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index 1280cf9160..28a260384d 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -493,7 +493,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, 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 || sensor_pid == SC031GS_PID) { + if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID) { cam->in_bytes_per_pixel = 1; // camera sends Y8 } else { cam->in_bytes_per_pixel = 2; // camera sends YU/YV From 772aefdb67927f9cee2966024353f286326244dd Mon Sep 17 00:00:00 2001 From: Vikram Dattu Date: Wed, 11 Oct 2023 21:25:12 +0530 Subject: [PATCH 18/90] Fix cam_take going into infinite loop (#578) - Check if we have remaining ticks before going for math and calling cam_take recursively --- driver/cam_hal.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index d9bb7cf680..5377653fe5 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -435,7 +435,7 @@ esp_err_t cam_deinit(void) } ll_cam_deinit(cam_obj); - + if (cam_obj->dma) { free(cam_obj->dma); } @@ -484,7 +484,11 @@ camera_fb_t *cam_take(TickType_t timeout) } else { ESP_LOGW(TAG, "NO-EOI"); cam_give(dma_buffer); - return cam_take(timeout - (xTaskGetTickCount() - start));//recurse!!!! + TickType_t ticks_spent = xTaskGetTickCount() - start; + if (ticks_spent >= timeout) { + return NULL; /* We are out of time */ + } + return cam_take(timeout - ticks_spent);//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 From efe711df9a348c56e56e5d7b8961836d94a0f9c1 Mon Sep 17 00:00:00 2001 From: yuxinwww <67987549+WangYuxin-esp@users.noreply.github.com> Date: Wed, 11 Oct 2023 23:55:58 +0800 Subject: [PATCH 19/90] fix: add SC031GS VGA framesize support (#581) --- sensors/private_include/sc031gs_settings.h | 120 ++++++++++++++++++++- sensors/sc031gs.c | 22 ++-- 2 files changed, 127 insertions(+), 15 deletions(-) diff --git a/sensors/private_include/sc031gs_settings.h b/sensors/private_include/sc031gs_settings.h index 255c1b28a6..ba5ca02d86 100644 --- a/sensors/private_include/sc031gs_settings.h +++ b/sensors/private_include/sc031gs_settings.h @@ -78,10 +78,13 @@ struct sc031gs_regval { uint8_t val; }; -// 200*200, xclk=10M, fps=120fps -static const struct sc031gs_regval sc031gs_default_init_regs[] = { - {0x0103, 0x01}, // soft reset. +static const struct sc031gs_regval sc031gs_reset_regs[] = { + {0x0103, 0x01}, // soft reset. {REG_DELAY, 10}, // delay. +}; + +// 200*200, xclk=10M, fps=120fps +static const struct sc031gs_regval sc031gs_200x200_init_regs[] = { {0x0100, 0x00}, {0x36e9, 0x80}, {0x36f9, 0x80}, @@ -200,3 +203,114 @@ static const struct sc031gs_regval sc031gs_default_init_regs[] = { {0x3317, 0x0e}, {REG_NULL, 0x00}, }; + +// 640*480, xclk=20M, fps=50fps, xclk=10M, fps=25fps +static const struct sc031gs_regval sc031gs_640x480_50fps_init_regs[] = { + {0x0100, 0x00}, + {0x36e9, 0x80}, + {0x36f9, 0x80}, + {0x300f, 0x0f}, + {0x3018, 0x1f}, + {0x3019, 0xff}, + {0x301c, 0xb4}, + {0x301f, 0x6c}, + {0x3028, 0x82}, + {0x3200, 0x00}, + {0x3201, 0x00}, + {0x3202, 0x00}, + {0x3203, 0x08}, + {0x3204, 0x02}, + {0x3205, 0x8f}, + {0x3206, 0x01}, + {0x3207, 0xf7}, + {SC031GS_OUTPUT_WINDOW_WIDTH_H_REG, 0x02}, + {SC031GS_OUTPUT_WINDOW_WIDTH_L_REG, 0x80}, + {SC031GS_OUTPUT_WINDOW_HIGH_H_REG, 0x01}, + {SC031GS_OUTPUT_WINDOW_HIGH_L_REG, 0xe0}, + {0x320c, 0x03}, + {0x320d, 0x6e}, + {0x320e, 0x04}, + {0x320f, 0x72}, + {SC031GS_OUTPUT_WINDOW_START_Y_H_REG, 0x00}, + {SC031GS_OUTPUT_WINDOW_START_Y_L_REG, 0x08}, + {SC031GS_OUTPUT_WINDOW_START_X_H_REG, 0x00}, + {SC031GS_OUTPUT_WINDOW_START_X_L_REG, 0x08}, + {0x3220, 0x10}, + {0x3223, 0x50}, + {0x3250, 0xf0}, + {0x3251, 0x02}, + {0x3252, 0x03}, + {0x3253, 0xb0}, + {0x3254, 0x02}, + {0x3255, 0x07}, + {0x3304, 0x48}, + {0x3306, 0x38}, + {0x3309, 0x68}, + {0x330b, 0xe0}, + {0x330c, 0x18}, + {0x330f, 0x20}, + {0x3310, 0x10}, + {0x3314, 0x6d}, + {0x3315, 0x38}, + {0x3316, 0x68}, + {0x3317, 0x0f}, + {0x3329, 0x5c}, + {0x332d, 0x5c}, + {0x332f, 0x60}, + {0x3335, 0x64}, + {0x3344, 0x64}, + {0x335b, 0x80}, + {0x335f, 0x80}, + {0x3366, 0x06}, + {0x3385, 0x31}, + {0x3387, 0x51}, + {0x3389, 0x01}, + {0x33b1, 0x03}, + {0x33b2, 0x06}, + {0x3621, 0xa4}, + {0x3622, 0x05}, + {0x3624, 0x47}, + {0x3631, 0x48}, + {0x3633, 0x52}, + {0x3635, 0x18}, + {0x3636, 0x25}, + {0x3637, 0x89}, + {0x3638, 0x0f}, + {0x3639, 0x08}, + {0x363a, 0x00}, + {0x363b, 0x48}, + {0x363c, 0x06}, + {0x363e, 0xf8}, + {0x3640, 0x00}, + {0x3641, 0x01}, + {0x36ea, 0x36}, + {0x36eb, 0x1a}, + {0x36ec, 0x0a}, + {0x36ed, 0x23}, + {0x36fa, 0x36}, + {0x36fb, 0x10}, + {0x36fc, 0x01}, + {0x36fd, 0x03}, + {0x3908, 0x91}, + {0x3d08, 0x01}, + {0x3e01, 0x14}, + {0x3e02, 0x80}, + {0x3e06, 0x0c}, + {0x3f04, 0x03}, + {0x3f05, 0x4e}, + {0x4500, 0x59}, + {0x4501, 0xc4}, + {0x4809, 0x01}, + {0x4837, 0x1b}, + {0x5011, 0x00}, + {0x36e9, 0x20}, + {0x36f9, 0x24}, + {0x0100, 0x01}, // must write 0x0100 with 0x01, must delay no less then 7ms + //delay 10ms + {REG_DELAY, 0X0a}, + {0x4418, 0x08}, + {0x4419, 0x80}, + {0x363d, 0x10}, + {0x3630, 0x48}, + {REG_NULL, 0x00}, +}; \ No newline at end of file diff --git a/sensors/sc031gs.c b/sensors/sc031gs.c index dbd75ea054..627b70136d 100644 --- a/sensors/sc031gs.c +++ b/sensors/sc031gs.c @@ -204,7 +204,7 @@ static int set_aec_value(sensor_t *sensor, int value) static int reset(sensor_t *sensor) { - int ret = write_regs(sensor->slv_addr, sc031gs_default_init_regs); + int ret = write_regs(sensor->slv_addr, sc031gs_reset_regs); if (ret) { ESP_LOGE(TAG, "reset fail"); } @@ -217,17 +217,11 @@ static int set_output_window(sensor_t *sensor, int offset_x, int offset_y, int w { int ret = 0; //sc:H_start={0x3212[1:0],0x3213},H_length={0x3208[1:0],0x3209}, - // printf("%d, %d, %d, %d\r\n", ((offset_x>>8) & 0x03), offset_x & 0xff, ((w>>8) & 0x03), w & 0xff); - WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_START_X_H_REG, 0x0); // For now, we use x_start is 0x04 - WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_START_X_L_REG, 0x04); WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_WIDTH_H_REG, ((w>>8) & 0x03)); WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_WIDTH_L_REG, w & 0xff); //sc:V_start={0x3210[1:0],0x3211},V_length={0x320a[1:0],0x320b}, - // printf("%d, %d, %d, %d\r\n", ((offset_y>>8) & 0x03), offset_y & 0xff, ((h>>8) & 0x03), h & 0xff); - WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_START_Y_H_REG, 0x0); // For now, we use y_start is 0x08 - WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_START_Y_L_REG, 0x08); WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_HIGH_H_REG, ((h>>8) & 0x03)); WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_HIGH_L_REG, h & 0xff); @@ -240,17 +234,21 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) { uint16_t w = resolution[framesize].width; uint16_t h = resolution[framesize].height; - if(w > SC031GS_MAX_FRAME_WIDTH || h > SC031GS_MAX_FRAME_HIGH) { - goto err; - } - if(w != 200 || h != 200) { - ESP_LOGE(TAG, "Only support 200*200 for now, contact us if you want to use other resolutions"); + struct sc031gs_regval const *framesize_regs = sc031gs_200x200_init_regs; + if(framesize > FRAMESIZE_VGA) { goto err; + } else if(framesize > FRAMESIZE_QVGA) { + framesize_regs = sc031gs_640x480_50fps_init_regs; } uint16_t offset_x = (640-w) /2 + 4; uint16_t offset_y = (480-h) /2 + 4; + + int ret = write_regs(sensor->slv_addr, framesize_regs); + if (ret) { + ESP_LOGE(TAG, "reset fail"); + } if(set_output_window(sensor, offset_x, offset_y, w, h)) { goto err; From 81cd88c6a0d3a9f9a2da708b2febc7cedc1973a1 Mon Sep 17 00:00:00 2001 From: yuxinwww <67987549+WangYuxin-esp@users.noreply.github.com> Date: Wed, 8 Nov 2023 21:44:40 +0800 Subject: [PATCH 20/90] fix: add gc0308 output only Y format support (#589) --- README.md | 2 +- sensors/gc0308.c | 4 ++++ target/esp32/ll_cam.c | 2 +- target/esp32s2/ll_cam.c | 2 +- target/esp32s3/ll_cam.c | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 792f657565..316a1d90bb 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi | 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" | +| GC0308 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
RGB565
Grayscale | 1/6.5" | | GC2145 | 1600 x 1200 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/5" | | BF3005 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/4" | | BF20A6 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
Only Y | 1/10" | diff --git a/sensors/gc0308.c b/sensors/gc0308.c index f19025ebb3..176f012ac0 100644 --- a/sensors/gc0308.c +++ b/sensors/gc0308.c @@ -160,6 +160,10 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) write_reg(sensor->slv_addr, 0xfe, 0x00); ret = set_reg_bits(sensor->slv_addr, 0x24, 0, 0x0f, 2); //yuv422 Y Cb Y Cr break; + case PIXFORMAT_GRAYSCALE: + write_reg(sensor->slv_addr, 0xfe, 0x00); + ret = write_reg(sensor->slv_addr, 0x24, 0xb1); + break; default: ESP_LOGW(TAG, "unsupport format"); ret = -1; diff --git a/target/esp32/ll_cam.c b/target/esp32/ll_cam.c index e1a2fee719..4c486444c8 100644 --- a/target/esp32/ll_cam.c +++ b/target/esp32/ll_cam.c @@ -491,7 +491,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, 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 || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID) { + if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID || sensor_pid == GC0308_PID) { if (xclk_freq_hz > 10000000) { sampling_mode = SM_0A00_0B00; dma_filter = ll_cam_dma_filter_yuyv_highspeed; diff --git a/target/esp32s2/ll_cam.c b/target/esp32s2/ll_cam.c index 93313e8f38..a1a7479b9c 100644 --- a/target/esp32s2/ll_cam.c +++ b/target/esp32s2/ll_cam.c @@ -394,7 +394,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, 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 || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID) { + if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID || sensor_pid == GC0308_PID) { cam->in_bytes_per_pixel = 1; // camera sends Y8 } else { cam->in_bytes_per_pixel = 2; // camera sends YU/YV diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index 28a260384d..515bfe1546 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -493,7 +493,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, 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 || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID) { + if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID || sensor_pid == GC0308_PID) { cam->in_bytes_per_pixel = 1; // camera sends Y8 } else { cam->in_bytes_per_pixel = 2; // camera sends YU/YV From 8e1a00391a0bcc3b9669516133b26155e2451439 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 9 Nov 2023 17:23:30 +0100 Subject: [PATCH 21/90] Maintenance changes in the component, examples and readme (#597) * maint(build): remove legacy GNU Make build system support * maint(example): move the example so that component manager finds it * maint(example): specify component dependency * maint(build): clean up legacy 'register_component' usage * maint(build): specify more fields in component manifest * doc(readme): simplify installation instructions See https://github.com/espressif/esp32-camera/issues/596 * maint(ci): remove tests for EOL versions (v4.1, v4.2) * fix(build): add missing transitive dependency on driver component --- .github/workflows/build.yml | 9 ++-- CMakeLists.txt | 34 +++++++----- README.md | 52 +++++++------------ component.mk | 4 -- examples/Makefile | 11 ---- examples/{ => camera_example}/CMakeLists.txt | 6 +-- examples/camera_example/main/CMakeLists.txt | 3 ++ .../camera_example/main/idf_component.yml | 5 ++ .../{ => camera_example}/main/take_picture.c | 0 .../{ => camera_example}/sdkconfig.defaults | 0 examples/main/CMakeLists.txt | 3 -- examples/main/component.mk | 5 -- idf_component.yml | 3 ++ 13 files changed, 55 insertions(+), 80 deletions(-) delete mode 100755 component.mk delete mode 100644 examples/Makefile rename examples/{ => camera_example}/CMakeLists.txt (67%) create mode 100644 examples/camera_example/main/CMakeLists.txt create mode 100644 examples/camera_example/main/idf_component.yml rename examples/{ => camera_example}/main/take_picture.c (100%) rename examples/{ => camera_example}/sdkconfig.defaults (100%) delete mode 100644 examples/main/CMakeLists.txt delete mode 100644 examples/main/component.mk diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bd35672a7d..35eb6dd6c4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,11 +11,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - idf_ver: ["release-v4.1", "release-v4.2", "release-v4.3"] + idf_ver: ["release-v4.3"] idf_target: ["esp32", "esp32s2"] - exclude: - - idf_ver: "release-v4.1" - idf_target: esp32s2 # ESP32S2 support started with version 4.2 container: espressif/idf:${{ matrix.idf_ver }} steps: - uses: actions/checkout@v1 @@ -25,7 +22,7 @@ jobs: env: IDF_TARGET: ${{ matrix.idf_target }} shell: bash - working-directory: examples + working-directory: examples/camera_example run: | . ${IDF_PATH}/export.sh idf.py build @@ -48,7 +45,7 @@ jobs: env: IDF_TARGET: ${{ matrix.idf_target }} shell: bash - working-directory: examples + working-directory: examples/camera_example run: | . ${IDF_PATH}/export.sh export PEDANTIC_FLAGS="-DIDF_CI_BUILD -Werror -Werror=deprecated-declarations -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function" diff --git a/CMakeLists.txt b/CMakeLists.txt index 65e46aef60..a3c60fdfd6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}") # set conversion sources -set(COMPONENT_SRCS +set(srcs conversions/yuv.c conversions/to_jpg.cpp conversions/to_bmp.c @@ -10,11 +10,11 @@ set(COMPONENT_SRCS conversions/esp_jpg_decode.c ) -set(COMPONENT_PRIV_INCLUDEDIRS +set(priv_include_dirs conversions/private_include ) -set(COMPONENT_ADD_INCLUDEDIRS +set(include_dirs driver/include conversions/include ) @@ -23,7 +23,7 @@ set(COMPONENT_REQUIRES driver) # set driver sources only for supported platforms if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3") - list(APPEND COMPONENT_SRCS + list(APPEND srcs driver/esp_camera.c driver/cam_hal.c driver/sccb.c @@ -44,42 +44,42 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST sensors/sc031gs.c ) - list(APPEND COMPONENT_PRIV_INCLUDEDIRS + list(APPEND priv_include_dirs driver/private_include sensors/private_include target/private_include ) if(IDF_TARGET STREQUAL "esp32") - list(APPEND COMPONENT_SRCS + list(APPEND srcs target/xclk.c target/esp32/ll_cam.c ) endif() if(IDF_TARGET STREQUAL "esp32s2") - list(APPEND COMPONENT_SRCS + list(APPEND srcs target/xclk.c target/esp32s2/ll_cam.c target/tjpgd.c ) - list(APPEND COMPONENT_PRIV_INCLUDEDIRS + list(APPEND priv_include_dirs target/esp32s2/private_include ) endif() if(IDF_TARGET STREQUAL "esp32s3") - list(APPEND COMPONENT_SRCS + list(APPEND srcs target/esp32s3/ll_cam.c ) endif() - set(COMPONENT_PRIV_REQUIRES freertos nvs_flash) + set(priv_requires freertos nvs_flash) set(min_version_for_esp_timer "4.2") if (idf_version VERSION_GREATER_EQUAL min_version_for_esp_timer) - list(APPEND COMPONENT_PRIV_REQUIRES esp_timer) + list(APPEND priv_requires esp_timer) endif() endif() @@ -87,12 +87,18 @@ endif() # CONFIG_ESP_ROM_HAS_JPEG_DECODE is available from IDF v4.4 but # previous IDF supported chips already support JPEG decoder, hence okay to use this if(idf_version VERSION_GREATER_EQUAL "4.4" AND NOT CONFIG_ESP_ROM_HAS_JPEG_DECODE) - list(APPEND COMPONENT_SRCS + list(APPEND srcs target/tjpgd.c ) - list(APPEND COMPONENT_PRIV_INCLUDEDIRS + list(APPEND priv_include_dirs target/jpeg_include/ ) endif() -register_component() +idf_component_register( + SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_INCLUDE_DIRS ${priv_include_dirs} + REQUIRES driver # due to include of driver/gpio.h in esp_camera.h + PRIV_REQUIRES ${priv_requires} +) diff --git a/README.md b/README.md index 316a1d90bb..092f893f8a 100644 --- a/README.md +++ b/README.md @@ -40,13 +40,25 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi ## Installation Instructions -### Using esp-idf +### Using with ESP-IDF -- Clone or download and extract the repository to the components folder of your ESP-IDF project +- Add a dependency on `espressif/esp32-camera` component: + ```bash + idf.py add-dependency "espressif/esp32-camera" + ``` + (or add it manually in idf_component.yml of your project) - Enable PSRAM in `menuconfig` (also set Flash and PSRAM frequiencies to 80MHz) - Include `esp_camera.h` in your code -### Using PlatformIO +These instructions also work for PlatformIO, if you are using `framework=espidf`. + +### Using with Arduino + +#### Arduino IDE + +If you are using the arduino-esp32 core in Arduino IDE, no installation is needed! You can use esp32-camera right away. + +#### PlatformIO The easy way -- on the `env` section of `platformio.ini`, add the following: @@ -68,41 +80,15 @@ Enable PSRAM on `menuconfig` or type it direclty on `sdkconfig`. Check the [offi CONFIG_ESP32_SPIRAM_SUPPORT=y ``` -***Arduino*** The easy-way (content above) only seems to work if you're using `framework=arduino` which seems to take a bunch of the guesswork out (thanks Arduino!) but also suck up a lot more memory and flash, almost crippling the performance. If you plan to use the `framework=espidf` then read the sections below carefully!! - -## Platform.io lib/submodule (for framework=espidf) +## Examples -It's probably easier to just skip the platform.io library registry version and link the git repo as a submodule. (i.e. using code outside the platform.io library management). In this example we will install this as a submodule inside the platform.io $project/lib folder: -``` -cd $project\lib -git submodule add -b master https://github.com/espressif/esp32-camera.git -``` +This component comes with a basic example illustrating how to get frames from the camera. You can try out the example using the following command: -Then in `platformio.ini` file ``` -build_flags = - -I../lib/esp32-camera +idf.py create-project-from-example "espressif/esp32-camera:camera_example" ``` -After that `#include "esp_camera.h"` statement will be available. Now the module is included, and you're hopefully back to the same place as the easy-Arduino way. - -**Warning about platform.io/espidf and fresh (not initialized) git repos** -There is a sharp-edge on you'll discover in the platform.io build process (in espidf v3.3 & 4.0.1) where a project which has only had `git init` but nothing committed will crash platform.io build process with highly non-useful output. The cause is due to lack of a version (making you think you did something wrong, when you didn't at all) - the output is horribly non-descript. Solution: the devs want you to create a file called version.txt with a number in it, or simply commit any file to the projects git repo and use git. This happens because platform.io build process tries to be too clever and determine the build version number from the git repo - it's a sharp edge you'll only encounter if you're experimenting on a new project with no commits .. like wtf is my camera not working let's try a 'clean project'?! -## Platform.io Kconfig -Kconfig is used by the platform.io menuconfig (accessed by running: `pio run -t menuconfig`) to interactively manage the various #ifdef statements throughout the espidf and supporting libraries (i.e. this repo: esp32-camera and arduino-esp32.git). The menuconfig process generates the `sdkconfig` file which is ultimately used behind the scenes by espidf compile+build process. - -**Make sure to append or symlink** [this `Kconfig`](./Kconfig) content into the `Kconfig` of your project. - -You symlink (or copy) the included Kconfig into your platform.io projects src directory. The file should be named `Kconfig.projbuild` in your projects src\ directory or you could also add the library path to a CMakefile.txt and hope the `Kconfig` (or `Kconfig.projbuild`) gets discovered by the menuconfig process, though this unpredictable for me. - -The unpredictable wonky behavior in platform.io build process around Kconfig naming (Kconfig vs. Kconfig.projbuild) occurs between espidf versions 3.3 and 4.0 - but if you don't see "Camera configuration" in your `pio run -t menuconfig` then there is no point trying to test camera code (it may compile, but it probably won't work!) and it seems the platform.io devs (when they built their wrapper around the espidf menuconfig) didn't implement it properly. You've probably already figured out you can't use the espidf build tools since the files are in totally different locations and also different versions with sometimes different syntax. This is one of those times you might consider changing the `platformio.ini` from `platform=espressif32` to `platform=https://github.com/platformio/platform-espressif32.git#develop` to get a more recent version of the espidf 4.0 tools. - -However with a bit of patience and experimenting you'll figure the Kconfig out. Once Kconfig (or Kconfig.projbuild) is working then you will be able to choose the configurations according to your setup or the camera libraries will be compiled. Although you might also need to delete your .pio/build directory before the options appear .. again, the `pio run -t menuconfig` doens't always notice the new Kconfig files! - -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! - - -## Examples +This command will download the example into `camera_example` directory. It comes already pre-configured with the correct settings in menuconfig. ### Initialization diff --git a/component.mk b/component.mk deleted file mode 100755 index 8db15eb883..0000000000 --- a/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -COMPONENT_ADD_INCLUDEDIRS := driver/include conversions/include -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/examples/Makefile b/examples/Makefile deleted file mode 100644 index f06df0ecc9..0000000000 --- a/examples/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# -# This is a project Makefile. It is assumed the directory this Makefile resides in is a -# project subdirectory. -# - -PROJECT_NAME := camera_example - -EXTRA_COMPONENT_DIRS := ../ - -include $(IDF_PATH)/make/project.mk - diff --git a/examples/CMakeLists.txt b/examples/camera_example/CMakeLists.txt similarity index 67% rename from examples/CMakeLists.txt rename to examples/camera_example/CMakeLists.txt index 0a03968845..c44adc7b70 100644 --- a/examples/CMakeLists.txt +++ b/examples/camera_example/CMakeLists.txt @@ -2,8 +2,6 @@ # 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) +set(COMPONENTS main) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(camera_example) \ No newline at end of file +project(camera_example) diff --git a/examples/camera_example/main/CMakeLists.txt b/examples/camera_example/main/CMakeLists.txt new file mode 100644 index 0000000000..0811eaadb5 --- /dev/null +++ b/examples/camera_example/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS take_picture.c + PRIV_INCLUDE_DIRS . + PRIV_REQUIRES nvs_flash) diff --git a/examples/camera_example/main/idf_component.yml b/examples/camera_example/main/idf_component.yml new file mode 100644 index 0000000000..efa9638bc0 --- /dev/null +++ b/examples/camera_example/main/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + espressif/esp32-camera: + version: "*" + override_path: "../../../" + diff --git a/examples/main/take_picture.c b/examples/camera_example/main/take_picture.c similarity index 100% rename from examples/main/take_picture.c rename to examples/camera_example/main/take_picture.c diff --git a/examples/sdkconfig.defaults b/examples/camera_example/sdkconfig.defaults similarity index 100% rename from examples/sdkconfig.defaults rename to examples/camera_example/sdkconfig.defaults diff --git a/examples/main/CMakeLists.txt b/examples/main/CMakeLists.txt deleted file mode 100644 index 1735fb184b..0000000000 --- a/examples/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -set(COMPONENT_SRCS take_picture.c) -set(COMPONENT_ADD_INCLUDEDIRS .) -register_component() \ No newline at end of file diff --git a/examples/main/component.mk b/examples/main/component.mk deleted file mode 100644 index 0b9d7585e7..0000000000 --- a/examples/main/component.mk +++ /dev/null @@ -1,5 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) - diff --git a/idf_component.yml b/idf_component.yml index 2b98f8d06c..d51d3691b7 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,2 +1,5 @@ description: ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors. url: https://github.com/espressif/esp32-camera +issues: https://github.com/espressif/esp32-camera/issues +documentation: https://github.com/espressif/esp32-camera/tree/main/README.md +repository: https://github.com/espressif/esp32-camera.git From dba8da9898928d9808d57a0b0cdcde9f130ed8fe Mon Sep 17 00:00:00 2001 From: Larry Li Date: Fri, 10 Nov 2023 18:35:17 +0800 Subject: [PATCH 22/90] Fix right column & row of the measure window (#588) Co-authored-by: Me No Dev --- sensors/gc0308.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sensors/gc0308.c b/sensors/gc0308.c index 176f012ac0..09d333183a 100644 --- a/sensors/gc0308.c +++ b/sensors/gc0308.c @@ -257,8 +257,8 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) 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, 0xf9, (col_s + w) / 4); + write_reg(sensor->slv_addr, 0xfa, (row_s + h) / 4); write_reg(sensor->slv_addr, 0x05, H8(row_s)); write_reg(sensor->slv_addr, 0x06, L8(row_s)); From 8df17f7286ff8c7fd4a60edba2b0d53446ca669b Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Mon, 22 Jan 2024 11:27:48 +0200 Subject: [PATCH 23/90] Align the frame buffers to the structure alignment (#623) * Align the frame buffers to the structure alignment cc: https://github.com/esp-rs/esp-idf-sys/issues/278 cc: https://github.com/esp-rs/rust/pull/195 * Include stdalign.h --- driver/cam_hal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 5377653fe5..a6e2e1df4e 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -14,6 +14,7 @@ #include #include +#include #include "esp_heap_caps.h" #include "ll_cam.h" #include "cam_hal.h" @@ -265,7 +266,7 @@ static esp_err_t cam_dma_config(const camera_config_t *config) 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_obj->frames = (cam_frame_t *)heap_caps_aligned_calloc(alignof(cam_frame_t), 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; From 8ee9c75dffc132b80ac74cfb1d8a3ce221840321 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Mon, 22 Jan 2024 11:31:09 +0200 Subject: [PATCH 24/90] Update build config to include IDFv5.2 and remove IDFv4.3 (EOL) (#624) * Update build config to include IDFv5.2 and remove IDFv4.3 (EOL) * Update build.yml --- .github/workflows/build.yml | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 35eb6dd6c4..90fc5dc2b4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,27 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - idf_ver: ["release-v4.3"] - idf_target: ["esp32", "esp32s2"] - container: espressif/idf:${{ matrix.idf_ver }} - steps: - - uses: actions/checkout@v1 - with: - submodules: 'true' - - name: esp-idf build - env: - IDF_TARGET: ${{ matrix.idf_target }} - shell: bash - working-directory: examples/camera_example - run: | - . ${IDF_PATH}/export.sh - idf.py build - build-examples-pedantic: - name: Build for ${{ matrix.idf_target }} on ${{ matrix.idf_ver }} - runs-on: ubuntu-latest - strategy: - matrix: - idf_ver: ["release-v4.4", "release-v5.0", "release-v5.1", "latest"] + idf_ver: ["release-v4.4", "release-v5.0", "release-v5.1", "release-v5.2", "latest"] idf_target: ["esp32", "esp32s2", "esp32s3", "esp32c2", "esp32c3"] exclude: - idf_ver: "release-v4.4" From 984999f26b41a7e62bbcad8b521a696abbce2626 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 23 Jan 2024 12:16:48 +0200 Subject: [PATCH 25/90] Create workaround for DMA failing on ESP32-S3 when WiFi is started (#625) * Create workaround for DMA failing on ESP32-S3 when WiFi is started Relates to https://github.com/espressif/esp32-camera/issues/620 * Add comment explaining the workaround --- driver/cam_hal.c | 13 ++++++ target/esp32s3/ll_cam.c | 70 +++++++++++++++++++++++---------- target/private_include/ll_cam.h | 4 ++ 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index a6e2e1df4e..356e2cd72c 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -474,6 +474,16 @@ 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 CONFIG_IDF_TARGET_ESP32S3 + // Currently (22.01.2024) there is a bug in ESP-IDF v5.2, that causes + // GDMA to fall into a strange state if it is running while WiFi STA is connecting. + // This code tries to reset GDMA if frame is not received, to try and help with + // this case. It is possible to have some side effects too, though none come to mind + if (!dma_buffer) { + ll_cam_dma_reset(cam_obj); + xQueueReceive(cam_obj->frame_buffer_queue, (void *)&dma_buffer, timeout); + } +#endif if (dma_buffer) { if(cam_obj->jpeg_mode){ // find the end marker for JPEG. Data after that can be discarded @@ -498,6 +508,9 @@ camera_fb_t *cam_take(TickType_t timeout) return dma_buffer; } else { ESP_LOGW(TAG, "Failed to get the frame on time!"); +// #if CONFIG_IDF_TARGET_ESP32S3 +// ll_cam_dma_print_state(cam_obj); +// #endif } return NULL; } diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index 515bfe1546..193c1821bc 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -39,6 +39,54 @@ static const char *TAG = "s3 ll_cam"; +void ll_cam_dma_print_state(cam_obj_t *cam) +{ + esp_rom_printf("dma_infifo_status[%u] :\n", cam->dma_num); + esp_rom_printf(" infifo_full_l1 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_full_l1); + esp_rom_printf(" infifo_empty_l1 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_empty_l1); + esp_rom_printf(" infifo_full_l2 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_full_l2); + esp_rom_printf(" infifo_empty_l2 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_empty_l2); + esp_rom_printf(" infifo_full_l3 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_full_l3); + esp_rom_printf(" infifo_empty_l3 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_empty_l3); + esp_rom_printf(" infifo_cnt_l1 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_cnt_l1); + esp_rom_printf(" infifo_cnt_l2 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_cnt_l2); + esp_rom_printf(" infifo_cnt_l3 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_cnt_l3); + esp_rom_printf(" in_remain_under_1b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_1b_l3); + esp_rom_printf(" in_remain_under_2b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_2b_l3); + esp_rom_printf(" in_remain_under_3b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_3b_l3); + esp_rom_printf(" in_remain_under_4b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_4b_l3); + esp_rom_printf(" in_buf_hungry : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_buf_hungry); + esp_rom_printf("dma_state[%u] :\n", cam->dma_num); + esp_rom_printf(" dscr_addr : 0x%lx\n", GDMA.channel[cam->dma_num].in.state.dscr_addr); + esp_rom_printf(" in_dscr_state : %lu\n", GDMA.channel[cam->dma_num].in.state.in_dscr_state); + esp_rom_printf(" in_state : %lu\n", GDMA.channel[cam->dma_num].in.state.in_state); +} + +void ll_cam_dma_reset(cam_obj_t *cam) +{ + + 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.conf1.in_ext_mem_bk_size = 2; + + GDMA.channel[cam->dma_num].in.peri_sel.sel = 5; + //GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15 + //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 +} + static void IRAM_ATTR ll_cam_vsync_isr(void *arg) { //DBG_PIN_SET(1); @@ -164,27 +212,7 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam) 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.conf1.in_ext_mem_bk_size = 2; - - GDMA.channel[cam->dma_num].in.peri_sel.sel = 5; - //GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15 - //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 + ll_cam_dma_reset(cam); return ESP_OK; } diff --git a/target/private_include/ll_cam.h b/target/private_include/ll_cam.h index 34c8da36dd..da80a667ef 100644 --- a/target/private_include/ll_cam.h +++ b/target/private_include/ll_cam.h @@ -142,6 +142,10 @@ uint8_t ll_cam_get_dma_align(cam_obj_t *cam); bool ll_cam_dma_sizes(cam_obj_t *cam); size_t 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); +#if CONFIG_IDF_TARGET_ESP32S3 +void ll_cam_dma_print_state(cam_obj_t *cam); +void ll_cam_dma_reset(cam_obj_t *cam); +#endif // implemented in cam_hal void ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType_t * HPTaskAwoken); From f0bb42917cddcfba2c32c2e2fb2875b4fea11b7a Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Mon, 29 Jan 2024 13:21:30 +0200 Subject: [PATCH 26/90] Implement proper GDMA allocator and ISR from IRAM option (#629) * Implement proper GDMA allocator * Add Kconfig option to execute ISR from IRAM on S2 and S3 Up until now, default was to execute from IRAM. This changes that for S2 and S3. ESP32 is left unchanged, because of the work that is required on it. --- Kconfig | 7 ++ target/esp32s2/ll_cam.c | 8 +-- target/esp32s3/ll_cam.c | 122 ++++++++++++++++++++------------ target/private_include/ll_cam.h | 14 ++++ 4 files changed, 102 insertions(+), 49 deletions(-) diff --git a/Kconfig b/Kconfig index 2632c8235f..a07ba19440 100755 --- a/Kconfig +++ b/Kconfig @@ -210,4 +210,11 @@ menu "Camera configuration" Full color range mode has a wider color range, so details in the image show more clearly. Please confirm the color range mode of the current camera sensor, incorrect color range mode may cause color difference in the final converted image. Full range mode is used by default. If this option is not selected, the format conversion function will be done using the limited range mode. + + config LCD_CAM_ISR_IRAM_SAFE + bool "Execute camera ISR from IRAM" + depends on (IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3) + default n + help + If this option is enabled, camera ISR will execute from IRAM. endmenu diff --git a/target/esp32s2/ll_cam.c b/target/esp32s2/ll_cam.c index a1a7479b9c..1c3fdb10e3 100644 --- a/target/esp32s2/ll_cam.c +++ b/target/esp32s2/ll_cam.c @@ -37,7 +37,7 @@ 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) +static void CAMERA_ISR_IRAM_ATTR ll_cam_vsync_isr(void *arg) { //DBG_PIN_SET(1); cam_obj_t *cam = (cam_obj_t *)arg; @@ -54,7 +54,7 @@ static void IRAM_ATTR ll_cam_vsync_isr(void *arg) //DBG_PIN_SET(0); } -static void IRAM_ATTR ll_cam_dma_isr(void *arg) +static void CAMERA_ISR_IRAM_ATTR ll_cam_dma_isr(void *arg) { cam_obj_t *cam = (cam_obj_t *)arg; BaseType_t HPTaskAwoken = pdFALSE; @@ -217,7 +217,7 @@ esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config) 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_install_isr_service(ESP_INTR_FLAG_LOWMED | CAMERA_ISR_IRAM_FLAG); gpio_isr_handler_add(config->pin_vsync, ll_cam_vsync_isr, cam); gpio_intr_disable(config->pin_vsync); @@ -255,7 +255,7 @@ 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) { - 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); + return esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_LOWMED | CAMERA_ISR_IRAM_FLAG, ll_cam_dma_isr, cam, &cam->cam_intr_handle); } void ll_cam_do_vsync(cam_obj_t *cam) diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index 193c1821bc..c61846999e 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -20,6 +20,8 @@ #include "soc/gdma_struct.h" #include "soc/gdma_periph.h" #include "soc/gdma_reg.h" +#include "hal/clk_gate_ll.h" +#include "esp_private/gdma.h" #include "ll_cam.h" #include "cam_hal.h" #include "esp_rom_gpio.h" @@ -87,7 +89,7 @@ void ll_cam_dma_reset(cam_obj_t *cam) //GDMA.channel[cam->dma_num].in.wight.rx_weight = 7;//The weight of Rx channel 0-15 } -static void IRAM_ATTR ll_cam_vsync_isr(void *arg) +static void CAMERA_ISR_IRAM_ATTR ll_cam_vsync_isr(void *arg) { //DBG_PIN_SET(1); cam_obj_t *cam = (cam_obj_t *)arg; @@ -110,7 +112,7 @@ static void IRAM_ATTR ll_cam_vsync_isr(void *arg) //DBG_PIN_SET(0); } -static void IRAM_ATTR ll_cam_dma_isr(void *arg) +static void CAMERA_ISR_IRAM_ATTR ll_cam_dma_isr(void *arg) { cam_obj_t *cam = (cam_obj_t *)arg; BaseType_t HPTaskAwoken = pdFALSE; @@ -141,25 +143,6 @@ bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam) 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; @@ -191,27 +174,72 @@ bool ll_cam_start(cam_obj_t *cam, int frame_pos) return true; } -static esp_err_t ll_cam_dma_init(cam_obj_t *cam) +esp_err_t ll_cam_deinit(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 (cam->cam_intr_handle) { + esp_intr_free(cam->cam_intr_handle); + cam->cam_intr_handle = NULL; } - 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); + if (cam->dma_intr_handle) { + esp_intr_free(cam->dma_intr_handle); + cam->dma_intr_handle = NULL; + } + gdma_disconnect(cam->dma_channel_handle); + gdma_del_channel(cam->dma_channel_handle); + cam->dma_channel_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; +} + +static esp_err_t ll_cam_dma_init(cam_obj_t *cam) +{ + //alloc rx gdma channel + gdma_channel_alloc_config_t rx_alloc_config = { + .direction = GDMA_CHANNEL_DIRECTION_RX, + }; + esp_err_t ret = gdma_new_channel(&rx_alloc_config, &cam->dma_channel_handle); + if (ret != ESP_OK) { + cam_deinit(); + ESP_LOGE(TAG, "Can't find available GDMA channel"); + return ESP_FAIL; + } + int chan_id = -1; + ret = gdma_get_channel_id(cam->dma_channel_handle, &chan_id); + if (ret != ESP_OK) { + cam_deinit(); + ESP_LOGE(TAG, "Can't get GDMA channel number"); + return ESP_FAIL; + } + cam->dma_num = chan_id; + ESP_LOGI(TAG, "DMA Channel=%d", cam->dma_num); + // 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 (!periph_ll_periph_enabled(PERIPH_GDMA_MODULE)) { + periph_ll_disable_clk_set_rst(PERIPH_GDMA_MODULE); + periph_ll_enable_clk_clear_rst(PERIPH_GDMA_MODULE); } + // 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); + // } ll_cam_dma_reset(cam); return ESP_OK; } @@ -267,12 +295,16 @@ static esp_err_t ll_cam_converter_config(cam_obj_t *cam, const camera_config_t * esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config) { esp_err_t ret = ESP_OK; - if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0) { - REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN); - REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN); - REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST); - REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST); + if (!periph_ll_periph_enabled(PERIPH_LCD_CAM_MODULE)) { + periph_ll_disable_clk_set_rst(PERIPH_LCD_CAM_MODULE); + periph_ll_enable_clk_clear_rst(PERIPH_LCD_CAM_MODULE); } + // 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; @@ -369,7 +401,7 @@ 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, + ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | CAMERA_ISR_IRAM_FLAG, (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) { @@ -378,7 +410,7 @@ esp_err_t ll_cam_init_isr(cam_obj_t *cam) } ret = esp_intr_alloc_intrstatus(ETS_LCD_CAM_INTR_SOURCE, - ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM, + ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | CAMERA_ISR_IRAM_FLAG, (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) { diff --git a/target/private_include/ll_cam.h b/target/private_include/ll_cam.h index da80a667ef..df6135f27e 100644 --- a/target/private_include/ll_cam.h +++ b/target/private_include/ll_cam.h @@ -38,6 +38,17 @@ #if __has_include("esp_private/periph_ctrl.h") # include "esp_private/periph_ctrl.h" #endif +#if __has_include("esp_private/gdma.h") +# include "esp_private/gdma.h" +#endif + +#if CONFIG_LCD_CAM_ISR_IRAM_SAFE +#define CAMERA_ISR_IRAM_FLAG ESP_INTR_FLAG_IRAM +#define CAMERA_ISR_IRAM_ATTR IRAM_ATTR +#else +#define CAMERA_ISR_IRAM_FLAG 0 +#define CAMERA_ISR_IRAM_ATTR +#endif #define CAMERA_DBG_PIN_ENABLE 0 #if CAMERA_DBG_PIN_ENABLE @@ -104,6 +115,9 @@ typedef struct { uint8_t dma_num;//ESP32-S3 intr_handle_t dma_intr_handle;//ESP32-S3 +#if SOC_GDMA_SUPPORTED + gdma_channel_handle_t dma_channel_handle;//ESP32-S3 +#endif uint8_t jpeg_mode; uint8_t vsync_pin; From bae46be5eb126c46c692a29a953991117f68336b Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Thu, 21 Mar 2024 14:06:18 +0200 Subject: [PATCH 27/90] Update example to place frame buffer in PSRAM (#648) ESP32 failed to start the example --- examples/camera_example/main/take_picture.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/camera_example/main/take_picture.c b/examples/camera_example/main/take_picture.c index 3a243d2554..115b10f0ee 100644 --- a/examples/camera_example/main/take_picture.c +++ b/examples/camera_example/main/take_picture.c @@ -125,6 +125,7 @@ static camera_config_t camera_config = { .jpeg_quality = 12, //0-63, for OV series camera sensors, lower number means higher quality .fb_count = 1, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode. + .fb_location = CAMERA_FB_IN_PSRAM, .grab_mode = CAMERA_GRAB_WHEN_EMPTY, }; From 30aeeeed61e2b0182d673fe2cfedb1b7aa43b1b9 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Thu, 30 May 2024 21:32:49 +0300 Subject: [PATCH 28/90] fix(s2): Remove rx_pcm_bypass from registers The register is removed in ESP-IDF --- target/esp32s2/ll_cam.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/esp32s2/ll_cam.c b/target/esp32s2/ll_cam.c index 1c3fdb10e3..580c0355d6 100644 --- a/target/esp32s2/ll_cam.c +++ b/target/esp32s2/ll_cam.c @@ -178,8 +178,6 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config) 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; From 28296929286584d38e0a9e3456029204898a59a7 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 17 Jun 2024 11:18:36 +0300 Subject: [PATCH 29/90] Revert Uninstalls ISR service on cam deinit #516 Change is necessary in order to not stop other GPIO ISR uses that users might have in their code. This would cause an error to be emitted when driver is re-initialized, but it's dismissed in code, so the error will not cause any other change in behavior. cc @AxelLin @wouterdebie --- target/esp32/ll_cam.c | 1 - target/esp32s2/ll_cam.c | 1 - 2 files changed, 2 deletions(-) diff --git a/target/esp32/ll_cam.c b/target/esp32/ll_cam.c index 4c486444c8..73c979768b 100644 --- a/target/esp32/ll_cam.c +++ b/target/esp32/ll_cam.c @@ -257,7 +257,6 @@ esp_err_t ll_cam_deinit(cam_obj_t *cam) esp_intr_free(cam->cam_intr_handle); cam->cam_intr_handle = NULL; } - gpio_uninstall_isr_service(); return ESP_OK; } diff --git a/target/esp32s2/ll_cam.c b/target/esp32s2/ll_cam.c index 580c0355d6..b02116162d 100644 --- a/target/esp32s2/ll_cam.c +++ b/target/esp32s2/ll_cam.c @@ -95,7 +95,6 @@ esp_err_t ll_cam_deinit(cam_obj_t *cam) esp_intr_free(cam->cam_intr_handle); cam->cam_intr_handle = NULL; } - gpio_uninstall_isr_service(); return ESP_OK; } From 7aa37d4f22503fdac9ccd449e4678c4894c40055 Mon Sep 17 00:00:00 2001 From: Ichiro Maruta Date: Tue, 16 Jul 2024 21:47:46 +0900 Subject: [PATCH 30/90] Add option to customize JPEG mode frame size in menuconfig (#667) --- Kconfig | 27 +++++++++++++++++++++++++++ driver/cam_hal.c | 4 ++++ 2 files changed, 31 insertions(+) diff --git a/Kconfig b/Kconfig index a07ba19440..de706e0e89 100755 --- a/Kconfig +++ b/Kconfig @@ -179,6 +179,33 @@ menu "Camera configuration" Maximum value of DMA buffer Larger values may fail to allocate due to insufficient contiguous memory blocks, and smaller value may cause DMA interrupt to be too frequent. + choice CAMERA_JPEG_MODE_FRAME_SIZE_OPTION + prompt "JPEG mode frame size option" + default CAMERA_JPEG_MODE_FRAME_SIZE_AUTO + help + Select whether to use automatic calculation for JPEG mode frame size or specify a custom value. + + config CAMERA_JPEG_MODE_FRAME_SIZE_AUTO + bool "Use automatic calculation (width * height / 5)" + help + Use the default calculation for JPEG mode frame size. + Note: In very low resolutions like QQVGA, the default calculation tends to result in insufficient buffer size. + + config CAMERA_JPEG_MODE_FRAME_SIZE_CUSTOM + bool "Specify custom frame size" + help + Specify a custom frame size in bytes for JPEG mode. + + endchoice + + config CAMERA_JPEG_MODE_FRAME_SIZE + int "Custom JPEG mode frame size (bytes)" + default 8192 + depends on CAMERA_JPEG_MODE_FRAME_SIZE_CUSTOM + help + This option sets the custom frame size in JPEG mode. + Specify the desired buffer size in bytes. + config CAMERA_CONVERTER_ENABLED bool "Enable camera RGB/YUV converter" depends on IDF_TARGET_ESP32S3 diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 356e2cd72c..f5ca22e61d 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -374,7 +374,11 @@ esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint cam_obj->height = resolution[frame_size].height; if(cam_obj->jpeg_mode){ +#ifdef CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_AUTO cam_obj->recv_size = cam_obj->width * cam_obj->height / 5; +#else + cam_obj->recv_size = CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE; +#endif 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; From 8e1ec63c99ec0a3187dc7bc31bcef7ba9236794f Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 13 Sep 2024 13:10:59 +0200 Subject: [PATCH 31/90] Implement new idf-v5 I2C driver (#677) * moved sccb to new i2c driver * implemented val16 functions * address to dev_handle translation * moved new i2c driver to a seperate file * removed old idf version warning --- CMakeLists.txt | 10 +- driver/sccb-ng.c | 358 +++++++++++++++++++++++++++++++++++++++++++++++ driver/sccb.c | 1 + 3 files changed, 368 insertions(+), 1 deletion(-) create mode 100755 driver/sccb-ng.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a3c60fdfd6..9947ae802d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,6 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST list(APPEND srcs driver/esp_camera.c driver/cam_hal.c - driver/sccb.c driver/sensor.c sensors/ov2640.c sensors/ov3660.c @@ -82,6 +81,15 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST list(APPEND priv_requires esp_timer) endif() + # include the SCCB I2C driver + # this uses either the legacy I2C API or the newwer version from IDF v5.4 + # as this features a method to obtain the I2C driver from a port number + if (idf_version VERSION_GREATER_EQUAL "5.4") + list(APPEND srcs driver/sccb-ng.c) + else() + list(APPEND srcs driver/sccb.c) + endif() + endif() # CONFIG_ESP_ROM_HAS_JPEG_DECODE is available from IDF v4.4 but diff --git a/driver/sccb-ng.c b/driver/sccb-ng.c new file mode 100755 index 0000000000..0ea99659b3 --- /dev/null +++ b/driver/sccb-ng.c @@ -0,0 +1,358 @@ +/* + * This file is part of the OpenMV project. + * Copyright (c) 2013/2014 Ibrahim Abdelkader + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * SCCB (I2C like) driver with the new esp-idf I2C API. + * + */ +#include +#include +#include +#include +#include "sccb.h" +#include "sensor.h" +#include +#include "sdkconfig.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#else +#include "esp_log.h" +static const char *TAG = "sccb-ng"; +#endif + +#define LITTLETOBIG(x) ((x << 8) | (x >> 8)) + +#include "esp_private/i2c_platform.h" +#include "driver/i2c_master.h" +#include "driver/i2c_types.h" + +// support IDF 5.x +#ifndef portTICK_RATE_MS +#define portTICK_RATE_MS portTICK_PERIOD_MS +#endif + +#define TIMEOUT_MS 1000 /*!< I2C timeout duration */ +#define SCCB_FREQ CONFIG_SCCB_CLK_FREQ /*!< I2C master frequency */ +#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */ +#define READ_BIT I2C_MASTER_READ /*!< I2C master read */ +#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave */ +#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ +#define ACK_VAL 0x0 /*!< I2C ack value */ +#define NACK_VAL 0x1 /*!< I2C nack value */ +#if CONFIG_SCCB_HARDWARE_I2C_PORT1 +const int SCCB_I2C_PORT_DEFAULT = 1; +#else +const int SCCB_I2C_PORT_DEFAULT = 0; +#endif + +#define MAX_DEVICES UINT8_MAX-1 + +/* + The legacy I2C driver used addresses to differentiate between devices, whereas the new driver uses + i2c_master_dev_handle_t structs which are registed to the bus. + To avoid re-writing all camera dependant code, we simply translate the devices address to the corresponding + device_handle. This keeps all interfaces to the drivers identical. + To perform this conversion the following local struct is used. +*/ +typedef struct +{ + i2c_master_dev_handle_t dev_handle; + uint16_t address; +} device_t; + +static device_t devices[MAX_DEVICES]; +static uint8_t device_count = 0; +static int sccb_i2c_port; +static bool sccb_owns_i2c_port; + +i2c_master_dev_handle_t *get_handle_from_address(uint8_t slv_addr) +{ + for (uint8_t i = 0; i < device_count; i++) + { + + if (slv_addr == devices[i].address) + { + return &(devices[i].dev_handle); + } + } + + ESP_LOGE(TAG, "Device with address %02x not found", slv_addr); + return NULL; +} + +int SCCB_Install_Device(uint8_t slv_addr) +{ + esp_err_t ret; + i2c_master_bus_handle_t bus_handle; + + if (device_count > MAX_DEVICES) + { + ESP_LOGE(TAG, "cannot add more than %d devices", MAX_DEVICES); + return ESP_FAIL; + } + + ret = i2c_master_get_bus_handle(sccb_i2c_port, &bus_handle); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to get SCCB I2C Bus handle for port %d", sccb_i2c_port); + return ret; + } + + i2c_device_config_t dev_cfg = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = slv_addr, // not yet set + .scl_speed_hz = SCCB_FREQ, + }; + + ret = i2c_master_bus_add_device(bus_handle, &dev_cfg, &(devices[device_count].dev_handle)); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to install SCCB I2C device: %s", esp_err_to_name(ret)); + return -1; + } + + devices[device_count].address = slv_addr; + device_count++; + return 0; +} + +int SCCB_Init(int pin_sda, int pin_scl) +{ + ESP_LOGI(TAG, "pin_sda %d pin_scl %d", pin_sda, pin_scl); + // i2c_config_t conf; + esp_err_t ret; + + sccb_i2c_port = SCCB_I2C_PORT_DEFAULT; + sccb_owns_i2c_port = true; + ESP_LOGI(TAG, "sccb_i2c_port=%d", sccb_i2c_port); + + i2c_master_bus_config_t i2c_mst_config = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .i2c_port = SCCB_I2C_PORT_DEFAULT, + .scl_io_num = pin_scl, + .sda_io_num = pin_sda, + .glitch_ignore_cnt = 7, + .flags.enable_internal_pullup = 1}; + + i2c_master_bus_handle_t bus_handle; + ret = i2c_new_master_bus(&i2c_mst_config, &bus_handle); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to install SCCB I2C master bus on port %d: %s", sccb_i2c_port, esp_err_to_name(ret)); + return ret; + } + + return ESP_OK; +} + +int SCCB_Use_Port(int i2c_num) +{ // sccb use an already initialized I2C port + if (sccb_owns_i2c_port) + { + SCCB_Deinit(); + } + if (i2c_num < 0 || i2c_num > I2C_NUM_MAX) + { + return ESP_ERR_INVALID_ARG; + } + sccb_i2c_port = i2c_num; + + return ESP_OK; +} + +int SCCB_Deinit(void) +{ + esp_err_t ret; + + for (uint8_t i = 0; i < device_count; i++) + { + ret = i2c_master_bus_rm_device(devices[i].dev_handle); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to remove SCCB I2C Device"); + return ret; + } + + devices[i].dev_handle = NULL; + devices[i].address = 0; + } + device_count = 0; + + if (!sccb_owns_i2c_port) + { + return ESP_OK; + } + sccb_owns_i2c_port = false; + + i2c_master_bus_handle_t bus_handle; + ret = i2c_master_get_bus_handle(sccb_i2c_port, &bus_handle); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to get SCCB I2C Bus handle for port %d", sccb_i2c_port); + return ret; + } + + ret = i2c_del_master_bus(bus_handle); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to get delete SCCB I2C Master Bus at port %d", sccb_i2c_port); + return ret; + } + + return ESP_OK; +} + +uint8_t SCCB_Probe(void) +{ + uint8_t slave_addr = 0x0; + esp_err_t ret; + i2c_master_bus_handle_t bus_handle; + + ret = i2c_master_get_bus_handle(sccb_i2c_port, &bus_handle); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "failed to get SCCB I2C Bus handle for port %d", sccb_i2c_port); + return ret; + } + + 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; + + ret = i2c_master_probe(bus_handle, slave_addr, TIMEOUT_MS); + + if (ret == ESP_OK) + { + if (SCCB_Install_Device(slave_addr) != 0) + { + return 0; + } + return slave_addr; + } + } + return 0; +} + +uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint8_t tx_buffer[1]; + uint8_t rx_buffer[1]; + + tx_buffer[0] = reg; + + esp_err_t ret = i2c_master_transmit_receive(dev_handle, tx_buffer, 1, rx_buffer, 1, TIMEOUT_MS); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, rx_buffer[0], ret); + } + + return rx_buffer[0]; +} + +int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint8_t tx_buffer[2]; + tx_buffer[0] = reg; + tx_buffer[1] = data; + + esp_err_t ret = i2c_master_transmit(dev_handle, tx_buffer, 2, TIMEOUT_MS); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret); + } + + return ret == ESP_OK ? 0 : -1; +} + +uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint8_t rx_buffer[1]; + + uint16_t reg_htons = LITTLETOBIG(reg); + uint8_t *reg_u8 = (uint8_t *)®_htons; + + esp_err_t ret = i2c_master_transmit_receive(dev_handle, reg_u8, 2, rx_buffer, 1, TIMEOUT_MS); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, rx_buffer[0]); + } + + return rx_buffer[0]; +} + +int SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint16_t reg_htons = LITTLETOBIG(reg); + + uint8_t tx_buffer[3]; + tx_buffer[0] = reg_htons >> 8; + tx_buffer[1] = reg_htons & 0x00ff; + tx_buffer[2] = data; + + esp_err_t ret = i2c_master_transmit(dev_handle, tx_buffer, 3, TIMEOUT_MS); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data); + } + return ret == ESP_OK ? 0 : -1; +} + +uint16_t SCCB_Read_Addr16_Val16(uint8_t slv_addr, uint16_t reg) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint8_t rx_buffer[2]; + + uint16_t reg_htons = LITTLETOBIG(reg); + uint8_t *reg_u8 = (uint8_t *)®_htons; + + esp_err_t ret = i2c_master_transmit_receive(dev_handle, reg_u8, 2, rx_buffer, 2, TIMEOUT_MS); + uint16_t data = ((uint16_t)rx_buffer[0] << 8) | (uint16_t)rx_buffer[1]; + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data); + } + + return data; +} + +int SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data) +{ + i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); + + uint16_t reg_htons = LITTLETOBIG(reg); + + uint8_t tx_buffer[4]; + tx_buffer[0] = reg_htons >> 8; + tx_buffer[1] = reg_htons & 0x00ff; + tx_buffer[2] = data >> 8; + tx_buffer[3] = data & 0x00ff; + + esp_err_t ret = i2c_master_transmit(dev_handle, tx_buffer, 4, TIMEOUT_MS); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data); + } + return ret == ESP_OK ? 0 : -1; + return 0; +} diff --git a/driver/sccb.c b/driver/sccb.c index 307166d0fb..7007883c15 100755 --- a/driver/sccb.c +++ b/driver/sccb.c @@ -6,6 +6,7 @@ * SCCB (I2C like) driver. * */ + #include #include #include From c8a96e3570812ff03e2cc9e67406189aa30d8a9e Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Fri, 13 Sep 2024 14:19:55 +0300 Subject: [PATCH 32/90] Fix deprecation in IDF 5.4+ and update IDF build versions (#681) IDF v5.3 added and EOL IDF v4.4 removed --- .github/workflows/build.yml | 5 +---- target/esp32s3/ll_cam.c | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 90fc5dc2b4..5ecd283102 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,11 +11,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - idf_ver: ["release-v4.4", "release-v5.0", "release-v5.1", "release-v5.2", "latest"] + idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "latest"] idf_target: ["esp32", "esp32s2", "esp32s3", "esp32c2", "esp32c3"] - exclude: - - idf_ver: "release-v4.4" - idf_target: esp32c2 # ESP32C2 support started with version 5.0 container: espressif/idf:${{ matrix.idf_ver }} steps: - uses: actions/checkout@v1 diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index c61846999e..71caf14f69 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -202,7 +202,11 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam) gdma_channel_alloc_config_t rx_alloc_config = { .direction = GDMA_CHANNEL_DIRECTION_RX, }; +#if ((ESP_IDF_VERSION_MAJOR == 5 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 5) + esp_err_t ret = gdma_new_ahb_channel(&rx_alloc_config, &cam->dma_channel_handle); +#else esp_err_t ret = gdma_new_channel(&rx_alloc_config, &cam->dma_channel_handle); +#endif if (ret != ESP_OK) { cam_deinit(); ESP_LOGE(TAG, "Can't find available GDMA channel"); From 1a05a48e045ec82d58b6b4b7afbf4ccc49d10900 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 16 Sep 2024 09:29:55 +0300 Subject: [PATCH 33/90] Update upload_component.yml --- .github/workflows/upload_component.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index f550e696de..4f95e43a1b 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -11,7 +11,7 @@ jobs: with: submodules: "recursive" - name: Upload component to the component registry - uses: espressif/github-actions/upload_components@master + uses: espressif/upload-components-ci-action@v1 with: name: "esp32-camera" namespace: "espressif" From 4f577672515090da05a9761408d3270df13ab956 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 17 Sep 2024 11:40:38 +0200 Subject: [PATCH 34/90] fix: correct calloc call arguments order (#683) GCC 14 adds "calloc-transposed-args" warning which is an error at the default IDF build settings. --- driver/esp_camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/esp_camera.c b/driver/esp_camera.c index 4b7e906a57..e39b597fa1 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -153,7 +153,7 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out return ESP_ERR_INVALID_STATE; } - s_state = (camera_state_t *) calloc(sizeof(camera_state_t), 1); + s_state = (camera_state_t *) calloc(1, sizeof(camera_state_t)); if (!s_state) { return ESP_ERR_NO_MEM; } From 0054ab76046d2879ca9682edb2419b0613b06acd Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 30 Sep 2024 05:33:27 -0400 Subject: [PATCH 35/90] sccb-ng.c: correct address byte-swapping in Write16 routines (#690) --- driver/sccb-ng.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/driver/sccb-ng.c b/driver/sccb-ng.c index 0ea99659b3..9272c37c1b 100755 --- a/driver/sccb-ng.c +++ b/driver/sccb-ng.c @@ -34,12 +34,6 @@ static const char *TAG = "sccb-ng"; #define TIMEOUT_MS 1000 /*!< I2C timeout duration */ #define SCCB_FREQ CONFIG_SCCB_CLK_FREQ /*!< I2C master frequency */ -#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */ -#define READ_BIT I2C_MASTER_READ /*!< I2C master read */ -#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave */ -#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ -#define ACK_VAL 0x0 /*!< I2C ack value */ -#define NACK_VAL 0x1 /*!< I2C nack value */ #if CONFIG_SCCB_HARDWARE_I2C_PORT1 const int SCCB_I2C_PORT_DEFAULT = 1; #else @@ -299,11 +293,9 @@ int SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) { i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); - uint16_t reg_htons = LITTLETOBIG(reg); - uint8_t tx_buffer[3]; - tx_buffer[0] = reg_htons >> 8; - tx_buffer[1] = reg_htons & 0x00ff; + tx_buffer[0] = reg >> 8; + tx_buffer[1] = reg & 0x00ff; tx_buffer[2] = data; esp_err_t ret = i2c_master_transmit(dev_handle, tx_buffer, 3, TIMEOUT_MS); @@ -339,11 +331,9 @@ int SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data) { i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr)); - uint16_t reg_htons = LITTLETOBIG(reg); - uint8_t tx_buffer[4]; - tx_buffer[0] = reg_htons >> 8; - tx_buffer[1] = reg_htons & 0x00ff; + tx_buffer[0] = reg >> 8; + tx_buffer[1] = reg & 0x00ff; tx_buffer[2] = data >> 8; tx_buffer[3] = data & 0x00ff; @@ -354,5 +344,4 @@ int SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data) ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data); } return ret == ESP_OK ? 0 : -1; - return 0; } From b6a8297342ed728774036089f196d599f03ea367 Mon Sep 17 00:00:00 2001 From: Lee Jackson Date: Tue, 12 Nov 2024 18:01:28 +0800 Subject: [PATCH 36/90] Add mega ccm driver support Signed-off-by: Lee Jackson --- CMakeLists.txt | 1 + Kconfig | 7 + driver/esp_camera.c | 6 + driver/include/sensor.h | 6 + driver/sensor.c | 4 + examples/camera_example/main/CMakeLists.txt | 2 +- examples/camera_example/main/take_picture.c | 21 +- sensors/mega_ccm.c | 407 ++++++++++++++++++++ sensors/private_include/mega_ccm.h | 31 ++ sensors/private_include/mega_ccm_regs.h | 43 +++ sensors/private_include/mega_ccm_settings.h | 19 + 11 files changed, 545 insertions(+), 2 deletions(-) create mode 100644 sensors/mega_ccm.c create mode 100644 sensors/private_include/mega_ccm.h create mode 100644 sensors/private_include/mega_ccm_regs.h create mode 100644 sensors/private_include/mega_ccm_settings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9947ae802d..6ebc992074 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST sensors/sc101iot.c sensors/sc030iot.c sensors/sc031gs.c + sensors/mega_ccm.c ) list(APPEND priv_include_dirs diff --git a/Kconfig b/Kconfig index de706e0e89..2285e43b11 100755 --- a/Kconfig +++ b/Kconfig @@ -117,6 +117,13 @@ menu "Camera configuration" Enable this option if you want to use the SC031GS. Disable this option to save memory. + config MEGA_CCM_SUPPORT + bool "Support MEGA CCM 5MP" + default y + help + Enable this option if you want to use the MEGA CCM. + Disable this option to save memory. + choice SCCB_HARDWARE_I2C_PORT bool "I2C peripheral to use for SCCB" default SCCB_HARDWARE_I2C_PORT1 diff --git a/driver/esp_camera.c b/driver/esp_camera.c index e39b597fa1..7c693a6957 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -69,6 +69,9 @@ #if CONFIG_SC031GS_SUPPORT #include "sc031gs.h" #endif +#if CONFIG_MEGA_CCM_SUPPORT +#include "mega_ccm.h" +#endif #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" @@ -143,6 +146,9 @@ static const sensor_func_t g_sensors[] = { #if CONFIG_SC031GS_SUPPORT {sc031gs_detect, sc031gs_init}, #endif +#if CONFIG_MEGA_CCM_SUPPORT + {mega_ccm_detect, mega_ccm_init}, +#endif }; static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model) diff --git a/driver/include/sensor.h b/driver/include/sensor.h index 6ab12a21aa..f57e4e26a9 100755 --- a/driver/include/sensor.h +++ b/driver/include/sensor.h @@ -31,6 +31,7 @@ typedef enum { SC101IOT_PID = 0xda4a, SC030IOT_PID = 0x9a46, SC031GS_PID = 0x0031, + MEGA_CCM_PID =0x039E, } camera_pid_t; typedef enum { @@ -48,6 +49,7 @@ typedef enum { CAMERA_SC101IOT, CAMERA_SC030IOT, CAMERA_SC031GS, + CAMERA_MEGA_CCM, CAMERA_MODEL_MAX, CAMERA_NONE, } camera_model_t; @@ -67,6 +69,7 @@ typedef enum { SC101IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1 SC030IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1 SC031GS_SCCB_ADDR = 0x30, + MEGA_CCM_SCCB_ADDR = 0x1F, // 0x3E >> 1 } camera_sccb_addr_t; typedef enum { @@ -84,10 +87,12 @@ typedef enum { typedef enum { FRAMESIZE_96X96, // 96x96 FRAMESIZE_QQVGA, // 160x120 + FRAMESIZE_128X128, // 128x128 FRAMESIZE_QCIF, // 176x144 FRAMESIZE_HQVGA, // 240x176 FRAMESIZE_240X240, // 240x240 FRAMESIZE_QVGA, // 320x240 + FRAMESIZE_320X320, // 320x320 FRAMESIZE_CIF, // 400x296 FRAMESIZE_HVGA, // 480x320 FRAMESIZE_VGA, // 640x480 @@ -106,6 +111,7 @@ typedef enum { FRAMESIZE_WQXGA, // 2560x1600 FRAMESIZE_P_FHD, // 1080x1920 FRAMESIZE_QSXGA, // 2560x1920 + FRAMESIZE_5MP, // 2592x1944 FRAMESIZE_INVALID } framesize_t; diff --git a/driver/sensor.c b/driver/sensor.c index 23071f6c2c..e0acbfc26d 100644 --- a/driver/sensor.c +++ b/driver/sensor.c @@ -17,15 +17,18 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = { {CAMERA_SC101IOT, "SC101IOT", SC101IOT_SCCB_ADDR, SC101IOT_PID, FRAMESIZE_HD, false}, {CAMERA_SC030IOT, "SC030IOT", SC030IOT_SCCB_ADDR, SC030IOT_PID, FRAMESIZE_VGA, false}, {CAMERA_SC031GS, "SC031GS", SC031GS_SCCB_ADDR, SC031GS_PID, FRAMESIZE_VGA, false}, + {CAMERA_MEGA_CCM, "MEGA_CCM", MEGA_CCM_SCCB_ADDR, MEGA_CCM_PID, FRAMESIZE_5MP, true}, }; const resolution_info_t resolution[FRAMESIZE_INVALID] = { { 96, 96, ASPECT_RATIO_1X1 }, /* 96x96 */ + { 128, 128, ASPECT_RATIO_1X1 }, /* 128x128 */ { 160, 120, ASPECT_RATIO_4X3 }, /* QQVGA */ { 176, 144, ASPECT_RATIO_5X4 }, /* QCIF */ { 240, 176, ASPECT_RATIO_4X3 }, /* HQVGA */ { 240, 240, ASPECT_RATIO_1X1 }, /* 240x240 */ { 320, 240, ASPECT_RATIO_4X3 }, /* QVGA */ + { 320, 320, ASPECT_RATIO_1X1 }, /* 320x320 */ { 400, 296, ASPECT_RATIO_4X3 }, /* CIF */ { 480, 320, ASPECT_RATIO_3X2 }, /* HVGA */ { 640, 480, ASPECT_RATIO_4X3 }, /* VGA */ @@ -44,6 +47,7 @@ const resolution_info_t resolution[FRAMESIZE_INVALID] = { { 2560, 1600, ASPECT_RATIO_16X10 }, /* WQXGA */ { 1088, 1920, ASPECT_RATIO_9X16 }, /* Portrait FHD */ { 2560, 1920, ASPECT_RATIO_4X3 }, /* QSXGA */ + { 2592, 1944, ASPECT_RATIO_4X3 }, /* 5MP */ }; camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id) diff --git a/examples/camera_example/main/CMakeLists.txt b/examples/camera_example/main/CMakeLists.txt index 0811eaadb5..b3b4892124 100644 --- a/examples/camera_example/main/CMakeLists.txt +++ b/examples/camera_example/main/CMakeLists.txt @@ -1,3 +1,3 @@ idf_component_register(SRCS take_picture.c PRIV_INCLUDE_DIRS . - PRIV_REQUIRES nvs_flash) + PRIV_REQUIRES nvs_flash esp_psram) diff --git a/examples/camera_example/main/take_picture.c b/examples/camera_example/main/take_picture.c index 115b10f0ee..15147407e1 100644 --- a/examples/camera_example/main/take_picture.c +++ b/examples/camera_example/main/take_picture.c @@ -7,6 +7,7 @@ // 1. Board setup (Uncomment): // #define BOARD_WROVER_KIT // #define BOARD_ESP32CAM_AITHINKER +// #define BOARD_ESP32S3_WROOM /** * 2. Kconfig setup @@ -92,7 +93,25 @@ #define CAM_PIN_PCLK 22 #endif - +// ESP32S3 (WROOM) PIN Map +#ifdef BOARD_ESP32S3_WROOM +#define CAM_PIN_PWDN 38 +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_VSYNC 6 +#define CAM_PIN_HREF 7 +#define CAM_PIN_PCLK 13 +#define CAM_PIN_XCLK 15 +#define CAM_PIN_SIOD 4 +#define CAM_PIN_SIOC 5 +#define CAM_PIN_D0 11 +#define CAM_PIN_D1 9 +#define CAM_PIN_D2 8 +#define CAM_PIN_D3 10 +#define CAM_PIN_D4 12 +#define CAM_PIN_D5 18 +#define CAM_PIN_D6 17 +#define CAM_PIN_D7 16 +#endif static const char *TAG = "example:take_picture"; #if ESP_CAMERA_SUPPORTED diff --git a/sensors/mega_ccm.c b/sensors/mega_ccm.c new file mode 100644 index 0000000000..159c3922be --- /dev/null +++ b/sensors/mega_ccm.c @@ -0,0 +1,407 @@ +// 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 "mega_ccm.h" +#include "mega_ccm_regs.h" +#include "mega_ccm_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 = "mega_ccm"; +#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_Read16(slv_addr, reg); +#ifdef REG_DEBUG_ON + if (ret < 0) { + ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret); + } +#endif + return ret; +} + +static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value){ + int ret = 0; + ret = SCCB_Write16(slv_addr, reg, value); + return ret; +} + + +static int reset(sensor_t *sensor) +{ + int ret; + ret = write_reg(sensor->slv_addr, CAMERA_RST_REG, 0x00); + ret += write_reg(sensor->slv_addr, CAMERA_RST_REG, 0x01); + vTaskDelay(1000 / portTICK_PERIOD_MS); + return ret; +} + +static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) +{ + int ret = 0; + switch (pixformat) { + case PIXFORMAT_JPEG: + ret = write_reg(sensor->slv_addr, PIXEL_FMT_REG, 0x01); + break; + case PIXFORMAT_RGB565: + ret = write_reg(sensor->slv_addr, PIXEL_FMT_REG, 0x02); + break; + case PIXFORMAT_YUV422: + ret = write_reg(sensor->slv_addr, PIXEL_FMT_REG, 0x03); + 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_5MP) { + ESP_LOGW(TAG, "Invalid framesize: %u", framesize); + framesize = FRAMESIZE_5MP; + } + sensor->status.framesize = framesize; + uint16_t w = resolution[framesize].width; + uint16_t h = resolution[framesize].height; + switch (framesize){ + case FRAMESIZE_QVGA: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x01); //320x240 + ret += write_reg(sensor->slv_addr, SYSTEM_CLK_DIV_REG, 0x02); // set system clk + ret += write_reg(sensor->slv_addr, SYSTEM_PLL_DIV_REG, 0x01); // set system pll + break; + case FRAMESIZE_VGA: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x02); //640x480 + ret += write_reg(sensor->slv_addr, SYSTEM_CLK_DIV_REG, 0x02); // set system clk + ret += write_reg(sensor->slv_addr, SYSTEM_PLL_DIV_REG, 0x01); // set system pll + break; + case FRAMESIZE_HD: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x03); //1280x720 + ret += write_reg(sensor->slv_addr, SYSTEM_CLK_DIV_REG, 0x02); // set system clk + ret += write_reg(sensor->slv_addr, SYSTEM_PLL_DIV_REG, 0x01); // set system pll + break; + case FRAMESIZE_UXGA: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x04); //1600x1200 + ret += write_reg(sensor->slv_addr, SYSTEM_CLK_DIV_REG, 0x02); // set system clk + ret += write_reg(sensor->slv_addr, SYSTEM_PLL_DIV_REG, 0x01); // set system pll + break; + case FRAMESIZE_FHD: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x05); //1920x1080 + ret += write_reg(sensor->slv_addr, SYSTEM_CLK_DIV_REG, 0x02); // set system clk + ret += write_reg(sensor->slv_addr, SYSTEM_PLL_DIV_REG, 0x01); // set system pll + break; + case FRAMESIZE_5MP: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x06); //2592x1944 + break; + case FRAMESIZE_96X96: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x07); //96x96 + break; + case FRAMESIZE_128X128: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x08); //128x128 + break; + case FRAMESIZE_320X320: + ret = write_reg(sensor->slv_addr, RESOLUTION_REG, 0x09); //320x320 + break; + default: + ESP_LOGW(TAG, "unsupport framesize"); + ret = -1; + break; + } + 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, IMAGE_MIRROR_REG, 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, IMAGE_FLIP_REG, enable); + if (ret == 0) { + ESP_LOGD(TAG, "Set v-flip to: %d", enable); + } + return ret; +} +static int set_quality(sensor_t *sensor, int qs) +{ + int ret = 0; + ret = write_reg(sensor->slv_addr, IMAGE_QUALITY_REG, qs); + if (ret == 0) { + sensor->status.quality = qs; + ESP_LOGD(TAG, "Set quality to: %d", qs); + } + return ret; +} + + +static int set_brightness(sensor_t *sensor, int level) +{ + int ret = 0; + if(level < 0) { + level = 0; + } else if(level > 8) { + level = 8; + } + ret = write_reg(sensor->slv_addr, BRIGHTNESS_REG, level); + if (ret == 0) { + ESP_LOGD(TAG, "Set brightness to: %d", level); + sensor->status.brightness = level; + } + return ret; +} + +static int set_contrast (sensor_t *sensor, int level) +{ + int ret = 0; + if(level < 0) { + level = 0; + } else if(level > 6) { + level = 6; + } + ret = write_reg(sensor->slv_addr, CONTRAST_REG, level); + if (ret == 0) { + ESP_LOGD(TAG, "Set contrast to: %d", level); + sensor->status.contrast = level; + } + return ret; +} + +static int set_saturation (sensor_t *sensor, int level) +{ + int ret = 0; + if(level < 0) { + level = 0; + } else if(level > 6) { + level = 6; + } + ret = write_reg(sensor->slv_addr, SATURATION_REG, level); + if (ret == 0) { + ESP_LOGD(TAG, "Set saturation to: %d", level); + sensor->status.saturation = level; + } + return ret; +} +static int set_agc_mode (sensor_t *sensor, int enable) +{ + int ret = 0; + ret = write_reg(sensor->slv_addr, AGC_MODE_REG, enable); + if (ret == 0) { + ESP_LOGD(TAG, "Set agc mode to: %d", enable); + sensor->status.aec = enable; + } + return ret; +} + + +static int set_wb_mode (sensor_t *sensor, int mode) +{ + int ret = 0; + if(mode < 0) { + mode = 0; + } else if(mode > 5) { + mode = 5; + } + ret = write_reg(sensor->slv_addr, AWB_MODE_REG, mode); + if (ret == 0) { + ESP_LOGD(TAG, "Set wb_mode to: %d", mode); + sensor->status.wb_mode = mode; + } + return ret; + +} + +static int set_special_effect (sensor_t *sensor, int effect) +{ + + int ret = 0; + if(effect < 0) { + effect = 0; + } else if(effect > 6) { + effect = 6; + } + ret = write_reg(sensor->slv_addr, SPECIAL_REG, effect); + if (ret == 0) { + ESP_LOGD(TAG, "Set special_effect to: %d", effect); + sensor->status.special_effect = effect; + } + return ret; + +} + + +static int analog_gain (sensor_t *sensor, int val) +{ + + int ret = 0; + ret = write_reg(sensor->slv_addr, MANUAL_AGC_REG, val); + if (ret == 0) { + ESP_LOGD(TAG, "Set analog gain to: %d", val); + } + return ret; + +} + + +static int exposure_line (sensor_t *sensor, int val) +{ + + int ret = 0; + ret = write_reg(sensor->slv_addr, MANUAL_EXP_H_REG, val>>8); + ret += write_reg(sensor->slv_addr, MANUAL_EXP_L_REG, val>>8); + if (ret == 0) { + ESP_LOGD(TAG, "Set exposure_line to: %d", val); + } + return ret; + +} + + +static int init_status(sensor_t *sensor) +{ + 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 = 0; + sensor->status.vflip = 0; + 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 mega_ccm_detect(int slv_addr, sensor_id_t *id) +{ + if (MEGA_CCM_SCCB_ADDR == slv_addr) { + uint8_t h = read_reg(slv_addr, SENSOR_ID_HIGH); + uint8_t l = read_reg(slv_addr, SENSOR_ID_LOW); + uint16_t PID = (h<<8) | l; + if (MEGA_CCM_PID == PID) { + id->PID = PID; + return PID; + } else { + ESP_LOGI(TAG, "Mismatch PID=0x%x", PID); + } + } + return 0; +} + +int mega_ccm_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_brightness; + sensor->set_saturation = set_saturation; + sensor->set_sharpness = set_dummy; + sensor->set_denoise = set_dummy; + sensor->set_gainceiling = set_gainceiling_dummy; + sensor->set_quality = set_quality; + sensor->set_colorbar = set_dummy; + 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_agc_mode; + sensor->set_awb_gain = set_dummy; + sensor->set_agc_gain = analog_gain; + sensor->set_aec_value = exposure_line; + + sensor->set_special_effect = set_special_effect; + sensor->set_wb_mode = set_wb_mode; + 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 = NULL; + sensor->set_reg = NULL; + sensor->set_res_raw = NULL; + sensor->set_pll = NULL; + sensor->set_xclk = NULL; + + ESP_LOGD(TAG, "MEGA_CCM Attached"); + return 0; +} diff --git a/sensors/private_include/mega_ccm.h b/sensors/private_include/mega_ccm.h new file mode 100644 index 0000000000..bbad247b53 --- /dev/null +++ b/sensors/private_include/mega_ccm.h @@ -0,0 +1,31 @@ +/* + * + * MEGA_CCM driver. + * + */ +#ifndef __MEGA_CCM_H__ +#define __MEGA_CCM_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 mega_ccm_detect(int slv_addr, sensor_id_t *id); + +/** + * @brief initialize sensor function pointers + * + * @param sensor pointer of sensor + * @return + * Always 0 + */ +int mega_ccm_init(sensor_t *sensor); + +#endif // __MEGA_CCM_H__ diff --git a/sensors/private_include/mega_ccm_regs.h b/sensors/private_include/mega_ccm_regs.h new file mode 100644 index 0000000000..dbd51dd274 --- /dev/null +++ b/sensors/private_include/mega_ccm_regs.h @@ -0,0 +1,43 @@ +/* + * MEGA_CCM register definitions. + */ +#ifndef __MEGA_CCM_REG_REGS_H__ +#define __MEGA_CCM_REG_REGS_H__ + +#define ID_BASE 0x0000 +#define SENSOR_BASE 0x0100 +#define SYS_CLK_BASE 0x0200 +#define BYPASS_BASE 0XFFF0 + +#define SENSOR_ID_HIGH ID_BASE | 0x00 +#define SENSOR_ID_LOW ID_BASE | 0x01 +#define FIRMWARE_VER ID_BASE | 0x02 + +#define CAMERA_RST_REG SENSOR_BASE|0x02 + + +#define PIXEL_FMT_REG SENSOR_BASE|0x20 +#define RESOLUTION_REG SENSOR_BASE|0x21 +#define BRIGHTNESS_REG SENSOR_BASE|0x22 +#define CONTRAST_REG SENSOR_BASE|0x23 +#define SATURATION_REG SENSOR_BASE|0x24 +#define EXP_COMPENSATE_REG SENSOR_BASE|0x25 +#define AWB_MODE_REG SENSOR_BASE|0x26 +#define SPECIAL_REG SENSOR_BASE|0x27 +#define SHARPNESS_REG SENSOR_BASE|0x28 +#define FOCUS_REG SENSOR_BASE|0x29 +#define IMAGE_QUALITY_REG SENSOR_BASE|0x2A +#define IMAGE_FLIP_REG SENSOR_BASE|0x2B +#define IMAGE_MIRROR_REG SENSOR_BASE|0x2C + + +#define AGC_MODE_REG SENSOR_BASE|0x30 +#define MANUAL_AGC_REG SENSOR_BASE|0x31 +#define MANUAL_EXP_H_REG SENSOR_BASE|0x33 +#define MANUAL_EXP_L_REG SENSOR_BASE|0x34 + + +#define SYSTEM_CLK_DIV_REG SYS_CLK_BASE|0x00 +#define SYSTEM_PLL_DIV_REG SYS_CLK_BASE|0x01 + +#endif //__MEGA_CCM_REG_REGS_H__ diff --git a/sensors/private_include/mega_ccm_settings.h b/sensors/private_include/mega_ccm_settings.h new file mode 100644 index 0000000000..ff8e1866d6 --- /dev/null +++ b/sensors/private_include/mega_ccm_settings.h @@ -0,0 +1,19 @@ +#ifndef _GC032A_SETTINGS_H_ +#define _GC032A_SETTINGS_H_ + +#include +#include +#include "esp_attr.h" +#include "mega_ccm_regs.h" + + +#define REG_DLY 0xffff +#define REGLIST_TAIL 0x0000 + +static const uint16_t mega_ccm_default_regs[][2] = { + {0x0120, 0x01 }, // JPEG + {0x0121, 0x01 }, // 320X240 + {REGLIST_TAIL, 0x00}, +}; + +#endif From d9ee2114c92e189fa446634118b9050ca0e53b08 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Fri, 15 Nov 2024 08:43:49 +0100 Subject: [PATCH 37/90] fix(xclk): Zero init ledc_channel_config_t for forward compatibility LEDC driver was extended in https://github.com/espressif/esp-idf/commit/4a90deb2278322e6c605484770caafebfbbc615f with new config structure members. We must zero init the config struct to get default behavior. --- target/xclk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/xclk.c b/target/xclk.c index cb67b677a5..7ee155b1fe 100755 --- a/target/xclk.c +++ b/target/xclk.c @@ -47,7 +47,7 @@ esp_err_t camera_enable_out_clock(const camera_config_t* config) } g_ledc_channel = config->ledc_channel; - ledc_channel_config_t ch_conf; + ledc_channel_config_t ch_conf = {0}; ch_conf.gpio_num = config->pin_xclk; ch_conf.speed_mode = LEDC_LOW_SPEED_MODE; ch_conf.channel = config->ledc_channel; From 263af74861b55b3039b3ac8b8dd9ad867b063773 Mon Sep 17 00:00:00 2001 From: Christopher Nadler Date: Sat, 16 Nov 2024 05:44:49 +0100 Subject: [PATCH 38/90] Fixes missing return error code validation during camera initialization --- driver/cam_hal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index f5ca22e61d..86b430aa72 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -362,7 +362,8 @@ esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint 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_CHECK_GOTO(ret == ESP_OK, "ll_cam_set_sample_mode failed", err); + cam_obj->jpeg_mode = config->pixel_format == PIXFORMAT_JPEG; #if CONFIG_IDF_TARGET_ESP32 cam_obj->psram_mode = false; From e61e9da470cffe605fa7123752930416e77b9504 Mon Sep 17 00:00:00 2001 From: J Date: Tue, 19 Nov 2024 00:07:13 -0500 Subject: [PATCH 39/90] Remove instruction to call camera_probe This function is not public. esp_camera_init actually calls camera_probe so the documentation is backward. --- driver/include/esp_camera.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/driver/include/esp_camera.h b/driver/include/esp_camera.h index ce031c88d4..ff459cbd65 100755 --- a/driver/include/esp_camera.h +++ b/driver/include/esp_camera.h @@ -178,8 +178,6 @@ typedef struct { /** * @brief Initialize the camera driver * - * @note call camera_probe before calling this function - * * This function detects and configures camera over I2C interface, * allocates framebuffer and DMA buffers, * initializes parallel I2S input, and sets up DMA descriptors. From 6a821a898651d9437f4794a8a2d76d7d0f283d2b Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 6 Jan 2025 11:51:39 +0200 Subject: [PATCH 40/90] Fix 128x128 resolution index Framesize enum was not matching the resolution index for 128x128 --- driver/sensor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/sensor.c b/driver/sensor.c index e0acbfc26d..c46ccb2207 100644 --- a/driver/sensor.c +++ b/driver/sensor.c @@ -22,8 +22,8 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = { const resolution_info_t resolution[FRAMESIZE_INVALID] = { { 96, 96, ASPECT_RATIO_1X1 }, /* 96x96 */ - { 128, 128, ASPECT_RATIO_1X1 }, /* 128x128 */ { 160, 120, ASPECT_RATIO_4X3 }, /* QQVGA */ + { 128, 128, ASPECT_RATIO_1X1 }, /* 128x128 */ { 176, 144, ASPECT_RATIO_5X4 }, /* QCIF */ { 240, 176, ASPECT_RATIO_4X3 }, /* HQVGA */ { 240, 240, ASPECT_RATIO_1X1 }, /* 240x240 */ From aa15c51c83949c1f7e8050a2718b139cb8fa6678 Mon Sep 17 00:00:00 2001 From: brendan Date: Sat, 11 Jan 2025 19:24:43 +0800 Subject: [PATCH 41/90] Add error handling for esp_jpg_decode esp_jpg_decode does not return if writer fails, causing unhandled exceptions. Also logs when rgb_write malloc fails. --- conversions/esp_jpg_decode.c | 10 ++++++++-- conversions/to_bmp.c | 8 ++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/conversions/esp_jpg_decode.c b/conversions/esp_jpg_decode.c index 168325baf5..cd9a3558bd 100644 --- a/conversions/esp_jpg_decode.c +++ b/conversions/esp_jpg_decode.c @@ -114,11 +114,17 @@ esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jp uint16_t output_height = decoder.height / (1 << (uint8_t)(jpeg.scale)); //output start - writer(arg, 0, 0, output_width, output_height, NULL); + if (!writer(arg, 0, 0, output_width, output_height, NULL)) { + ESP_LOGE(TAG, "JPG Writer Start Failed!"); + return ESP_FAIL; + } //output write jres = jd_decomp(&decoder, _jpg_write, (uint8_t)jpeg.scale); //output end - writer(arg, output_width, output_height, output_width, output_height, NULL); + if (!writer(arg, output_width, output_height, output_width, output_height, NULL)) { + ESP_LOGE(TAG, "JPG Writer End Failed!"); + return ESP_FAIL; + } if (jres != JDR_OK) { ESP_LOGE(TAG, "JPG Decompression Failed! %s", jd_errors[jres]); diff --git a/conversions/to_bmp.c b/conversions/to_bmp.c index 27072f7113..9f016aa97d 100644 --- a/conversions/to_bmp.c +++ b/conversions/to_bmp.c @@ -78,8 +78,10 @@ static bool _rgb_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t jpeg->height = h; //if output is null, this is BMP if(!jpeg->output){ - jpeg->output = (uint8_t *)_malloc((w*h*3)+jpeg->data_offset); + size_t out_size = (w*h*3)+jpeg->data_offset; + jpeg->output = (uint8_t *)_malloc(out_size); if(!jpeg->output){ + ESP_LOGE(TAG, "_malloc failed! %zu", out_size); return false; } } @@ -121,8 +123,10 @@ static bool _rgb565_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16 jpeg->height = h; //if output is null, this is BMP if(!jpeg->output){ - jpeg->output = (uint8_t *)_malloc((w*h*3)+jpeg->data_offset); + size_t out_size = (w*h*3)+jpeg->data_offset; + jpeg->output = (uint8_t *)_malloc(out_size); if(!jpeg->output){ + ESP_LOGE(TAG, "_malloc failed! %zu", out_size); return false; } } From 8aad5f789005ef086bfc88e6a24f853ab05f2220 Mon Sep 17 00:00:00 2001 From: LynnL4 Date: Tue, 5 Nov 2024 09:34:43 +0800 Subject: [PATCH 42/90] Add support for HM1055 --- CMakeLists.txt | 1 + Kconfig | 9 + driver/esp_camera.c | 6 + driver/include/sensor.h | 3 + driver/sensor.c | 1 + sensors/hm1055.c | 833 ++++++++++++++++++++++ sensors/private_include/hm1055.h | 27 + sensors/private_include/hm1055_regs.h | 120 ++++ sensors/private_include/hm1055_settings.h | 697 ++++++++++++++++++ 9 files changed, 1697 insertions(+) create mode 100755 sensors/hm1055.c create mode 100755 sensors/private_include/hm1055.h create mode 100755 sensors/private_include/hm1055_regs.h create mode 100755 sensors/private_include/hm1055_settings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ebc992074..493e4c5a9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST sensors/sc030iot.c sensors/sc031gs.c sensors/mega_ccm.c + sensors/hm1055.c ) list(APPEND priv_include_dirs diff --git a/Kconfig b/Kconfig index 2285e43b11..991cbc38f2 100755 --- a/Kconfig +++ b/Kconfig @@ -116,6 +116,15 @@ menu "Camera configuration" SC031GS is a global shutter CMOS sensor with high frame rate and single-frame HDR. Enable this option if you want to use the SC031GS. Disable this option to save memory. + + config HM1055_SUPPORT + bool "Support HM1055 VGA" + default y + help + Enable this option if you want to use the HM1055. + Disable this option to save memory. + + config SCCB_I2C_PORT config MEGA_CCM_SUPPORT bool "Support MEGA CCM 5MP" diff --git a/driver/esp_camera.c b/driver/esp_camera.c index 7c693a6957..8985cee77d 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -72,6 +72,9 @@ #if CONFIG_MEGA_CCM_SUPPORT #include "mega_ccm.h" #endif +#if CONFIG_HM1055_SUPPORT +#include "hm1055.h" +#endif #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" @@ -149,6 +152,9 @@ static const sensor_func_t g_sensors[] = { #if CONFIG_MEGA_CCM_SUPPORT {mega_ccm_detect, mega_ccm_init}, #endif +#if CONFIG_HM1055_SUPPORT + {hm1055_detect, hm1055_init}, +#endif }; static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model) diff --git a/driver/include/sensor.h b/driver/include/sensor.h index f57e4e26a9..354a8c0dfd 100755 --- a/driver/include/sensor.h +++ b/driver/include/sensor.h @@ -32,6 +32,7 @@ typedef enum { SC030IOT_PID = 0x9a46, SC031GS_PID = 0x0031, MEGA_CCM_PID =0x039E, + HM1055_PID = 0x0955, } camera_pid_t; typedef enum { @@ -50,6 +51,7 @@ typedef enum { CAMERA_SC030IOT, CAMERA_SC031GS, CAMERA_MEGA_CCM, + CAMERA_HM1055, CAMERA_MODEL_MAX, CAMERA_NONE, } camera_model_t; @@ -70,6 +72,7 @@ typedef enum { SC030IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1 SC031GS_SCCB_ADDR = 0x30, MEGA_CCM_SCCB_ADDR = 0x1F, // 0x3E >> 1 + HM1055_SCCB_ADDR = 0x24, } camera_sccb_addr_t; typedef enum { diff --git a/driver/sensor.c b/driver/sensor.c index c46ccb2207..847d18b01d 100644 --- a/driver/sensor.c +++ b/driver/sensor.c @@ -18,6 +18,7 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = { {CAMERA_SC030IOT, "SC030IOT", SC030IOT_SCCB_ADDR, SC030IOT_PID, FRAMESIZE_VGA, false}, {CAMERA_SC031GS, "SC031GS", SC031GS_SCCB_ADDR, SC031GS_PID, FRAMESIZE_VGA, false}, {CAMERA_MEGA_CCM, "MEGA_CCM", MEGA_CCM_SCCB_ADDR, MEGA_CCM_PID, FRAMESIZE_5MP, true}, + {CAMERA_HM1055, "HM1055", HM1055_SCCB_ADDR, HM1055_PID, FRAMESIZE_HD, false}, }; const resolution_info_t resolution[FRAMESIZE_INVALID] = { diff --git a/sensors/hm1055.c b/sensors/hm1055.c new file mode 100755 index 0000000000..d3318a7f30 --- /dev/null +++ b/sensors/hm1055.c @@ -0,0 +1,833 @@ +/* + * + * HM1055 driver. + * + */ +#include +#include +#include +#include "sccb.h" +#include "xclk.h" +#include "hm1055.h" +#include "hm1055_regs.h" +#include "hm1055_settings.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#else +#include "esp_log.h" +static const char *TAG = "HM1055"; +#endif + +// #define REG_DEBUG_ON + +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); + +static int read_reg(uint8_t slv_addr, const uint16_t reg) +{ + int ret = SCCB_Read16(slv_addr, reg); +#ifdef REG_DEBUG_ON + if (ret < 0) + { + ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret); + } +#endif + return ret; +} + +static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask) +{ + return (read_reg(slv_addr, reg) & mask) == mask; +} + +static int read_reg16(uint8_t slv_addr, const uint16_t reg) +{ + int ret = 0, ret2 = 0; + ret = read_reg(slv_addr, reg); + if (ret >= 0) + { + ret = (ret & 0xFF) << 8; + ret2 = read_reg(slv_addr, reg + 1); + if (ret2 < 0) + { + ret = ret2; + } + else + { + ret |= ret2 & 0xFF; + } + } + return ret; +} + +static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value) +{ + int ret = 0; +#ifndef REG_DEBUG_ON + ret = SCCB_Write16(slv_addr, reg, value); +#else + int old_value = read_reg(slv_addr, reg); + if (old_value < 0) + { + return old_value; + } + if ((uint8_t)old_value != value) + { + ESP_LOGD(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value); + ret = SCCB_Write16(slv_addr, reg, value); + } + else + { + ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value); + ret = SCCB_Write16(slv_addr, reg, value); // maybe not? + } + if (ret < 0) + { + ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret); + } +#endif + return ret; +} + +static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value) +{ + int ret = 0; + uint8_t c_value, new_value; + ret = read_reg(slv_addr, reg); + if (ret < 0) + { + return ret; + } + c_value = ret; + new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset); + ret = write_reg(slv_addr, reg, new_value); + return ret; +} + +static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2]) +{ + int i = 0, ret = 0; + while (!ret && regs[i][0] != REGLIST_TAIL) + { + if (regs[i][0] == REG_DLY) + { + vTaskDelay(regs[i][1] / portTICK_PERIOD_MS); + } + else + { + ret = write_reg(slv_addr, regs[i][0], regs[i][1]); + } + i++; + } + return ret; +} + +static int write_reg16(uint8_t slv_addr, const uint16_t reg, uint16_t value) +{ + if (write_reg(slv_addr, reg, value >> 8) || write_reg(slv_addr, reg + 1, value)) + { + return -1; + } + return 0; +} + +static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value, uint16_t y_value) +{ + if (write_reg16(slv_addr, reg, x_value) || write_reg16(slv_addr, reg + 2, y_value)) + { + return -1; + } + return 0; +} + +#define write_reg_bits(slv_addr, reg, mask, enable) set_reg_bits(slv_addr, reg, 0, mask, (enable) ? (mask) : 0) + +static int set_ae_level(sensor_t *sensor, int level); + +static int reset(sensor_t *sensor) +{ + vTaskDelay(100 / portTICK_PERIOD_MS); + int ret = 0; + // Software Reset: clear all registers and reset them to their default values + ret = write_reg(sensor->slv_addr, SFTRST, 0x55); + if (ret) + { + ESP_LOGE(TAG, "Software Reset FAILED!"); + return ret; + } + vTaskDelay(100 / portTICK_PERIOD_MS); + ret = write_regs(sensor->slv_addr, sensor_default_regs); + if (ret == 0) + { + ESP_LOGD(TAG, "Camera defaults loaded"); + vTaskDelay(100 / portTICK_PERIOD_MS); + set_ae_level(sensor, 0); + } + return ret; +} + +static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) +{ + int ret = 0; + + switch (pixformat) + { + case PIXFORMAT_RAW: + ret = write_reg(sensor->slv_addr, PORTCTRL, 0x20); + break; + case PIXFORMAT_YUV422: + ret = write_reg(sensor->slv_addr, PORTCTRL, 0x30); + break; + case PIXFORMAT_RGB565: + case PIXFORMAT_RGB888: + ret = write_reg(sensor->slv_addr, PORTCTRL, 0x40); + break; + case PIXFORMAT_RGB555: + ret = write_reg(sensor->slv_addr, PORTCTRL, 0x50); + break; + case PIXFORMAT_RGB444: + ret = write_reg(sensor->slv_addr, PORTCTRL, 0x60); + break; + default: + break; + } + + if (ret == 0) + { + sensor->pixformat = pixformat; + ESP_LOGD(TAG, "Set pixformat: %d", pixformat); + } + + return ret; +} + +static int set_framesize(sensor_t *sensor, framesize_t framesize) +{ + int ret = 0; + + sensor->status.framesize = framesize; + ESP_LOGD(TAG, "Set framesize: %d", framesize); + ret = write_regs(sensor->slv_addr, sensor_default_regs); + if (framesize == FRAMESIZE_QQVGA) + { + ESP_LOGD(TAG, "Set FRAMESIZE_QQVGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QQVGA); + } + else if (framesize == FRAMESIZE_QCIF) + { + ESP_LOGD(TAG, "Set FRAMESIZE_QCIF"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QCIF); + } + else if (framesize == FRAMESIZE_240X240) + { + ESP_LOGD(TAG, "Set FRAMESIZE_240X240"); + ret = write_regs(sensor->slv_addr, sensor_framesize_240X240); + } + else if (framesize == FRAMESIZE_QVGA) + { + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA); + } + else if (framesize == FRAMESIZE_CIF) + { + ESP_LOGD(TAG, "Set FRAMESIZE_CIF"); + ret = write_regs(sensor->slv_addr, sensor_framesize_CIF); + } + else if (framesize == FRAMESIZE_VGA) + { + ESP_LOGD(TAG, "Set FRAMESIZE_VGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA); + } + else if (framesize == FRAMESIZE_SVGA) + { + ESP_LOGD(TAG, "Set FRAMESIZE_SVGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_SVGA); + } + else if (framesize == FRAMESIZE_HD) + { + ESP_LOGD(TAG, "Set FRAMESIZE_HD"); + ret = write_regs(sensor->slv_addr, sensor_framesize_HD); + ret = _set_pll(sensor, 0, 288, 1, 0, 0, 0, 1, 16); + } + else + { + ESP_LOGD(TAG, "Dont suppost this size, Set FRAMESIZE_VGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA); + } + + if (ret == 0) + { + ret = write_reg(sensor->slv_addr, CMU, 0x01) || write_reg(sensor->slv_addr, TGRDCFG, 0x01); + } + + return ret; +} + +static int set_hmirror(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = write_reg_bits(sensor->slv_addr, RDCFG, 0x02, enable); + if (ret == 0) + { + ESP_LOGD(TAG, "Set hmirror to: %d", enable); + sensor->status.hmirror = enable; + } + return ret; +} + +static int set_vflip(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = write_reg_bits(sensor->slv_addr, RDCFG, 0x01, enable); + if (ret == 0) + { + ESP_LOGD(TAG, "Set vflip to: %d", enable); + sensor->status.vflip = enable; + } + return ret; +} + +static int set_quality(sensor_t *sensor, int qs) +{ + return 0; +} + +static int set_colorbar(sensor_t *sensor, int enable) +{ + return 0; +} + +static int set_gain_ctrl(sensor_t *sensor, int enable) +{ + return 0; +} + +static int set_exposure_ctrl(sensor_t *sensor, int enable) +{ + int ret = 0; + + ret = write_reg_bits(sensor->slv_addr, AEWBCFG, 0x01, enable); + + if (ret == 0) + { + ESP_LOGD(TAG, "Set aec to: %d", enable); + sensor->status.aec = enable; + } + + return ret; +} + +static int set_whitebal(sensor_t *sensor, int enable) +{ + int ret = 0; + + ret = write_reg_bits(sensor->slv_addr, AEWBCFG, 0x02, enable); + + if (ret == 0) + { + ESP_LOGD(TAG, "Set awb to: %d", enable); + sensor->status.awb = enable; + } + return ret; +} + +// Gamma enable +static int set_raw_gma_dsp(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = write_reg_bits(sensor->slv_addr, ISPCTRL1, 0x04, enable); + if (ret == 0) + { + ESP_LOGD(TAG, "Set raw_gma to: %d", enable); + sensor->status.raw_gma = enable; + } + return 0; +} + +static int set_lenc_dsp(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = write_reg_bits(sensor->slv_addr, ISPCTRL3, 0x40, enable); + if (ret == 0) + { + ESP_LOGD(TAG, "Set lenc to: %d", enable); + sensor->status.lenc = enable; + } + return -1; +} +// real gain +static int set_agc_gain(sensor_t *sensor, int gain) +{ + int ret = 0; + if (gain < 0 || gain > 7) + { + return -1; + } + + ret = write_reg(sensor->slv_addr, AGAIN, gain); + if (ret == 0) + { + ESP_LOGD(TAG, "Set gain to: %d", gain); + sensor->status.agc_gain = gain; + } + return 0; +} +static int set_aec_value(sensor_t *sensor, int value) +{ + int ret = 0; + ret = write_reg(sensor->slv_addr, AETARGM, value); + if (ret == 0) + { + ESP_LOGD(TAG, "Set aec_value to: %d", value); + sensor->status.aec_value = value; + } + + return 0; +} + +static int set_ae_level(sensor_t *sensor, int level) +{ + int ret = 0; + if (level < -5 || level > 5) + { + return -1; + } + uint8_t target_level = ((level + 5) * 10) + 5; + uint8_t upper = target_level * 27 / 25; + uint8_t lower = target_level * 23 / 25; + + ret = write_reg(sensor->slv_addr, AETARGU, upper) || write_reg(sensor->slv_addr, AETARGL, lower); + if (ret == 0) + { + ESP_LOGD(TAG, "Set ae_level to: %d", level); + sensor->status.ae_level = level; + } + return 0; +} + +static int set_awb_gain_dsp(sensor_t *sensor, int enable) +{ + return 0; +} + +static int set_brightness(sensor_t *sensor, int level) +{ + int ret = 0; + uint8_t ispctrl5 = read_reg(sensor->slv_addr, ISPCTRL5); + uint8_t brightness = 0; + + switch (level) + { + case 3: + brightness = 0xFF; + break; + + case 2: + brightness = 0xBA; + break; + + case 1: + brightness = 0x96; + break; + + case 0: + brightness = 0x72; + break; + + case -1: + brightness = 0x48; + break; + + case -2: + brightness = 0x24; + break; + + case -3: + brightness = 0x00; + break; + + default: // 0 + break; + } + + ispctrl5 |= 0x40; // enable brightness + ret = write_reg(sensor->slv_addr, ISPCTRL5, ispctrl5); + ret = write_reg(sensor->slv_addr, BRIGHT, brightness); + if (ret != 0) + { + ESP_LOGD(TAG, "Set brightness to: %d", level); + sensor->status.brightness = level; + } + return ret; +} + +static int set_contrast(sensor_t *sensor, int level) +{ + int ret = 0; + uint8_t ispctrl5 = read_reg(sensor->slv_addr, ISPCTRL5); + uint8_t value1 = 0; + + ispctrl5 |= 0x80; // enable contrast + ret = write_reg(sensor->slv_addr, ISPCTRL5, ispctrl5); + ret = write_reg(sensor->slv_addr, ACONTQ, (level * 0x20) & 0xFF); + if (ret == 0) + { + ESP_LOGD(TAG, "Set contrast to: %d", level); + sensor->status.contrast = level; + } + return ret; +} + +static int set_saturation(sensor_t *sensor, int level) +{ + int ret = 0; + ret = write_reg(sensor->slv_addr, SAT, (level * 0x20) + 0x4A); + if (ret == 0) + { + ESP_LOGD(TAG, "Set saturation to: %d", level); + sensor->status.saturation = level; + } + return ret; +} + +static int get_sharpness(sensor_t *sensor) +{ + int ret = 0; + int level = 0; + ret = read_reg(sensor->slv_addr, EDGE); + + level = (ret - 0x60) / 0x20; + ESP_LOGD(TAG, "Get sharpness: %d", level); + + return level; +} +static int set_sharpness(sensor_t *sensor, int level) +{ + int ret = 0; + ret = write_reg(sensor->slv_addr, EDGE, (level * 0x20) + 0x60); + if (ret == 0) + { + ESP_LOGD(TAG, "Set sharpness to: %d", level); + sensor->status.sharpness = level; + } + return ret; +} + +static int get_denoise(sensor_t *sensor) +{ + int ret = 0; + int level = 0; + ret = read_reg(sensor->slv_addr, YDN); + + level = (ret - 0x07) / 2; + ESP_LOGD(TAG, "Get denoise: %d", level); + + return level; +} +static int set_denoise(sensor_t *sensor, int level) +{ + int ret = 0; + uint8_t ispctrl5 = read_reg(sensor->slv_addr, ISPCTRL5); + + ispctrl5 |= 0x20; // enable denoise + ret = write_reg(sensor->slv_addr, ISPCTRL5, ispctrl5); + ret = write_reg(sensor->slv_addr, YDN, (level * 2) + 0x07); + + if (ret == 0) + { + ESP_LOGD(TAG, "Set denoise to: %d", level); + sensor->status.denoise = level; + } + + return ret; +} + +static int get_reg(sensor_t *sensor, int reg, int mask) +{ + int ret = 0, ret2 = 0; + if (mask > 0xFF) + { + ret = read_reg16(sensor->slv_addr, reg); + if (ret >= 0 && mask > 0xFFFF) + { + ret2 = read_reg(sensor->slv_addr, reg + 2); + if (ret2 >= 0) + { + ret = (ret << 8) | ret2; + } + else + { + ret = ret2; + } + } + } + else + { + ret = read_reg(sensor->slv_addr, reg); + } + if (ret > 0) + { + ret &= mask; + } + return ret; +} + +static int set_reg(sensor_t *sensor, int reg, int mask, int value) +{ + int ret = 0, ret2 = 0; + if (mask > 0xFF) + { + ret = read_reg16(sensor->slv_addr, reg); + if (ret >= 0 && mask > 0xFFFF) + { + ret2 = read_reg(sensor->slv_addr, reg + 2); + if (ret2 >= 0) + { + ret = (ret << 8) | ret2; + } + else + { + ret = ret2; + } + } + } + else + { + ret = read_reg(sensor->slv_addr, reg); + } + if (ret < 0) + { + return ret; + } + value = (ret & ~mask) | (value & mask); + if (mask > 0xFFFF) + { + ret = write_reg16(sensor->slv_addr, reg, value >> 8); + if (ret >= 0) + { + ret = write_reg(sensor->slv_addr, reg + 2, value & 0xFF); + } + } + else if (mask > 0xFF) + { + ret = write_reg16(sensor->slv_addr, reg, value); + } + else + { + ret = write_reg(sensor->slv_addr, reg, value); + } + return ret; +} + +static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning) +{ + return 0; +} + +static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div) +{ + int ret = 0; + uint8_t ckcfg1 = 0; + uint8_t ckcfg2 = 0; + uint8_t ckcfg3 = 0; + uint8_t pll2 = 0; + + if (sensor->xclk_freq_hz <= 6000000) + { + ckcfg2 = 0x00; + } + else if (sensor->xclk_freq_hz <= 12000000) + { + ckcfg2 = 0x20; + } + else if (sensor->xclk_freq_hz <= 18000000) + { + ckcfg2 = 0x40; + } + else if (sensor->xclk_freq_hz <= 24000000) + { + ckcfg2 = 0x60; + } + else if (sensor->xclk_freq_hz <= 30000000) + { + ckcfg2 = 0x80; + } + else if (sensor->xclk_freq_hz <= 36000000) + { + ckcfg2 = 0xA0; + } + else if (sensor->xclk_freq_hz <= 42000000) + { + ckcfg2 = 0xC0; + } + else + { // max is 48000000 + ckcfg2 = 0xE0; + } + + if (bypass == 0) + { + switch (multiplier) + { + case 204: + ckcfg2 |= 10; + break; + case 216: + ckcfg2 |= 11; + break; + case 228: + ckcfg2 |= 0x12; + break; + case 240: + ckcfg2 |= 0x13; + break; + case 288: + ckcfg2 |= 0x17; + break; + case 300: + ckcfg2 |= 0x18; + break; + case 312: + ckcfg2 |= 0x19; + break; + case 324: + ckcfg2 |= 0x1A; + break; + case 336: + ckcfg2 |= 0x1B; + break; + case 348: + ckcfg2 |= 0x1C; + break; + case 360: + ckcfg2 |= 0x1D; + break; + default: + ckcfg2 |= 0x17; + break; + } + } + + if (pclk_manual > 0) + { + if (pclk_div > 128) + { + pclk_div = 128; + } + if (pclk_div < 1) + { + pclk_div = 1; + } + ckcfg1 |= (pclk_div - 1); + } + + if (root_2x > 0) + { + ckcfg3 = 0x00; + } + else + { + ckcfg3 = 0x01; + } + + ESP_LOGD(TAG, "ckcfg1 = 0x%02x, ckcfg2 = 0x%02x, ckcfg3 = 0x%02x, pll2 = 0x%02x", ckcfg1, ckcfg2, ckcfg3, pll2); + ret = write_reg(sensor->slv_addr, CKCFG1, ckcfg1); + ret = write_reg(sensor->slv_addr, CKCFG2, ckcfg2); + ret = write_reg(sensor->slv_addr, CKCFG3, ckcfg3); + ret = write_reg(sensor->slv_addr, PLL2, pll2); + + return ret; +} + +static int set_xclk(sensor_t *sensor, int timer, int xclk) +{ + int ret = 0; + sensor->xclk_freq_hz = xclk * 1000000U; + ret = xclk_timer_conf(timer, sensor->xclk_freq_hz); + if (ret == 0) + { + ESP_LOGD(TAG, "Set xclk to %d", xclk); + } + return ret; +} + +static int init_status(sensor_t *sensor) +{ + sensor->status.brightness = 0; + sensor->status.contrast = 0; + sensor->status.saturation = 0; + sensor->status.sharpness = get_sharpness(sensor); + sensor->status.denoise = get_denoise(sensor); + sensor->status.ae_level = 0; + sensor->status.awb = check_reg_mask(sensor->slv_addr, AEWBCFG, 0x02); + sensor->status.agc = true; + sensor->status.aec = check_reg_mask(sensor->slv_addr, AEWBCFG, 0x04); + sensor->status.hmirror = check_reg_mask(sensor->slv_addr, RDCFG, 0x02); + sensor->status.vflip = check_reg_mask(sensor->slv_addr, RDCFG, 0x01); + sensor->status.lenc = check_reg_mask(sensor->slv_addr, ISPCTRL3, 0x40); + sensor->status.awb_gain = read_reg(sensor->slv_addr, DGAIN); + sensor->status.agc_gain = read_reg(sensor->slv_addr, AGAIN); + sensor->status.aec_value = read_reg(sensor->slv_addr, AETARGM); + return 0; +} + +int hm1055_detect(int slv_addr, sensor_id_t *id) +{ + if (HM1055_SCCB_ADDR == slv_addr) + { + uint8_t h = SCCB_Read16(slv_addr, IDH); + uint8_t l = SCCB_Read16(slv_addr, IDL); + uint16_t PID = (h << 8) | l; + if (HM1055_PID == PID) + { + id->PID = PID; + return PID; + } + else + { + ESP_LOGD(TAG, "Mismatch PID=0x%x", PID); + } + } + return 0; +} + +int hm1055_init(sensor_t *sensor) +{ + sensor->reset = reset; + sensor->set_pixformat = set_pixformat; + sensor->set_framesize = set_framesize; + sensor->set_contrast = set_contrast; + sensor->set_brightness = set_brightness; + sensor->set_saturation = set_saturation; + sensor->set_sharpness = set_sharpness; + sensor->set_gainceiling = NULL; + sensor->set_quality = set_quality; + sensor->set_colorbar = set_colorbar; + sensor->set_gain_ctrl = set_gain_ctrl; + sensor->set_exposure_ctrl = set_exposure_ctrl; + sensor->set_whitebal = set_whitebal; + sensor->set_hmirror = set_hmirror; + sensor->set_vflip = set_vflip; + sensor->init_status = init_status; + sensor->set_aec2 = NULL; + sensor->set_aec_value = set_aec_value; + sensor->set_special_effect = NULL; + sensor->set_wb_mode = NULL; + sensor->set_ae_level = set_ae_level; + sensor->set_dcw = NULL; + sensor->set_bpc = NULL; + sensor->set_wpc = NULL; + sensor->set_agc_gain = set_agc_gain; + sensor->set_raw_gma = set_raw_gma_dsp; + sensor->set_lenc = set_lenc_dsp; + sensor->set_denoise = set_denoise; + + sensor->get_reg = get_reg; + sensor->set_reg = set_reg; + sensor->set_res_raw = set_res_raw; + sensor->set_pll = _set_pll; + sensor->set_xclk = set_xclk; + return 0; +} diff --git a/sensors/private_include/hm1055.h b/sensors/private_include/hm1055.h new file mode 100755 index 0000000000..dd6d44d753 --- /dev/null +++ b/sensors/private_include/hm1055.h @@ -0,0 +1,27 @@ +/* + * HM1055 driver. + */ +#ifndef __HM1055_H__ +#define __HM1055_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 hm1055_detect(int slv_addr, sensor_id_t *id); + +/** + * @brief initialize sensor function pointers + * + * @param sensor pointer of sensor + * @return + * Always 0 + */ +int hm1055_init(sensor_t *sensor); + +#endif // __HM1055_H__ diff --git a/sensors/private_include/hm1055_regs.h b/sensors/private_include/hm1055_regs.h new file mode 100755 index 0000000000..da83db0993 --- /dev/null +++ b/sensors/private_include/hm1055_regs.h @@ -0,0 +1,120 @@ +/* + * HM1055 register definitions. + */ +#ifndef __REG_REGS_H__ +#define __REG_REGS_H__ + +// Imager Configuration +#define CMU 0x0000 +#define IDH 0x0001 +#define IDL 0x0002 +#define VID 0x0003 +#define PWDCTRL 0x0004 +#define TGRDCFG 0x0005 +#define RDCFG 0x0006 +#define VREAD 0x000D +#define HREAD 0x000E +#define IMG_CFG 0x000F + +// Imager Operation +#define BLNKRH 0x0010 +#define BLNKRL 0x0011 +#define BLNKCCLK 0x0012 +#define BLNKC 0x0013 +#define INTGH 0x0015 +#define INTGL 0x0016 +#define AGAIN 0x0018 +#define DGAIN 0x001D + +// IO and Clock Configuration Setting +#define OPRTCFG 0x0020 +#define SFTRST 0x0022 // any value to reset +#define IOCTRLH 0x0023 +#define IOCTRLL 0x0024 +#define CKCFG1 0x0025 +#define CKCFG2 0x0026 +#define PORTCTRL 0x0027 +#define TESTIMG 0x0028 +#define CCIR656 0x0029 +#define PLL1 0x002A +#define CKCFG3 0x002B +#define PLL2 0x002C + +// Black Level Target +#define BLCTGT 0x0040 +#define BLCTGT2 0x0053 + +// Vertical Arbitrary Window Configuration +#define VAWINSTARTH 0x0078 +#define VAWINSTARTL 0x0079 +#define VAWINENDH 0x007A +#define VAWINENDL 0x007B + +// Image Signal Processing Control +#define CMU_AE 0x0100 +#define CMU_AWB 0x0101 +#define ISPID 0x0105 +#define ISPCTRL1 0x0120 +#define ISPCTRL2 0x0121 +#define ISPCTRL3 0x0122 +#define ISPCTRL4 0x0124 +#define ISPCTRL5 0x0125 +#define ISPCTRL6 0x0126 + +// RAW Noise Filter Control +#define RAWNF 0x01E4 +#define ARAWNF 0x01E5 +#define ARAWNFODEL 0x01E6 + +// Automatic Exposure Control Registers +#define AEWBCFG 0x0380 +#define AETARGU 0x0381 +#define AETARGL 0x0382 +#define AETARGM 0x0383 + +// Saturation and Hue Control +#define SAT 0x0480 +#define ASAT 0x0481 +#define ASATODEL 0x0482 +#define HUESIN 0x0486 +#define HUECOS 0x0487 +#define SCENE 0x0488 + +// Contrast and Brightness Control +#define CONTM 0x04B0 +#define ACONTM 0x04B1 +#define CONTQ 0x04B3 +#define ACONTQ 0x04B4 +#define CONTN 0x04B6 +#define CONTP 0x04B9 +#define CONTGAIN 0x04BC +#define YMEAN 0x04BD +#define BRIGHT 0x04C0 + +// Y Denoise Control +#define YDN 0x0580 +#define AYDN 0x0581 +#define AYDNODEL 0x0582 + +// Sharpness Control +#define EDGE 0x05A1 + +// Fade to Black Control +#define F2BMEAN 0x05D0 +#define F2BRANGE 0x05D1 + +// Digital Window Control +#define YUVSCXL 0x05E0 +#define YUVSCXH 0x05E1 +#define YUVSCYL 0x05E2 +#define YUVSCYH 0x05E3 +#define WINXSTL 0x05E4 +#define WINXSTH 0x05E5 +#define WINXEDL 0x05E6 +#define WINXEDH 0x05E7 +#define WINYSTL 0x05E8 +#define WINYSTH 0x05E9 +#define WINYEDL 0x05EA +#define WINYEDH 0x05EB + +#endif //__REG_REGS_H__ diff --git a/sensors/private_include/hm1055_settings.h b/sensors/private_include/hm1055_settings.h new file mode 100755 index 0000000000..9d2831db18 --- /dev/null +++ b/sensors/private_include/hm1055_settings.h @@ -0,0 +1,697 @@ + +#include + +#define REG_DLY 0xffff +#define REGLIST_TAIL 0xffff + +static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { + {0x0022, 0x00}, // RESET + {0x0026, 0x77}, // 24MHz MCLK, 72MHz PCLK + {0x002A, 0x44}, // Modified by Wilson + {0x002B, 0x02}, + {0x002C, 0x00}, // Turn off DCC + {0x0025, 0x00}, // PLL on + {0x0020, 0x08}, + {0x0027, 0x40}, // FPGA 24 data format + {0x0028, 0xC0}, // + {0x0004, 0x10}, // + {0x0006, 0x00}, // Modified by Brandon. No Flip + {0x0012, 0x0F}, + {0x0044, 0x04}, + {0x004A, 0x2A}, + {0x004B, 0x72}, + {0x004E, 0x30}, + {0x0070, 0x2A}, + {0x0071, 0x57}, + {0x0072, 0x55}, + {0x0073, 0x30}, + {0x0077, 0x04}, + {0x0080, 0xC2}, + {0x0082, 0xA2}, + {0x0083, 0xF0}, + {0x0085, 0x11}, // Set ADC power to 100% and Increase Comparator power and reduce ADC swing range(4/19, K.Kim) + {0x0086, 0x22}, + {0x0087, 0x08}, + {0x0088, 0x6e}, // Temperature config 0x80[7:5]:76C, Minimize CTIA & PGA power and set VCMI to 200mV(4/19, K.Kim) + {0x0089, 0x2A}, + {0x008A, 0x2F}, // Set BLC power to 50%(4/19, K.Kim) + {0x008D, 0x20}, + {0x008f, 0x77}, + {0x0090, 0x01}, + {0x0091, 0x02}, + {0x0092, 0x03}, + {0x0093, 0x04}, + {0x0094, 0x14}, + {0x0095, 0x09}, + {0x0096, 0x0A}, + {0x0097, 0x0B}, + {0x0098, 0x0C}, + {0x0099, 0x04}, + {0x009A, 0x14}, + {0x009B, 0x34}, + {0x00A0, 0x00}, + {0x00A1, 0x00}, + {0x0B3B, 0x0B}, + {0x0040, 0x0A}, // BLC Phase1 + {0x0053, 0x0A}, // BLC Phase2 + {0x0120, 0x36}, // 36:50Hz, 37:60Hz + {0x0121, 0x80}, + {0x0122, 0xEB}, // 122[3] is temperature_control_bit2 + {0x0123, 0xCC}, // 123[3:0]maskTH + {0x0124, 0xDE}, + {0x0125, 0xDF}, + {0x0126, 0x70}, + {0x0128, 0x1F}, + {0x0129, 0x8F}, + {0x0132, 0xF8}, + {0x011F, 0x08}, // BPC COLD PIXEL ENABLE if 11F[0] = 1 then on else 0 off + {0x0144, 0x04}, // Hot pixel ratio + {0x0145, 0x00}, // Hot pixel ratio alpha + {0x0146, 0x20}, // Vertical Line TH + {0x0147, 0x20}, // Vertical Line TH alpha + {0x0148, 0x14}, // HD_2line_BPC_TH + {0x0149, 0x14}, // HD_2line_BPC_TH alpha + {0x0156, 0x0C}, // BPC RATIO FOR COLD PIXEL + {0x0157, 0x0C}, // BPC RATIO FOR COLD PIXEL + {0x0158, 0x0A}, // 2 LINE BPC RATIO + {0x0159, 0x0A}, // 2 LINE BPC RATIO + {0x015A, 0x03}, // BPC RATIO FOR COLD PIXEL + {0x015B, 0x40}, // Corner TH + {0x015C, 0x21}, // Corner TH alpha + {0x015E, 0x0F}, // Debug_mode + {0x0168, 0xC8}, // BPC T1 + {0x0169, 0xC8}, // BPC T1 ALPHA + {0x016A, 0x96}, // BPC T2 + {0x016B, 0x96}, // BPC T2 ALPHA + {0x016C, 0x64}, // BPC T3 + {0x016D, 0x64}, // BPC T3 ALPHA + {0x016E, 0x32}, // BPC T4 + {0x016F, 0x32}, // BPC T4 ALPHA + {0x01EF, 0xF1}, // BPC Control Byte + {0x0131, 0x44}, //[6] DS_EN, [5] VOFF, [4] EDGEON, [3] SBPI_MODE, [2] RAW_SAHRPNESS_EN, [1]G1G2B_EN + {0x014C, 0x60}, // VSUB + {0x014D, 0x24}, // VSUB ALPHA0 + {0x015D, 0x90}, //[7] TG_en// [6:0]:min Tgain + {0x01D8, 0x40}, // NOISE TH + {0x01D9, 0x20}, // NL_V1 + {0x01DA, 0x23}, // NL_V2 + {0x0150, 0x05}, // NL_INC1 + {0x0155, 0x07}, // NL_INC2 + {0x0178, 0x10}, // EVDIV + {0x017A, 0x10}, // EVDIVD + {0x01BA, 0x10}, // EVSUB + {0x0176, 0x00}, // EDGE2W + {0x0179, 0x10}, // EVDIV ALPHA0 + {0x017B, 0x10}, // EVDIVD ALPHA0 + {0x01BB, 0x10}, // EVSUB ALPHA0 + {0x0177, 0x00}, // EDGE2W ALPHA0 + {0x01E7, 0x20}, // TLTH1 + {0x01E8, 0x30}, // TLTH2 + {0x01E9, 0x50}, // TLTH3 + {0x01E4, 0x18}, // BD STRENGTH + {0x01E5, 0x20}, // BD STRENGTH ALPHA0 + {0x01E6, 0x04}, // BD STRENGTH OUTDOOR + {0x0210, 0x21}, // STD + {0x0211, 0x0A}, // STD ALPHA0 + {0x0212, 0x21}, // STD OUTDOOR + {0x01DB, 0x04}, // G1G2 STRENGTH + {0x01DC, 0x14}, // SBPI_OFFSET + {0x0151, 0x08}, + {0x01F2, 0x18}, + {0x01F8, 0x3C}, + {0x01FE, 0x24}, + {0x0213, 0x03}, // BPC_Control_BYTE_2 // BPC_line_edge_th 3rd bpc + {0x0214, 0x03}, // BPC_Control_BYTE_3 // 3rd bpc strength[4:0] , [7:5] is for 2line bpc temp_control_bit + {0x0215, 0x10}, // BPC_Control_BYTE_4 // 1st bpc strength[4:0] + {0x0216, 0x08}, // BPC_Control_BYTE_5 // 1st br pulse strength[4:0] + {0x0217, 0x05}, // BPC_Control_BYTE_6 // 1st gbgr pulse strengh[4:0] + {0x0218, 0xB8}, + {0x0219, 0x01}, + {0x021A, 0xB8}, + {0x021B, 0x01}, + {0x021C, 0xB8}, + {0x021D, 0x01}, + {0x021E, 0xB8}, + {0x021F, 0x01}, + {0x0220, 0xF1}, + {0x0221, 0x5D}, + {0x0222, 0x0A}, + {0x0223, 0x80}, + {0x0224, 0x50}, + {0x0225, 0x09}, + {0x0226, 0x80}, + {0x022A, 0x56}, + {0x022B, 0x13}, + {0x022C, 0x80}, + {0x022D, 0x11}, + {0x022E, 0x08}, + {0x022F, 0x11}, + {0x0230, 0x08}, + {0x0233, 0x11}, + {0x0234, 0x08}, + {0x0235, 0x88}, + {0x0236, 0x02}, + {0x0237, 0x88}, + {0x0238, 0x02}, + {0x023B, 0x88}, + {0x023C, 0x02}, + {0x023D, 0x68}, + {0x023E, 0x01}, + {0x023F, 0x68}, + {0x0240, 0x01}, + {0x0243, 0x68}, + {0x0244, 0x01}, + {0x0251, 0x0F}, + {0x0252, 0x00}, + {0x0260, 0x00}, + {0x0261, 0x4A}, + {0x0262, 0x2C}, + {0x0263, 0x68}, + {0x0264, 0x40}, + {0x0265, 0x2C}, + {0x0266, 0x6A}, + {0x026A, 0x40}, + {0x026B, 0x30}, + {0x026C, 0x66}, + {0x0278, 0x98}, + {0x0279, 0x20}, + {0x027A, 0x80}, + {0x027B, 0x73}, + {0x027C, 0x08}, + {0x027D, 0x80}, + {0x0280, 0x0D}, + {0x0282, 0x1A}, + {0x0284, 0x30}, + {0x0286, 0x53}, + {0x0288, 0x62}, + {0x028a, 0x6E}, + {0x028c, 0x7A}, + {0x028e, 0x83}, + {0x0290, 0x8B}, + {0x0292, 0x92}, + {0x0294, 0x9D}, + {0x0296, 0xA8}, + {0x0298, 0xBC}, + {0x029a, 0xCF}, + {0x029c, 0xE2}, + {0x029e, 0x2A}, + {0x02A0, 0x02}, + {0x02C0, 0x7D}, // CCM N + {0x02C1, 0x01}, + {0x02C2, 0x7C}, + {0x02C3, 0x04}, + {0x02C4, 0x01}, + {0x02C5, 0x04}, + {0x02C6, 0x3E}, + {0x02C7, 0x04}, + {0x02C8, 0x90}, + {0x02C9, 0x01}, + {0x02CA, 0x52}, + {0x02CB, 0x04}, + {0x02CC, 0x04}, + {0x02CD, 0x04}, + {0x02CE, 0xA9}, + {0x02CF, 0x04}, + {0x02D0, 0xAD}, + {0x02D1, 0x01}, + {0x0302, 0x00}, + {0x0303, 0x00}, + {0x0304, 0x00}, + {0x02e0, 0x04}, // CCM by Alpha + {0x02F0, 0x4E}, // CCM A + {0x02F1, 0x04}, + {0x02F2, 0xB1}, + {0x02F3, 0x00}, + {0x02F4, 0x63}, + {0x02F5, 0x04}, + {0x02F6, 0x28}, + {0x02F7, 0x04}, + {0x02F8, 0x29}, + {0x02F9, 0x04}, + {0x02FA, 0x51}, + {0x02FB, 0x00}, + {0x02FC, 0x64}, + {0x02FD, 0x04}, + {0x02FE, 0x6B}, + {0x02FF, 0x04}, + {0x0300, 0xCF}, + {0x0301, 0x00}, + {0x0305, 0x08}, + {0x0306, 0x40}, + {0x0307, 0x00}, + {0x032D, 0x70}, + {0x032E, 0x01}, + {0x032F, 0x00}, + {0x0330, 0x01}, + {0x0331, 0x70}, + {0x0332, 0x01}, + {0x0333, 0x82}, // AWB channel offset + {0x0334, 0x82}, + {0x0335, 0x86}, + {0x0340, 0x30}, // AWB + {0x0341, 0x44}, + {0x0342, 0x4A}, + {0x0343, 0x3C}, // CT1 + {0x0344, 0x83}, // + {0x0345, 0x4D}, // CT2 + {0x0346, 0x75}, // + {0x0347, 0x56}, // CT3 + {0x0348, 0x68}, // + {0x0349, 0x5E}, // CT4 + {0x034A, 0x5C}, // + {0x034B, 0x65}, // CT5 + {0x034C, 0x52}, // + {0x0350, 0x88}, + {0x0352, 0x18}, + {0x0354, 0x80}, + {0x0355, 0x50}, + {0x0356, 0x88}, + {0x0357, 0xE0}, + {0x0358, 0x00}, + {0x035A, 0x00}, + {0x035B, 0xAC}, + {0x0360, 0x02}, + {0x0361, 0x18}, + {0x0362, 0x50}, + {0x0363, 0x6C}, + {0x0364, 0x00}, + {0x0365, 0xF0}, + {0x0366, 0x08}, + {0x036A, 0x10}, + {0x036B, 0x18}, + {0x036E, 0x10}, + {0x0370, 0x10}, + {0x0371, 0x18}, + {0x0372, 0x0C}, + {0x0373, 0x38}, + {0x0374, 0x3A}, + {0x0375, 0x12}, + {0x0376, 0x20}, + {0x0380, 0xFF}, + {0x0381, 0x44}, + {0x0382, 0x34}, + {0x038A, 0x80}, + {0x038B, 0x0A}, + {0x038C, 0xC1}, + {0x038E, 0x3C}, + {0x038F, 0x09}, + {0x0390, 0xE0}, + {0x0391, 0x01}, + {0x0392, 0x03}, + {0x0393, 0x80}, + {0x0395, 0x22}, + {0x0398, 0x02}, // AE Frame Control + {0x0399, 0xF0}, + {0x039A, 0x03}, + {0x039B, 0xAC}, + {0x039C, 0x04}, + {0x039D, 0x68}, + {0x039E, 0x05}, + {0x039F, 0xE0}, + {0x03A0, 0x07}, + {0x03A1, 0x58}, + {0x03A2, 0x08}, + {0x03A3, 0xD0}, + {0x03A4, 0x0B}, + {0x03A5, 0xC0}, + {0x03A6, 0x18}, + {0x03A7, 0x1C}, + {0x03A8, 0x20}, + {0x03A9, 0x24}, + {0x03AA, 0x28}, + {0x03AB, 0x30}, + {0x03AC, 0x24}, + {0x03AD, 0x21}, + {0x03AE, 0x1C}, + {0x03AF, 0x18}, + {0x03B0, 0x17}, + {0x03B1, 0x13}, + {0x03B7, 0x64}, // AEAWB Windowing Step for 7x7 + {0x03B8, 0x00}, + {0x03B9, 0xB4}, + {0x03BA, 0x00}, + {0x03bb, 0xff}, // WinWeight 7x7 + {0x03bc, 0xff}, + {0x03bd, 0xff}, + {0x03be, 0xff}, + {0x03bf, 0xff}, + {0x03c0, 0xff}, + {0x03c1, 0x01}, + {0x03e0, 0x04}, + {0x03e1, 0x11}, + {0x03e2, 0x01}, + {0x03e3, 0x04}, + {0x03e4, 0x10}, + {0x03e5, 0x21}, + {0x03e6, 0x11}, + {0x03e7, 0x00}, + {0x03e8, 0x11}, + {0x03e9, 0x32}, + {0x03ea, 0x12}, + {0x03eb, 0x01}, + {0x03ec, 0x21}, + {0x03ed, 0x33}, + {0x03ee, 0x23}, + {0x03ef, 0x01}, + {0x03f0, 0x11}, + {0x03f1, 0x32}, + {0x03f2, 0x12}, + {0x03f3, 0x01}, + {0x03f4, 0x10}, + {0x03f5, 0x21}, + {0x03f6, 0x11}, + {0x03f7, 0x00}, + {0x03f8, 0x04}, + {0x03f9, 0x11}, + {0x03fa, 0x01}, + {0x03fb, 0x04}, + {0x03DC, 0x47}, // SubWinAE setting, min_dark_cnt=7, exit_dark_cnt=4 + {0x03DD, 0x5A}, // AEbl_th=70% + {0x03DE, 0x41}, // enable SubWinAE, bottom 3x5, DM_tol=12.5% + {0x03DF, 0x53}, // limit_ratio=65% + {0x0420, 0x82}, // Digital Gain offset + {0x0421, 0x00}, + {0x0422, 0x00}, + {0x0423, 0x88}, + {0x0430, 0x08}, // ABLC + {0x0431, 0x30}, + {0x0432, 0x0c}, + {0x0433, 0x04}, // 0A + {0x0435, 0x08}, // 10 + {0x0450, 0xFF}, // Alpha + {0x0451, 0xD0}, + {0x0452, 0xB8}, + {0x0453, 0x88}, + {0x0454, 0x00}, + {0x0458, 0x80}, + {0x0459, 0x03}, + {0x045A, 0x00}, + {0x045B, 0x50}, + {0x045C, 0x00}, + {0x045D, 0x90}, + {0x0465, 0x02}, + {0x0466, 0x14}, + {0x047A, 0x00}, // ELOFFNRB + {0x047B, 0x00}, // ELOFFNRY + {0x047C, 0x04}, + {0x047D, 0x50}, + {0x047E, 0x04}, + {0x047F, 0x90}, + {0x0480, 0x58}, + {0x0481, 0x06}, + {0x0482, 0x08}, // Sat outdoor + {0x04B0, 0x50}, // Contrast + {0x04B6, 0x30}, + {0x04B9, 0x10}, + {0x04B3, 0x00}, + {0x04B1, 0x85}, + {0x04B4, 0x00}, + {0x0540, 0x00}, // + {0x0541, 0xBC}, // 60Hz Flicker + {0x0542, 0x00}, // + {0x0543, 0xE1}, // 50Hz Flicker + {0x0580, 0x04}, // BLUR WEIGHT + {0x0581, 0x0F}, // BLUR WEIGHT ALPHA0 + {0x0582, 0x04}, // BLUR WEIGHT OUTDOOR + {0x05A1, 0x0A}, // SHARPNESS STRENGTH + {0x05A2, 0x21}, //[6:4] SHARP_ORE_NEG, [1:0] FIL SELECTION + {0x05A3, 0x84}, //[7]EA_EN, [6] EE_CENTER, [3:0] EDGEDIV + {0x05A4, 0x24}, // SHARP_ORE + {0x05A5, 0xFF}, // ORE_MAX + {0x05A6, 0x00}, // ORE_LOW + {0x05A7, 0x24}, // OUGB_HOT + {0x05A8, 0x24}, // OUGB_COLD + {0x05A9, 0x02}, // EDGE RANGE + {0x05B1, 0x24}, // EDGE SUB + {0x05B2, 0x0C}, //[3] Y_ADJC, [2]CMODE + {0x05B4, 0x1F}, // FIX WEIGHT + {0x05AE, 0x75}, // OAR START + {0x05AF, 0x78}, //[7:5]OAR STEP, [4:0]OAR DECSTR + {0x05B6, 0x00}, // SHARPNESS STRENGTH ALPHA0 + {0x05B7, 0x10}, // SHARP_ORE ALPHA0 + {0x05BF, 0x20}, // EDGESUB ALPHA0 + {0x05C1, 0x06}, // SHARPNESS STRENGTH OUTDOOR + {0x05C2, 0x18}, // SHARP ORE OUTDOOR + {0x05C7, 0x00}, // TFBPI + {0x05CC, 0x04}, // Raw sharpness strength + {0x05CD, 0x00}, // Raw sharpness strength ALPHA0 + {0x05CE, 0x03}, // Raw sharpness strength outdoor + {0x05E4, 0x08}, + {0x05E5, 0x00}, + {0x05E6, 0x07}, + {0x05E7, 0x05}, + {0x05E8, 0x08}, + {0x05E9, 0x00}, + {0x05EA, 0xD7}, + {0x05EB, 0x02}, + {0x0660, 0x00}, + {0x0661, 0x16}, + {0x0662, 0x07}, + {0x0663, 0xf1}, + {0x0664, 0x07}, + {0x0665, 0xde}, + {0x0666, 0x07}, + {0x0667, 0xe7}, + {0x0668, 0x00}, + {0x0669, 0x35}, + {0x066a, 0x07}, + {0x066b, 0xf9}, + {0x066c, 0x07}, + {0x066d, 0xb7}, + {0x066e, 0x00}, + {0x066f, 0x27}, + {0x0670, 0x07}, + {0x0671, 0xf3}, + {0x0672, 0x07}, + {0x0673, 0xc5}, + {0x0674, 0x07}, + {0x0675, 0xee}, + {0x0676, 0x00}, + {0x0677, 0x16}, + {0x0678, 0x01}, + {0x0679, 0x80}, + {0x067a, 0x00}, + {0x067b, 0x85}, + {0x067c, 0x07}, + {0x067d, 0xe1}, + {0x067e, 0x07}, + {0x067f, 0xf5}, + {0x0680, 0x07}, + {0x0681, 0xb9}, + {0x0682, 0x00}, + {0x0683, 0x31}, + {0x0684, 0x07}, + {0x0685, 0xe6}, + {0x0686, 0x07}, + {0x0687, 0xd3}, + {0x0688, 0x00}, + {0x0689, 0x18}, + {0x068a, 0x07}, + {0x068b, 0xfa}, + {0x068c, 0x07}, + {0x068d, 0xd2}, + {0x068e, 0x00}, + {0x068f, 0x08}, + {0x0690, 0x00}, + {0x0691, 0x02}, + {0xAFD0, 0x03}, // Auto Flicker detection off + {0xAFD3, 0x18}, + {0xAFD4, 0x04}, + {0xAFD5, 0xB8}, // auto flicker samp time 3188 + {0xAFD6, 0x02}, + {0xAFD7, 0x44}, + {0xAFD8, 0x02}, + + // QVGA + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XBE}, + {0x05E1, 0x00}, + {0x05E2, 0xBE}, + {0x05E3, 0x00}, + {0x05E4, 0x3A}, + {0x05E5, 0x00}, + {0x05E6, 0x79}, + {0x05E7, 0x01}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xF3}, + {0x05EB, 0x00}, + + {0x0000, 0x01}, + {0x0100, 0x01}, + {0x0101, 0x01}, + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_HD[][2] = { + {0x0006, 0x00}, + {0x000D, 0x00}, + {0x000E, 0x00}, + {0x0122, 0xFB}, + {0x0125, 0xDF}, + {0x0126, 0x70}, + {0x05E4, 0x08}, + {0x05E5, 0x00}, + {0x05E6, 0x07}, + {0x05E7, 0x05}, + {0x05E8, 0x08}, + {0x05E9, 0x00}, + {0x05EA, 0xD7}, + {0x05EB, 0x02}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_SVGA[][2] = { + {0x0006, 0x10}, + {0x000D, 0x00}, + {0x000E, 0x00}, + {0x0122, 0xEB}, + {0x0125, 0xDF}, + {0x0126, 0x70}, + {0x05E4, 0x58}, + {0x05E5, 0x00}, + {0x05E6, 0x77}, + {0x05E7, 0x03}, + {0x05E8, 0x42}, + {0x05E9, 0x00}, + {0x05EA, 0x99}, + {0x05EB, 0x02}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA[][2] = { + {0x0006, 0x10}, + {0x000D, 0x00}, + {0x000E, 0x00}, + {0x0122, 0xEB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XC1}, + {0x05E1, 0x00}, + {0x05E2, 0xC1}, + {0x05E3, 0x00}, + {0x05E4, 0x03}, + {0x05E5, 0x00}, + {0x05E6, 0x82}, + {0x05E7, 0x02}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xE3}, + {0x05EB, 0x01}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_CIF[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x72}, + {0x05E0, 0XC0}, + {0x05E1, 0x00}, + {0x05E2, 0x8F}, + {0x05E3, 0x00}, + {0x05E4, 0x11}, + {0x05E5, 0x00}, + {0x05E6, 0xA0}, + {0x05E7, 0x01}, + {0x05E8, 0x08}, + {0x05E9, 0x00}, + {0x05EA, 0x2F}, + {0x05EB, 0x01}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XBE}, + {0x05E1, 0x00}, + {0x05E2, 0xBE}, + {0x05E3, 0x00}, + {0x05E4, 0x3A}, + {0x05E5, 0x00}, + {0x05E6, 0x79}, + {0x05E7, 0x01}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xF3}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QCIF[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x72}, + {0x05E0, 0X52}, + {0x05E1, 0x01}, + {0x05E2, 0x38}, + {0x05E3, 0x01}, + {0x05E4, 0x22}, + {0x05E5, 0x00}, + {0x05E6, 0xD1}, + {0x05E7, 0x00}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0x93}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_240X240[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XBE}, + {0x05E1, 0x00}, + {0x05E2, 0xBE}, + {0x05E3, 0x00}, + {0x05E4, 0x62}, + {0x05E5, 0x00}, + {0x05E6, 0x51}, + {0x05E7, 0x01}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xF3}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QQVGA[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0X78}, + {0x05E1, 0x01}, + {0x05E2, 0x78}, + {0x05E3, 0x01}, + {0x05E4, 0x1E}, + {0x05E5, 0x00}, + {0x05E6, 0xBD}, + {0x05E7, 0x00}, + {0x05E8, 0x03}, + {0x05E9, 0x00}, + {0x05EA, 0x7A}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; \ No newline at end of file From 588eeff2cc231ab34f7a0fe360418f6779c61445 Mon Sep 17 00:00:00 2001 From: LynnL4 Date: Tue, 5 Nov 2024 15:08:50 +0800 Subject: [PATCH 43/90] Add support for HM0360 --- CMakeLists.txt | 1 + Kconfig | 7 + driver/esp_camera.c | 6 + driver/include/sensor.h | 4 + driver/sensor.c | 1 + sensors/hm0360.c | 410 +++++++++++++++++ sensors/private_include/hm0360.h | 27 ++ sensors/private_include/hm0360_regs.h | 86 ++++ sensors/private_include/hm0360_settings.h | 509 ++++++++++++++++++++++ target/esp32s3/ll_cam.c | 2 +- 10 files changed, 1052 insertions(+), 1 deletion(-) create mode 100755 sensors/hm0360.c create mode 100755 sensors/private_include/hm0360.h create mode 100755 sensors/private_include/hm0360_regs.h create mode 100755 sensors/private_include/hm0360_settings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 493e4c5a9e..ff771cdf0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST sensors/sc031gs.c sensors/mega_ccm.c sensors/hm1055.c + sensors/hm0360.c ) list(APPEND priv_include_dirs diff --git a/Kconfig b/Kconfig index 991cbc38f2..11c25617e5 100755 --- a/Kconfig +++ b/Kconfig @@ -123,6 +123,13 @@ menu "Camera configuration" help Enable this option if you want to use the HM1055. Disable this option to save memory. + + config HM0360_SUPPORT + bool "Support HM0360 VGA" + default y + help + Enable this option if you want to use the HM0360. + Disable this option to save memory. config SCCB_I2C_PORT diff --git a/driver/esp_camera.c b/driver/esp_camera.c index 8985cee77d..eb3585e43c 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -75,6 +75,9 @@ #if CONFIG_HM1055_SUPPORT #include "hm1055.h" #endif +#if CONFIG_HM0360_SUPPORT +#include "hm0360.h" +#endif #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" @@ -155,6 +158,9 @@ static const sensor_func_t g_sensors[] = { #if CONFIG_HM1055_SUPPORT {hm1055_detect, hm1055_init}, #endif +#if CONFIG_HM0360_SUPPORT + {hm0360_detect, hm0360_init}, +#endif }; static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model) diff --git a/driver/include/sensor.h b/driver/include/sensor.h index 354a8c0dfd..eb02c7a366 100755 --- a/driver/include/sensor.h +++ b/driver/include/sensor.h @@ -33,6 +33,7 @@ typedef enum { SC031GS_PID = 0x0031, MEGA_CCM_PID =0x039E, HM1055_PID = 0x0955, + HM0360_PID = 0x0360 } camera_pid_t; typedef enum { @@ -52,6 +53,7 @@ typedef enum { CAMERA_SC031GS, CAMERA_MEGA_CCM, CAMERA_HM1055, + CAMERA_HM0360, CAMERA_MODEL_MAX, CAMERA_NONE, } camera_model_t; @@ -73,6 +75,7 @@ typedef enum { SC031GS_SCCB_ADDR = 0x30, MEGA_CCM_SCCB_ADDR = 0x1F, // 0x3E >> 1 HM1055_SCCB_ADDR = 0x24, + HM0360_SCCB_ADDR = 0x12, } camera_sccb_addr_t; typedef enum { @@ -85,6 +88,7 @@ typedef enum { PIXFORMAT_RAW, // RAW PIXFORMAT_RGB444, // 3BP2P/RGB444 PIXFORMAT_RGB555, // 3BP2P/RGB555 + PIXFORMAT_RAW8, // RAW 8-bit } pixformat_t; typedef enum { diff --git a/driver/sensor.c b/driver/sensor.c index 847d18b01d..453a15fca2 100644 --- a/driver/sensor.c +++ b/driver/sensor.c @@ -19,6 +19,7 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = { {CAMERA_SC031GS, "SC031GS", SC031GS_SCCB_ADDR, SC031GS_PID, FRAMESIZE_VGA, false}, {CAMERA_MEGA_CCM, "MEGA_CCM", MEGA_CCM_SCCB_ADDR, MEGA_CCM_PID, FRAMESIZE_5MP, true}, {CAMERA_HM1055, "HM1055", HM1055_SCCB_ADDR, HM1055_PID, FRAMESIZE_HD, false}, + {CAMERA_HM0360, "HM0360", HM0360_SCCB_ADDR, HM0360_PID, FRAMESIZE_VGA, false}, }; const resolution_info_t resolution[FRAMESIZE_INVALID] = { diff --git a/sensors/hm0360.c b/sensors/hm0360.c new file mode 100755 index 0000000000..20ec82d389 --- /dev/null +++ b/sensors/hm0360.c @@ -0,0 +1,410 @@ +/* + * + * HM0360 driver. + * + */ +#include +#include +#include +#include "sccb.h" +#include "xclk.h" +#include "hm0360.h" +#include "hm0360_regs.h" +#include "hm0360_settings.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#else +#include "esp_log.h" +static const char *TAG = "HM0360"; +#endif + +// #define REG_DEBUG_ON + +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); + +static int read_reg(uint8_t slv_addr, const uint16_t reg) +{ + int ret = SCCB_Read16(slv_addr, reg); +#ifdef REG_DEBUG_ON + if (ret < 0) + { + ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret); + } +#endif + return ret; +} + +static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask) +{ + return (read_reg(slv_addr, reg) & mask) == mask; +} + +static int read_reg16(uint8_t slv_addr, const uint16_t reg) +{ + int ret = 0, ret2 = 0; + ret = read_reg(slv_addr, reg); + if (ret >= 0) + { + ret = (ret & 0xFF) << 8; + ret2 = read_reg(slv_addr, reg + 1); + if (ret2 < 0) + { + ret = ret2; + } + else + { + ret |= ret2 & 0xFF; + } + } + return ret; +} + +static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value) +{ + int ret = 0; +#ifndef REG_DEBUG_ON + ret = SCCB_Write16(slv_addr, reg, value); +#else + int old_value = read_reg(slv_addr, reg); + if (old_value < 0) + { + return old_value; + } + if ((uint8_t)old_value != value) + { + ESP_LOGD(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value); + ret = SCCB_Write16(slv_addr, reg, value); + } + else + { + ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value); + ret = SCCB_Write16(slv_addr, reg, value); // maybe not? + } + if (ret < 0) + { + ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret); + } +#endif + return ret; +} + +static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value) +{ + int ret = 0; + uint8_t c_value, new_value; + ret = read_reg(slv_addr, reg); + if (ret < 0) + { + return ret; + } + c_value = ret; + new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset); + ret = write_reg(slv_addr, reg, new_value); + return ret; +} + +static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2]) +{ + int i = 0, ret = 0; + while (!ret && regs[i][0] != REGLIST_TAIL) + { + if (regs[i][0] == REG_DLY) + { + vTaskDelay(regs[i][1] / portTICK_PERIOD_MS); + } + else + { + ret = write_reg(slv_addr, regs[i][0], regs[i][1]); + } + i++; + } + return ret; +} + +static int write_reg16(uint8_t slv_addr, const uint16_t reg, uint16_t value) +{ + if (write_reg(slv_addr, reg, value >> 8) || write_reg(slv_addr, reg + 1, value)) + { + return -1; + } + return 0; +} + +static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value, uint16_t y_value) +{ + if (write_reg16(slv_addr, reg, x_value) || write_reg16(slv_addr, reg + 2, y_value)) + { + return -1; + } + return 0; +} + +#define write_reg_bits(slv_addr, reg, mask, enable) set_reg_bits(slv_addr, reg, 0, mask, (enable) ? (mask) : 0) + +static int set_ae_level(sensor_t *sensor, int level); + +static int reset(sensor_t *sensor) +{ + vTaskDelay(100 / portTICK_PERIOD_MS); + int ret = 0; + // Software Reset: clear all registers and reset them to their default values + ret = write_reg(sensor->slv_addr, SW_RESET, 0x00); + if (ret) + { + ESP_LOGE(TAG, "Software Reset FAILED!"); + return ret; + } + vTaskDelay(100 / portTICK_PERIOD_MS); + ret = write_regs(sensor->slv_addr, sensor_default_regs); + if (ret == 0) + { + ESP_LOGD(TAG, "Camera defaults loaded"); + vTaskDelay(100 / portTICK_PERIOD_MS); + set_ae_level(sensor, 0); + } + return ret; +} + +static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) +{ + int ret = 0; + return ret; +} + +static int set_framesize(sensor_t *sensor, framesize_t framesize) +{ + int ret = 0; + + return ret; +} + +static int set_hmirror(sensor_t *sensor, int enable) +{ + int ret = 0; + return ret; +} + +static int set_vflip(sensor_t *sensor, int enable) +{ + int ret = 0; + return ret; +} + +static int set_quality(sensor_t *sensor, int qs) +{ + return 0; +} + +static int set_colorbar(sensor_t *sensor, int enable) +{ + return 0; +} + +static int set_gain_ctrl(sensor_t *sensor, int enable) +{ + return 0; +} + +static int set_exposure_ctrl(sensor_t *sensor, int enable) +{ + int ret = 0; + + return ret; +} + +// real gain +static int set_agc_gain(sensor_t *sensor, int gain) +{ + int ret = 0; + return ret; +} +static int set_aec_value(sensor_t *sensor, int value) +{ + int ret = 0; + return ret; +} + +static int set_ae_level(sensor_t *sensor, int level) +{ + int ret = 0; + return ret; +} + +static int set_brightness(sensor_t *sensor, int level) +{ + int ret = 0; + return ret; +} + +static int get_reg(sensor_t *sensor, int reg, int mask) +{ + int ret = 0, ret2 = 0; + if (mask > 0xFF) + { + ret = read_reg16(sensor->slv_addr, reg); + if (ret >= 0 && mask > 0xFFFF) + { + ret2 = read_reg(sensor->slv_addr, reg + 2); + if (ret2 >= 0) + { + ret = (ret << 8) | ret2; + } + else + { + ret = ret2; + } + } + } + else + { + ret = read_reg(sensor->slv_addr, reg); + } + if (ret > 0) + { + ret &= mask; + } + return ret; +} + +static int set_reg(sensor_t *sensor, int reg, int mask, int value) +{ + int ret = 0, ret2 = 0; + if (mask > 0xFF) + { + ret = read_reg16(sensor->slv_addr, reg); + if (ret >= 0 && mask > 0xFFFF) + { + ret2 = read_reg(sensor->slv_addr, reg + 2); + if (ret2 >= 0) + { + ret = (ret << 8) | ret2; + } + else + { + ret = ret2; + } + } + } + else + { + ret = read_reg(sensor->slv_addr, reg); + } + if (ret < 0) + { + return ret; + } + value = (ret & ~mask) | (value & mask); + if (mask > 0xFFFF) + { + ret = write_reg16(sensor->slv_addr, reg, value >> 8); + if (ret >= 0) + { + ret = write_reg(sensor->slv_addr, reg + 2, value & 0xFF); + } + } + else if (mask > 0xFF) + { + ret = write_reg16(sensor->slv_addr, reg, value); + } + else + { + ret = write_reg(sensor->slv_addr, reg, value); + } + return ret; +} + +static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning) +{ + return 0; +} +static int set_xclk(sensor_t *sensor, int timer, int xclk) +{ + int ret = 0; + sensor->xclk_freq_hz = xclk * 1000000U; + ret = xclk_timer_conf(timer, sensor->xclk_freq_hz); + if (ret == 0) + { + ESP_LOGD(TAG, "Set xclk to %d", xclk); + } + return ret; +} + +static int init_status(sensor_t *sensor) +{ + sensor->status.brightness = 0; + sensor->status.contrast = 0; + sensor->status.saturation = 0; + // sensor->status.awb = check_reg_mask(sensor->slv_addr, AEWBCFG, 0x02); + sensor->status.agc = true; + // sensor->status.aec = check_reg_mask(sensor->slv_addr, AEWBCFG, 0x04); + // sensor->status.hmirror = check_reg_mask(sensor->slv_addr, RDCFG, 0x02); + // sensor->status.vflip = check_reg_mask(sensor->slv_addr, RDCFG, 0x01); + // sensor->status.lenc = check_reg_mask(sensor->slv_addr, ISPCTRL3, 0x40); + // sensor->status.awb_gain = read_reg(sensor->slv_addr, DGAIN); + // sensor->status.agc_gain = read_reg(sensor->slv_addr, AGAIN); + // sensor->status.aec_value = read_reg(sensor->slv_addr, AETARGM); + return 0; +} + +int hm0360_detect(int slv_addr, sensor_id_t *id) +{ + if (HM1055_SCCB_ADDR == slv_addr) + { + uint8_t h = SCCB_Read16(slv_addr, MODEL_ID_H); + uint8_t l = SCCB_Read16(slv_addr, MODEL_ID_L); + uint16_t PID = (h << 8) | l; + if (HM0360_PID == PID) + { + id->PID = PID; + id->VER = SCCB_Read16(slv_addr, SILICON_REV); + return PID; + } + else + { + ESP_LOGD(TAG, "Mismatch PID=0x%x", PID); + } + } + return 0; +} + +int hm0360_init(sensor_t *sensor) +{ + sensor->reset = reset; + sensor->set_pixformat = set_pixformat; + sensor->set_framesize = set_framesize; + sensor->set_contrast = NULL; + sensor->set_brightness = set_brightness; + sensor->set_saturation = NULL; + sensor->set_sharpness = NULL; + sensor->set_gainceiling = NULL; + sensor->set_quality = set_quality; + sensor->set_colorbar = set_colorbar; + sensor->set_gain_ctrl = set_gain_ctrl; + sensor->set_exposure_ctrl = set_exposure_ctrl; + sensor->set_whitebal = NULL; + sensor->set_hmirror = set_hmirror; + sensor->set_vflip = set_vflip; + sensor->init_status = init_status; + sensor->set_aec2 = NULL; + sensor->set_aec_value = set_aec_value; + sensor->set_special_effect = NULL; + sensor->set_wb_mode = NULL; + sensor->set_ae_level = set_ae_level; + sensor->set_dcw = NULL; + sensor->set_bpc = NULL; + sensor->set_wpc = NULL; + sensor->set_agc_gain = set_agc_gain; + sensor->set_raw_gma = NULL; + sensor->set_lenc = NULL; + sensor->set_denoise = NULL; + + sensor->get_reg = get_reg; + sensor->set_reg = set_reg; + sensor->set_res_raw = set_res_raw; + sensor->set_pll = NULL; + sensor->set_xclk = set_xclk; + return 0; +} diff --git a/sensors/private_include/hm0360.h b/sensors/private_include/hm0360.h new file mode 100755 index 0000000000..822ecab2d4 --- /dev/null +++ b/sensors/private_include/hm0360.h @@ -0,0 +1,27 @@ +/* + * HM0360 driver. + */ +#ifndef __HM0360_H__ +#define __HM0360_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 hm0360_detect(int slv_addr, sensor_id_t *id); + +/** + * @brief initialize sensor function pointers + * + * @param sensor pointer of sensor + * @return + * Always 0 + */ +int hm0360_init(sensor_t *sensor); + +#endif // __HM1055_H__ diff --git a/sensors/private_include/hm0360_regs.h b/sensors/private_include/hm0360_regs.h new file mode 100755 index 0000000000..314ae6c126 --- /dev/null +++ b/sensors/private_include/hm0360_regs.h @@ -0,0 +1,86 @@ +/* + * HM1055 register definitions. + */ +#ifndef __REG_REGS_H__ +#define __REG_REGS_H__ + +// Sensor ID +#define MODEL_ID_H 0x0000 +#define MODEL_ID_L 0x0001 +#define SILICON_REV 0x0002 +#define FRAME_COUNT_H 0x0005 +#define FRAME_COUNT_L 0x0006 +#define PIXEL_ODERDER 0x0007 + +// Snesor mode control +#define MODEL_SELECT 0x0100 +#define IMAGE_ORIENTATION 0x0101 +#define EMBEDDED_LINE_EN 0x0102 +#define SW_RESET 0x0103 +#define COMMAND_UPDATE 0x0104 + +// Sensor exposure gain control +#define INTERGRATION_H 0x0202 +#define INTERGRATION_L 0x0203 +#define ANALOG_GAIN 0x0205 +#define DIGITAL_GAIN_H 0x020E +#define DIGITAL_GAIN_L 0x020F + +// clock control +#define PLL1CFG 0x0300 +#define PLL2CFG 0x0301 +#define PLL3CFG 0x0302 + +// frame timming control +#define FRAME_LENGTH_LINES_H 0x0340 +#define FRAME_LENGTH_LINES_L 0x0341 +#define LINE_LENGTH_PCK_H 0x0342 +#define LINE_LENGTH_PCK_L 0x0343 + +// monochrome programming +#define MONO_MODE 0x0370 +#define MONO_MODE_ISP 0x0371 +#define MONO_MONIDE_SEL 0x0372 + +// sub-sampling / binning control +#define H_SUB 0x0380 +#define V_SUB 0x0381 +#define BINNING 0x0382 + +// test pattern control +#define TEST_PATTERN_MODE 0x0601 +#define TEST_DATA_BLUE_H 0x0602 +#define TEST_DATA_BLUE_L 0x0603 +#define TEST_DATA_GB_H 0x0604 +#define TEST_DATA_GB_L 0x0605 +#define TEST_DATA_GR_H 0x0605 +#define TEST_DATA_GR_L 0x0606 +#define TEST_DATA_RED_H 0x0608 +#define TEST_DATA_RED_L 0x0609 + +// black level control +#define BLC_TGT 0x1004 +#define BLC2_TGT 0x1009 + +// monochrome programming +#define MONO_CTRL 0x100A + +// VSYNC / HSYNC / pixel shift +#define OPFM_CTRL 0x1014 + +// automatic exposure programming +#define AE_CTRL 0x2000 +#define AE_CTRL2 0x2001 +#define CNT_ORG_H_H 0x2002 +#define CNT_ORG_H_L 0x2003 +#define CNT_ORG_V_H 0x2004 +#define CNT_ORG_V_L 0x2005 +#define CNT_ST_H_H 0x2006 +#define CNT_ST_H_L 0x2007 +#define CNT_ST_V_H 0x2008 +#define CNT_ST_V_L 0x2009 +#define CTRL_PG_SKIPCNT 0x200A +#define BV_WIN_WEIGHT_EN 0x200D + + +#endif //__REG_REGS_H__ diff --git a/sensors/private_include/hm0360_settings.h b/sensors/private_include/hm0360_settings.h new file mode 100755 index 0000000000..39f442a301 --- /dev/null +++ b/sensors/private_include/hm0360_settings.h @@ -0,0 +1,509 @@ + +#include + +#define REG_DLY 0xffff +#define REGLIST_TAIL 0xffff + +static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { + {0x0103, 0x00}, + // 0xffff,0xc0, + {0x3035, 0x01}, + {0x3126, 0x03}, + {0x3128, 0x57}, + {0x3142, 0x9F}, + {0x311C, 0x10}, + {0x3115, 0x42}, + {0x3116, 0x10}, + {0x3117, 0x0A}, + {0x310B, 0x10}, + {0x3031, 0x01}, + {0x312E, 0x00}, + {0x30D7, 0x00}, + {0x30DC, 0x00}, + {0x30E1, 0x00}, + {0x30E6, 0x00}, + {0x30EB, 0x00}, + {0x30F0, 0x00}, + {0x30F5, 0x00}, + {0x30FA, 0x00}, + {0x30FF, 0x00}, + {0x3104, 0x00}, + {0x30D8, 0xA7}, + {0x30DD, 0x27}, + {0x30E2, 0x27}, + {0x30E7, 0x27}, + {0x30EC, 0x27}, + {0x30F1, 0xA7}, + {0x30F6, 0x27}, + {0x30FB, 0x27}, + {0x3100, 0x27}, + {0x3105, 0x27}, + {0x30D9, 0x05}, + {0x30DE, 0x05}, + {0x30E3, 0x05}, + {0x30E8, 0x05}, + {0x30ED, 0x05}, + {0x30F2, 0x05}, + {0x30F7, 0x05}, + {0x30FC, 0x05}, + {0x3101, 0x05}, + {0x3106, 0x05}, + {0x30C4, 0x10}, + {0x30C5, 0x01}, + {0x30C6, 0xBF}, + {0x30CB, 0xFF}, + {0x30CC, 0xFF}, + {0x30CD, 0x7F}, + {0x30CE, 0x7F}, + {0x30D3, 0x01}, + {0x30D4, 0xFF}, + {0x311F, 0x0E}, + {0x3120, 0x0D}, + {0x3121, 0x0F}, + {0x3122, 0x00}, + {0x3147, 0x18}, + {0x314B, 0x01}, + {0x3149, 0x18}, + {0x3150, 0x50}, + {0x3152, 0x00}, + {0x3156, 0x2C}, + {0x3163, 0x1F}, + {0x3165, 0x7F}, + {0x312B, 0x41}, + {0x3113, 0xA0}, + {0x3114, 0x67}, + {0x317D, 0x02}, + {0x3160, 0x1F}, + {0x318C, 0x00}, + {0x315C, 0xE0}, + {0x311E, 0x0F}, + {0x30D5, 0x00}, + {0x30DA, 0x01}, + {0x30DF, 0x07}, + {0x30E4, 0x47}, + {0x30E9, 0x87}, + {0x30FD, 0x47}, + {0x3102, 0x87}, + {0x311D, 0x06}, + {0x3141, 0x2A}, + {0x315A, 0x0A}, + {0x30D6, 0x40}, + {0x30DB, 0x40}, + {0x30E0, 0x40}, + {0x30E5, 0x30}, + {0x30EA, 0x30}, + {0x30EF, 0x40}, + {0x30F4, 0x40}, + {0x30F9, 0x40}, + {0x30FE, 0x30}, + {0x3103, 0x30}, + {0x3164, 0x7F}, + {0x282A, 0x0F}, + {0x282E, 0x2F}, + {0x282B, 0x08}, + {0x312A, 0x11}, + {0x1000, 0x43}, + {0x1001, 0x80}, + {0x0350, 0xE0}, + {0x101D, 0xCF}, + {0x1021, 0x5D}, + {0x3500, 0x74}, + {0x3501, 0x0A}, + {0x3502, 0x77}, + {0x3503, 0x04}, + {0x3504, 0x14}, + {0x3505, 0x03}, + {0x3506, 0x00}, + {0x3507, 0x00}, + {0x3508, 0x00}, + {0x3509, 0x00}, + {0x350A, 0xFF}, + {0x350B, 0x00}, + {0x350D, 0x01}, + {0x350C, 0x00}, + {0x350F, 0x00}, + {0x3510, 0x01}, + {0x3513, 0x00}, + {0x351C, 0x00}, + {0x3514, 0x00}, + {0x3515, 0x01}, + {0x3516, 0x00}, + {0x3517, 0x02}, + {0x3518, 0x00}, + {0x3519, 0x7F}, + {0x351A, 0x00}, + {0x351B, 0x5F}, + {0x351D, 0x04}, + {0x351E, 0x10}, + {0x352A, 0x01}, + {0x352B, 0x04}, + {0x352C, 0x01}, + {0x352D, 0x38}, + {0x354B, 0x21}, + {0x354C, 0x01}, + {0x354D, 0xE0}, + {0x354E, 0xF0}, + {0x354F, 0x10}, + {0x3550, 0x10}, + {0x3551, 0x10}, + {0x3552, 0x20}, + {0x3553, 0x10}, + {0x3554, 0x01}, + {0x3555, 0x06}, + {0x3556, 0x0C}, + {0x3557, 0x12}, + {0x3558, 0x1C}, + {0x3559, 0x30}, + {0x3549, 0x04}, + {0x354A, 0x35}, + {0x355A, 0x74}, + {0x355B, 0x0A}, + {0x355C, 0x77}, + {0x355D, 0x04}, + {0x355E, 0x14}, + {0x355F, 0x03}, + {0x3560, 0x00}, + {0x3561, 0x01}, + {0x3562, 0x01}, + {0x3563, 0x00}, + {0x3564, 0xFF}, + {0x3565, 0x00}, + {0x3567, 0x01}, + {0x3566, 0x00}, + {0x3569, 0x00}, + {0x356A, 0x01}, + {0x356D, 0x00}, + {0x3576, 0x00}, + {0x356E, 0x00}, + {0x356F, 0x01}, + {0x3570, 0x00}, + {0x3571, 0x02}, + {0x3572, 0x00}, + {0x3573, 0x3F}, + {0x3574, 0x00}, + {0x3575, 0x2F}, + {0x3577, 0x04}, + {0x3578, 0x10}, + {0x3584, 0x01}, + {0x3585, 0x04}, + {0x3586, 0x01}, + {0x3587, 0x38}, + {0x3588, 0x02}, + {0x3589, 0x12}, + {0x358A, 0x04}, + {0x358B, 0x24}, + {0x358C, 0x06}, + {0x358D, 0x36}, + {0x35A5, 0x21}, + {0x35A6, 0x01}, + {0x35A7, 0xE0}, + {0x35A8, 0xF0}, + {0x35A9, 0x10}, + {0x35AA, 0x10}, + {0x35AB, 0x10}, + {0x35AC, 0x20}, + {0x35AD, 0x10}, + {0x35AE, 0x01}, + {0x35AF, 0x06}, + {0x35B0, 0x0C}, + {0x35B1, 0x12}, + {0x35B2, 0x1C}, + {0x35B3, 0x30}, + {0x35A3, 0x02}, + {0x35A4, 0x03}, + {0x3512, 0x3F}, + {0x351F, 0x04}, + {0x3520, 0x03}, + {0x3521, 0x00}, + {0x3523, 0x60}, + {0x3524, 0x08}, + {0x3525, 0x19}, + {0x3526, 0x08}, + {0x3527, 0x10}, + {0x356C, 0x3F}, + {0x3579, 0x04}, + {0x357A, 0x03}, + {0x357B, 0x00}, + {0x357D, 0x60}, + {0x357E, 0x08}, + {0x357F, 0x19}, + {0x3580, 0x08}, + {0x3581, 0x10}, + {0x2048, 0x00}, + {0x2049, 0x10}, + {0x204A, 0x40}, + {0x204E, 0x00}, + {0x204F, 0x38}, + {0x2050, 0xE0}, + {0x204B, 0x00}, + {0x204C, 0x08}, + {0x204D, 0x20}, + {0x2051, 0x00}, + {0x2052, 0x1C}, + {0x2053, 0x70}, + {0x2054, 0x00}, + {0x2055, 0x1A}, + {0x2056, 0xC0}, + {0x2057, 0x00}, + {0x2058, 0x06}, + {0x2059, 0xB0}, + {0x2080, 0x41}, + {0x2083, 0x01}, + {0x208D, 0x02}, + {0x208E, 0x08}, + {0x208F, 0x0D}, + {0x2090, 0x14}, + {0x2091, 0x1D}, + {0x2092, 0x30}, + {0x2093, 0x08}, + {0x2094, 0x0A}, + {0x2095, 0x0F}, + {0x2096, 0x14}, + {0x2097, 0x18}, + {0x2098, 0x20}, + {0x2099, 0x10}, + {0x209A, 0x00}, + {0x209B, 0x01}, + {0x209C, 0x01}, + {0x209D, 0x11}, + {0x209E, 0x06}, + {0x1030, 0x09}, + {0x1031, 0x12}, + {0x1032, 0x23}, + {0x1033, 0x31}, + {0x1034, 0x3E}, + {0x1035, 0x4B}, + {0x1036, 0x56}, + {0x1037, 0x5E}, + {0x1038, 0x65}, + {0x1039, 0x72}, + {0x103A, 0x7F}, + {0x103B, 0x8C}, + {0x103C, 0x98}, + {0x103D, 0xB2}, + {0x103E, 0xCC}, + {0x103F, 0xE6}, + {0x35B4, 0x74}, + {0x35B5, 0x0A}, + {0x35B6, 0x77}, + {0x35B7, 0x00}, + {0x35B8, 0x94}, + {0x35B9, 0x03}, + {0x35BA, 0x00}, + {0x35BB, 0x03}, + {0x35BD, 0x00}, + {0x35BE, 0xFF}, + {0x35BF, 0x00}, + {0x35C1, 0x01}, + {0x35C0, 0x01}, + {0x35C3, 0x00}, + {0x35C4, 0x00}, + {0x35C6, 0x3F}, + {0x35C7, 0x00}, + {0x35D0, 0x00}, + {0x35D3, 0x04}, + {0x35D7, 0x18}, + {0x35D8, 0x01}, + {0x35D9, 0x20}, + {0x35DA, 0x08}, + {0x35DB, 0x14}, + {0x35DC, 0x70}, + {0x35C8, 0x00}, + {0x35C9, 0x01}, + {0x35CA, 0x00}, + {0x35CB, 0x02}, + {0x35CC, 0x00}, + {0x35CD, 0x0F}, + {0x35CE, 0x00}, + {0x35CF, 0x0B}, + {0x35DE, 0x00}, + {0x35DF, 0x01}, + {0x35FD, 0x00}, + {0x35FE, 0x5E}, + {0x3024, 0x00}, + {0x3025, 0x12}, + {0x3026, 0x03}, + {0x3027, 0x81}, + {0x3028, 0x01}, + {0x3029, 0x00}, + {0x302A, 0x30}, + {0x2061, 0x00}, + {0x2062, 0x00}, + {0x2063, 0xC8}, + {0x1014, 0x00}, + {0x102F, 0x08}, + {0x309E, 0x05}, + {0x309F, 0x02}, + {0x30A0, 0x02}, + {0x30A1, 0x00}, + {0x30A2, 0x08}, + {0x30A3, 0x00}, + {0x30A4, 0x20}, + {0x30A5, 0x04}, + {0x30A6, 0x02}, + {0x30A7, 0x02}, + {0x30A8, 0x01}, + {0x30B0, 0x03}, + {0x3112, 0x04}, + {0x311A, 0x30}, + {0x2800, 0x00}, + {0x0370, 0x00}, + {0x0371, 0x00}, + {0x0372, 0x01}, + {0x100A, 0x05}, + {0x2590, 0x01}, + {0x0104, 0x01}, + {0x0100, 0x01}, + // 0xFFFF,0xC0, + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_HD[][2] = { + {0x0006, 0x00}, + {0x000D, 0x00}, + {0x000E, 0x00}, + {0x0122, 0xFB}, + {0x0125, 0xDF}, + {0x0126, 0x70}, + {0x05E4, 0x08}, + {0x05E5, 0x00}, + {0x05E6, 0x07}, + {0x05E7, 0x05}, + {0x05E8, 0x08}, + {0x05E9, 0x00}, + {0x05EA, 0xD7}, + {0x05EB, 0x02}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA[][2] = { + {0x0006, 0x10}, + {0x000D, 0x00}, + {0x000E, 0x00}, + {0x0122, 0xEB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XC1}, + {0x05E1, 0x00}, + {0x05E2, 0xC1}, + {0x05E3, 0x00}, + {0x05E4, 0x03}, + {0x05E5, 0x00}, + {0x05E6, 0x82}, + {0x05E7, 0x02}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xE3}, + {0x05EB, 0x01}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_CIF[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x72}, + {0x05E0, 0XC0}, + {0x05E1, 0x00}, + {0x05E2, 0x8F}, + {0x05E3, 0x00}, + {0x05E4, 0x11}, + {0x05E5, 0x00}, + {0x05E6, 0xA0}, + {0x05E7, 0x01}, + {0x05E8, 0x08}, + {0x05E9, 0x00}, + {0x05EA, 0x2F}, + {0x05EB, 0x01}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XBE}, + {0x05E1, 0x00}, + {0x05E2, 0xBE}, + {0x05E3, 0x00}, + {0x05E4, 0x3A}, + {0x05E5, 0x00}, + {0x05E6, 0x79}, + {0x05E7, 0x01}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xF3}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QCIF[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x72}, + {0x05E0, 0X52}, + {0x05E1, 0x01}, + {0x05E2, 0x38}, + {0x05E3, 0x01}, + {0x05E4, 0x22}, + {0x05E5, 0x00}, + {0x05E6, 0xD1}, + {0x05E7, 0x00}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0x93}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_240X240[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0XBE}, + {0x05E1, 0x00}, + {0x05E2, 0xBE}, + {0x05E3, 0x00}, + {0x05E4, 0x62}, + {0x05E5, 0x00}, + {0x05E6, 0x51}, + {0x05E7, 0x01}, + {0x05E8, 0x04}, + {0x05E9, 0x00}, + {0x05EA, 0xF3}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QQVGA[][2] = { + {0x0006, 0x00}, + {0x000D, 0x01}, + {0x000E, 0x11}, + {0x0122, 0xFB}, + {0x0125, 0xFF}, + {0x0126, 0x70}, + {0x05E0, 0X78}, + {0x05E1, 0x01}, + {0x05E2, 0x78}, + {0x05E3, 0x01}, + {0x05E4, 0x1E}, + {0x05E5, 0x00}, + {0x05E6, 0xBD}, + {0x05E7, 0x00}, + {0x05E8, 0x03}, + {0x05E9, 0x00}, + {0x05EA, 0x7A}, + {0x05EB, 0x00}, + {REGLIST_TAIL, 0x00}, +}; \ No newline at end of file diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index 71caf14f69..f5c1aa0e7a 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -557,7 +557,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, 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 || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID || sensor_pid == GC0308_PID) { + if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID || sensor_pid == SC031GS_PID || sensor_pid == BF20A6_PID || sensor_pid == GC0308_PID || sensor_pid == HM0360_PID) { cam->in_bytes_per_pixel = 1; // camera sends Y8 } else { cam->in_bytes_per_pixel = 2; // camera sends YU/YV From 7fad3cdf9bc4ae0a2205bca9f5699de0618db44e Mon Sep 17 00:00:00 2001 From: Lesords <2385342343@qq.com> Date: Fri, 29 Nov 2024 20:22:55 +0800 Subject: [PATCH 44/90] perf: improve some interface functions of hm0360 camera and discarded useless interfaces --- sensors/hm0360.c | 209 ++++++++++------ sensors/private_include/hm0360_regs.h | 74 +++++- sensors/private_include/hm0360_settings.h | 284 ++++++++++++---------- 3 files changed, 368 insertions(+), 199 deletions(-) diff --git a/sensors/hm0360.c b/sensors/hm0360.c index 20ec82d389..1c96022e3e 100755 --- a/sensors/hm0360.c +++ b/sensors/hm0360.c @@ -144,8 +144,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 set_ae_level(sensor_t *sensor, int level); - static int reset(sensor_t *sensor) { vTaskDelay(100 / portTICK_PERIOD_MS); @@ -163,7 +161,6 @@ static int reset(sensor_t *sensor) { ESP_LOGD(TAG, "Camera defaults loaded"); vTaskDelay(100 / portTICK_PERIOD_MS); - set_ae_level(sensor, 0); } return ret; } @@ -171,6 +168,16 @@ static int reset(sensor_t *sensor) static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) { int ret = 0; + sensor->pixformat = pixformat; + + switch (pixformat) { + case PIXFORMAT_GRAYSCALE: + break; + default: + ESP_LOGE(TAG, "Only support GRAYSCALE"); + return -1; + } + return ret; } @@ -178,65 +185,107 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) { int ret = 0; + sensor->status.framesize = framesize; + ret = write_regs(sensor->slv_addr, sensor_default_regs); + + if (framesize == FRAMESIZE_QQVGA) + { + ESP_LOGI(TAG, "Set FRAMESIZE_QQVGA"); + ret |= write_regs(sensor->slv_addr, sensor_framesize_QQVGA); + ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 1); + } + else if (framesize == FRAMESIZE_QVGA) + { + ESP_LOGI(TAG, "Set FRAMESIZE_QVGA"); + ret |= write_regs(sensor->slv_addr, sensor_framesize_QVGA); + ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 1); + } + else if (framesize == FRAMESIZE_VGA) + { + ESP_LOGI(TAG, "Set FRAMESIZE_VGA"); + ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 0); + } + else + { + ESP_LOGI(TAG, "Dont suppost this size, Set FRAMESIZE_VGA"); + ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 0); + } + + if (ret == 0) + { + _set_pll(sensor, 0, 0, 0, 0, 0, 0, 0, 0); + ret |= write_reg(sensor->slv_addr, 0x0104, 0x01); + } + return ret; } static int set_hmirror(sensor_t *sensor, int enable) { - int ret = 0; - return ret; + if (set_reg_bits(sensor->slv_addr, 0x0101, 0, 0x01, enable)) { + return -1; + } + + ESP_LOGD(TAG, "Set h-mirror to: %d", enable); + + return 0; } static int set_vflip(sensor_t *sensor, int enable) { - int ret = 0; - return ret; -} + if (set_reg_bits(sensor->slv_addr, 0x0101, 1, 0x01, enable)) { + return -1; + } + + ESP_LOGD(TAG, "Set v-flip to: %d", enable); -static int set_quality(sensor_t *sensor, int qs) -{ return 0; } static int set_colorbar(sensor_t *sensor, int enable) { - return 0; -} + if (set_reg_bits(sensor->slv_addr, 0x0601, 0, 0x01, enable)) { + return -1; + } + + ESP_LOGD(TAG, "Set color-bar to: %d", enable); -static int set_gain_ctrl(sensor_t *sensor, int enable) -{ return 0; } static int set_exposure_ctrl(sensor_t *sensor, int enable) { - int ret = 0; - - return ret; -} + if (set_reg_bits(sensor->slv_addr, 0x2000, 0, 0x01, enable)) { + return -1; + } -// real gain -static int set_agc_gain(sensor_t *sensor, int gain) -{ - int ret = 0; - return ret; -} -static int set_aec_value(sensor_t *sensor, int value) -{ - int ret = 0; - return ret; -} + ESP_LOGD(TAG, "Set exposure to: %d", enable); -static int set_ae_level(sensor_t *sensor, int level) -{ - int ret = 0; - return ret; + return 0; } static int set_brightness(sensor_t *sensor, int level) { - int ret = 0; - return ret; + uint8_t ae_mean; + + switch (level) { + case 0: + ae_mean = 60; + break; + case 1: + ae_mean = 80; + break; + case 2: + ae_mean = 100; + break; + case 3: + ae_mean = 127; + break; + default: + ae_mean = 80; + } + + return write_reg(sensor->slv_addr, AE_TARGET_MEAN, ae_mean); } static int get_reg(sensor_t *sensor, int reg, int mask) @@ -316,10 +365,6 @@ static int set_reg(sensor_t *sensor, int reg, int mask, int value) return ret; } -static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning) -{ - return 0; -} static int set_xclk(sensor_t *sensor, int timer, int xclk) { int ret = 0; @@ -332,20 +377,40 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } +static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div) +{ + uint8_t value = 0; + uint8_t pll_cfg = 0; + + if (sensor->xclk_freq_hz <= 6000000) + { + value = 0x03; + } + else if (sensor->xclk_freq_hz <= 12000000) + { + value = 0x02; + } + else if (sensor->xclk_freq_hz <= 18000000) + { + value = 0x01; + } + else + { // max is 48000000 + value = 0x00; + } + + pll_cfg = read_reg(sensor->slv_addr, PLL1CFG); + return write_reg(sensor->slv_addr, PLL1CFG, (pll_cfg & 0xFC) | value); +} + +static int set_dummy(sensor_t *sensor, int val) +{ + ESP_LOGW(TAG, "Unsupported"); + return -1; +} + static int init_status(sensor_t *sensor) { - sensor->status.brightness = 0; - sensor->status.contrast = 0; - sensor->status.saturation = 0; - // sensor->status.awb = check_reg_mask(sensor->slv_addr, AEWBCFG, 0x02); - sensor->status.agc = true; - // sensor->status.aec = check_reg_mask(sensor->slv_addr, AEWBCFG, 0x04); - // sensor->status.hmirror = check_reg_mask(sensor->slv_addr, RDCFG, 0x02); - // sensor->status.vflip = check_reg_mask(sensor->slv_addr, RDCFG, 0x01); - // sensor->status.lenc = check_reg_mask(sensor->slv_addr, ISPCTRL3, 0x40); - // sensor->status.awb_gain = read_reg(sensor->slv_addr, DGAIN); - // sensor->status.agc_gain = read_reg(sensor->slv_addr, AGAIN); - // sensor->status.aec_value = read_reg(sensor->slv_addr, AETARGM); return 0; } @@ -375,36 +440,36 @@ int hm0360_init(sensor_t *sensor) sensor->reset = reset; sensor->set_pixformat = set_pixformat; sensor->set_framesize = set_framesize; - sensor->set_contrast = NULL; + sensor->set_contrast = set_dummy; sensor->set_brightness = set_brightness; - sensor->set_saturation = NULL; - sensor->set_sharpness = NULL; - sensor->set_gainceiling = NULL; - sensor->set_quality = set_quality; + sensor->set_saturation = set_dummy; + sensor->set_sharpness = set_dummy; + sensor->set_gainceiling = set_dummy; + sensor->set_quality = set_dummy; sensor->set_colorbar = set_colorbar; - sensor->set_gain_ctrl = set_gain_ctrl; + sensor->set_gain_ctrl = set_dummy; sensor->set_exposure_ctrl = set_exposure_ctrl; - sensor->set_whitebal = NULL; + sensor->set_whitebal = set_dummy; sensor->set_hmirror = set_hmirror; sensor->set_vflip = set_vflip; sensor->init_status = init_status; - sensor->set_aec2 = NULL; - sensor->set_aec_value = set_aec_value; - sensor->set_special_effect = NULL; - sensor->set_wb_mode = NULL; - sensor->set_ae_level = set_ae_level; - sensor->set_dcw = NULL; - sensor->set_bpc = NULL; - sensor->set_wpc = NULL; - sensor->set_agc_gain = set_agc_gain; - sensor->set_raw_gma = NULL; - sensor->set_lenc = NULL; - sensor->set_denoise = NULL; + sensor->set_aec2 = set_dummy; + sensor->set_aec_value = set_dummy; + sensor->set_special_effect = set_dummy; + sensor->set_wb_mode = set_dummy; + sensor->set_ae_level = set_dummy; + sensor->set_dcw = set_dummy; + sensor->set_bpc = set_dummy; + sensor->set_wpc = set_dummy; + sensor->set_agc_gain = set_dummy; + sensor->set_raw_gma = set_dummy; + sensor->set_lenc = set_dummy; + sensor->set_denoise = set_dummy; sensor->get_reg = get_reg; sensor->set_reg = set_reg; - sensor->set_res_raw = set_res_raw; - sensor->set_pll = NULL; + sensor->set_res_raw = NULL; + sensor->set_pll = _set_pll; sensor->set_xclk = set_xclk; return 0; } diff --git a/sensors/private_include/hm0360_regs.h b/sensors/private_include/hm0360_regs.h index 314ae6c126..9017484889 100755 --- a/sensors/private_include/hm0360_regs.h +++ b/sensors/private_include/hm0360_regs.h @@ -20,8 +20,8 @@ #define COMMAND_UPDATE 0x0104 // Sensor exposure gain control -#define INTERGRATION_H 0x0202 -#define INTERGRATION_L 0x0203 +#define INTEGRATION_H 0x0202 +#define INTEGRATION_L 0x0203 #define ANALOG_GAIN 0x0205 #define DIGITAL_GAIN_H 0x020E #define DIGITAL_GAIN_L 0x020F @@ -40,12 +40,12 @@ // monochrome programming #define MONO_MODE 0x0370 #define MONO_MODE_ISP 0x0371 -#define MONO_MONIDE_SEL 0x0372 +#define MONO_MODE_SEL 0x0372 // sub-sampling / binning control #define H_SUB 0x0380 #define V_SUB 0x0381 -#define BINNING 0x0382 +#define BINNING_MODE 0x0382 // test pattern control #define TEST_PATTERN_MODE 0x0601 @@ -68,9 +68,28 @@ // VSYNC / HSYNC / pixel shift #define OPFM_CTRL 0x1014 +// Tone mapping registers +#define CMPRS_CTRL 0x102F +#define CMPRS_01 0x1030 +#define CMPRS_02 0x1031 +#define CMPRS_03 0x1032 +#define CMPRS_04 0x1033 +#define CMPRS_05 0x1034 +#define CMPRS_06 0x1035 +#define CMPRS_07 0x1036 +#define CMPRS_08 0x1037 +#define CMPRS_09 0x1038 +#define CMPRS_10 0x1039 +#define CMPRS_11 0x103A +#define CMPRS_12 0x103B +#define CMPRS_13 0x103C +#define CMPRS_14 0x103D +#define CMPRS_15 0x103E +#define CMPRS_16 0x103F + // automatic exposure programming #define AE_CTRL 0x2000 -#define AE_CTRL2 0x2001 +#define AE_CTRL1 0x2001 #define CNT_ORG_H_H 0x2002 #define CNT_ORG_H_L 0x2003 #define CNT_ORG_V_H 0x2004 @@ -81,6 +100,51 @@ #define CNT_ST_V_L 0x2009 #define CTRL_PG_SKIPCNT 0x200A #define BV_WIN_WEIGHT_EN 0x200D +#define MAX_INTG_H 0x2029 +#define MAX_INTG_L 0x202A +#define MAX_AGAIN 0x202B +#define MAX_DGAIN_H 0x202C +#define MAX_DGAIN_L 0x202D +#define MIN_INTG 0x202E +#define MIN_AGAIN 0x202F +#define MIN_DGAIN 0x2030 +#define T_DAMPING 0x2031 +#define N_DAMPING 0x2032 +#define ALC_TH 0x2033 +#define AE_TARGET_MEAN 0x2034 +#define AE_MIN_MEAN 0x2035 +#define AE_TARGET_ZONE 0x2036 +#define CONVERGE_IN_TH 0x2037 +#define CONVERGE_OUT_TH 0x2038 + +// Interrupt control +#define PULSE_MODE 0x2061 +#define PULSE_TH_H 0x2062 +#define PULSE_TH_L 0x2063 +#define INT_INDIC 0x2064 +#define INT_CLEAR 0x2065 + +// Motion detection control +#define MD_CTRL 0x2080 +#define ROI_START_END_V 0x2081 +#define ROI_START_END_H 0x2082 +#define MD_TH_MIN 0x2083 +#define MD_TH_STR_L 0x2084 +#define MD_TH_STR_H 0x2085 +#define MD_LIGHT_COEF 0x2099 +#define MD_BLOCK_NUM_TH 0x209B +#define MD_LATENCY 0x209C +#define MD_LATENCY_TH 0x209D +#define MD_CTRL1 0x209E + +// Context switch control registers +#define PMU_CFG_3 0x3024 +#define PMU_CFG_4 0x3025 + +// Operation mode control +#define WIN_MODE 0x3030 +// IO and clock control +#define PAD_REGISTER_07 0x3112 #endif //__REG_REGS_H__ diff --git a/sensors/private_include/hm0360_settings.h b/sensors/private_include/hm0360_settings.h index 39f442a301..942e9b17da 100755 --- a/sensors/private_include/hm0360_settings.h +++ b/sensors/private_include/hm0360_settings.h @@ -2,11 +2,11 @@ #include #define REG_DLY 0xffff -#define REGLIST_TAIL 0xffff +#define REGLIST_TAIL 0x0000 static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x0103, 0x00}, - // 0xffff,0xc0, + {REG_DLY, 100}, {0x3035, 0x01}, {0x3126, 0x03}, {0x3128, 0x57}, @@ -38,19 +38,19 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x30FB, 0x27}, {0x3100, 0x27}, {0x3105, 0x27}, - {0x30D9, 0x05}, + {0x30D9, 0x00}, // 220215 {0x30DE, 0x05}, {0x30E3, 0x05}, {0x30E8, 0x05}, {0x30ED, 0x05}, - {0x30F2, 0x05}, + {0x30F2, 0x00}, // 220215 {0x30F7, 0x05}, {0x30FC, 0x05}, {0x3101, 0x05}, {0x3106, 0x05}, {0x30C4, 0x10}, {0x30C5, 0x01}, - {0x30C6, 0xBF}, + {0x30C6, 0x2F}, // 220215 {0x30CB, 0xFF}, {0x30CC, 0xFF}, {0x30CD, 0x7F}, @@ -63,7 +63,7 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x3122, 0x00}, {0x3147, 0x18}, {0x314B, 0x01}, - {0x3149, 0x18}, + {0x3149, 0x28}, {0x3150, 0x50}, {0x3152, 0x00}, {0x3156, 0x2C}, @@ -107,6 +107,8 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x0350, 0xE0}, {0x101D, 0xCF}, {0x1021, 0x5D}, + + // setA VGA {0x3500, 0x74}, {0x3501, 0x0A}, {0x3502, 0x77}, @@ -156,6 +158,8 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x3559, 0x30}, {0x3549, 0x04}, {0x354A, 0x35}, + + // setB QVGA {0x355A, 0x74}, {0x355B, 0x0A}, {0x355C, 0x77}, @@ -211,6 +215,8 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x35B3, 0x30}, {0x35A3, 0x02}, {0x35A4, 0x03}, + + // AE_tuning {0x3512, 0x3F}, {0x351F, 0x04}, {0x3520, 0x03}, @@ -247,6 +253,8 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x2057, 0x00}, {0x2058, 0x06}, {0x2059, 0xB0}, + + // MD tuning {0x2080, 0x41}, {0x2083, 0x01}, {0x208D, 0x02}, @@ -267,22 +275,26 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x209C, 0x01}, {0x209D, 0x11}, {0x209E, 0x06}, - {0x1030, 0x09}, - {0x1031, 0x12}, - {0x1032, 0x23}, - {0x1033, 0x31}, - {0x1034, 0x3E}, - {0x1035, 0x4B}, - {0x1036, 0x56}, - {0x1037, 0x5E}, - {0x1038, 0x65}, - {0x1039, 0x72}, - {0x103A, 0x7F}, - {0x103B, 0x8C}, - {0x103C, 0x98}, - {0x103D, 0xB2}, - {0x103E, 0xCC}, - {0x103F, 0xE6}, + + // Tone mapping + {0x1030, 0x04}, + {0x1031, 0x08}, + {0x1032, 0x10}, + {0x1033, 0x18}, + {0x1034, 0x20}, + {0x1035, 0x28}, + {0x1036, 0x30}, + {0x1037, 0x38}, + {0x1038, 0x40}, + {0x1039, 0x50}, + {0x103A, 0x60}, + {0x103B, 0x70}, + {0x103C, 0x80}, + {0x103D, 0xA0}, + {0x103E, 0xC0}, + {0x103F, 0xE0}, + + // RESERVED {0x35B4, 0x74}, {0x35B5, 0x0A}, {0x35B6, 0x77}, @@ -320,6 +332,8 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x35DF, 0x01}, {0x35FD, 0x00}, {0x35FE, 0x5E}, + + // RESERVED {0x3024, 0x00}, {0x3025, 0x12}, {0x3026, 0x03}, @@ -327,9 +341,13 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x3028, 0x01}, {0x3029, 0x00}, {0x302A, 0x30}, + + // Interrupt {0x2061, 0x00}, {0x2062, 0x00}, {0x2063, 0xC8}, + + // Parallel I/F {0x1014, 0x00}, {0x102F, 0x08}, {0x309E, 0x05}, @@ -345,8 +363,12 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x30A8, 0x01}, {0x30B0, 0x03}, {0x3112, 0x04}, - {0x311A, 0x30}, + {0x311A, 0x30}, //31:bypass internal ldo 30:internal LDO + + // MIPI {0x2800, 0x00}, + + // Enable BPC} {0x0370, 0x00}, {0x0371, 0x00}, {0x0372, 0x01}, @@ -354,26 +376,8 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { {0x2590, 0x01}, {0x0104, 0x01}, {0x0100, 0x01}, - // 0xFFFF,0xC0, - {REGLIST_TAIL, 0x00}, // tail -}; -static const DRAM_ATTR uint16_t sensor_framesize_HD[][2] = { - {0x0006, 0x00}, - {0x000D, 0x00}, - {0x000E, 0x00}, - {0x0122, 0xFB}, - {0x0125, 0xDF}, - {0x0126, 0x70}, - {0x05E4, 0x08}, - {0x05E5, 0x00}, - {0x05E6, 0x07}, - {0x05E7, 0x05}, - {0x05E8, 0x08}, - {0x05E9, 0x00}, - {0x05EA, 0xD7}, - {0x05EB, 0x02}, - {REGLIST_TAIL, 0x00}, + {REGLIST_TAIL, 0x00}, // tail }; static const DRAM_ATTR uint16_t sensor_framesize_VGA[][2] = { @@ -398,69 +402,65 @@ static const DRAM_ATTR uint16_t sensor_framesize_VGA[][2] = { {REGLIST_TAIL, 0x00}, }; -static const DRAM_ATTR uint16_t sensor_framesize_CIF[][2] = { - {0x0006, 0x00}, - {0x000D, 0x01}, - {0x000E, 0x11}, - {0x0122, 0xFB}, - {0x0125, 0xFF}, - {0x0126, 0x72}, - {0x05E0, 0XC0}, - {0x05E1, 0x00}, - {0x05E2, 0x8F}, - {0x05E3, 0x00}, - {0x05E4, 0x11}, - {0x05E5, 0x00}, - {0x05E6, 0xA0}, - {0x05E7, 0x01}, - {0x05E8, 0x08}, - {0x05E9, 0x00}, - {0x05EA, 0x2F}, - {0x05EB, 0x01}, - {REGLIST_TAIL, 0x00}, -}; - static const DRAM_ATTR uint16_t sensor_framesize_QVGA[][2] = { - {0x0006, 0x00}, - {0x000D, 0x01}, - {0x000E, 0x11}, - {0x0122, 0xFB}, - {0x0125, 0xFF}, - {0x0126, 0x70}, - {0x05E0, 0XBE}, - {0x05E1, 0x00}, - {0x05E2, 0xBE}, - {0x05E3, 0x00}, - {0x05E4, 0x3A}, - {0x05E5, 0x00}, - {0x05E6, 0x79}, - {0x05E7, 0x01}, - {0x05E8, 0x04}, - {0x05E9, 0x00}, - {0x05EA, 0xF3}, - {0x05EB, 0x00}, - {REGLIST_TAIL, 0x00}, -}; + {0x355A, 0x74}, // vt_sys_d + {0x355B, 0x0A}, // op_sys_d + {0x355C, 0x77}, // mclk_div_d + {0x355D, 0x01}, + {0x355E, 0x1C}, + {0x355F, 0x03}, + {0x3560, 0x00}, + {0x3561, 0x01}, + {0x3562, 0x01}, + {0x3563, 0x00}, + {0x3564, 0xFF}, + {0x3565, 0x00}, + {0x3567, 0x01}, + {0x3566, 0x00}, + {0x3569, 0x00}, + {0x356A, 0x01}, + {0x356C, 0x7F}, + {0x356D, 0x00}, + {0x3576, 0x00}, + {0x3579, 0x03}, // Max Again + {0x356E, 0x00}, // AE cnt org H HB + {0x356F, 0x01}, // AE cnt org H LB + {0x3570, 0x00}, // AE cnt org V HB + {0x3571, 0x02}, // AE cnt org V LB + {0x3572, 0x00}, // AE cnt st H HB + {0x3573, 0x3F}, // AE cnt st H LB + {0x3574, 0x00}, // AE cnt st V HB + {0x3575, 0x2F}, // AE cnt st V LB + {0x3577, 0x04}, + {0x3578, 0x24}, + {0x3584, 0x01}, + {0x3585, 0x04}, + {0x3586, 0x01}, + {0x3587, 0x38}, + {0x3588, 0x02}, // FR stage 1 H + {0x3589, 0x12}, // FR stage 1 L + {0x358A, 0x04}, // FR stage 2 H + {0x358B, 0x24}, // FR stage 2 L + {0x358C, 0x06}, // FR stage 3 H + {0x358D, 0x36}, // FR stage 3 L + {0x35A5, 0x21}, // [7:1]MD_light_coef [0] MD enable + {0x35A6, 0x01}, // MD_block_num_th + {0x35A7, 0xE0}, // [7:4]md_roi_end_v [3:0]md_roi_start_v + {0x35A8, 0xF0}, // [7:4]md_roi_end_h [3:0]md_roi_start_h + {0x35A9, 0x10}, // [6:0] md_th_str_HCG + {0x35AA, 0x10}, // [5:0] md_th_str_LCG + {0x35AB, 0x10}, // [5:0] md_th_str_HDR + {0x35AC, 0x20}, // md_flick_skip_th_adj_N + {0x35AD, 0x10}, // md_flick_skip_th_adj_P + {0x35AE, 0x01}, + {0x35AF, 0x06}, + {0x35B0, 0x0C}, + {0x35B1, 0x12}, + {0x35BC, 0x1C}, + {0x35B3, 0x30}, + {0x35A3, 0x02}, // full trigger H + {0x35A4, 0x03}, // full trigger L -static const DRAM_ATTR uint16_t sensor_framesize_QCIF[][2] = { - {0x0006, 0x00}, - {0x000D, 0x01}, - {0x000E, 0x11}, - {0x0122, 0xFB}, - {0x0125, 0xFF}, - {0x0126, 0x72}, - {0x05E0, 0X52}, - {0x05E1, 0x01}, - {0x05E2, 0x38}, - {0x05E3, 0x01}, - {0x05E4, 0x22}, - {0x05E5, 0x00}, - {0x05E6, 0xD1}, - {0x05E7, 0x00}, - {0x05E8, 0x04}, - {0x05E9, 0x00}, - {0x05EA, 0x93}, - {0x05EB, 0x00}, {REGLIST_TAIL, 0x00}, }; @@ -487,23 +487,63 @@ static const DRAM_ATTR uint16_t sensor_framesize_240X240[][2] = { }; static const DRAM_ATTR uint16_t sensor_framesize_QQVGA[][2] = { - {0x0006, 0x00}, - {0x000D, 0x01}, - {0x000E, 0x11}, - {0x0122, 0xFB}, - {0x0125, 0xFF}, - {0x0126, 0x70}, - {0x05E0, 0X78}, - {0x05E1, 0x01}, - {0x05E2, 0x78}, - {0x05E3, 0x01}, - {0x05E4, 0x1E}, - {0x05E5, 0x00}, - {0x05E6, 0xBD}, - {0x05E7, 0x00}, - {0x05E8, 0x03}, - {0x05E9, 0x00}, - {0x05EA, 0x7A}, - {0x05EB, 0x00}, + {0x355A, 0x74}, // vt_sys_d + {0x355B, 0x0A}, // op_sys_d + {0x355C, 0x77}, // mclk_div_d + {0x355D, 0x00}, + {0x355E, 0x8E}, + {0x355F, 0x03}, + {0x3560, 0x00}, + {0x3561, 0x02}, + {0x3562, 0x02}, + {0x3563, 0x00}, + {0x3564, 0xFF}, + {0x3565, 0x00}, + {0x3567, 0x01}, + {0x3566, 0x00}, + {0x3569, 0x00}, + {0x356A, 0x01}, + {0x356C, 0x7F}, + {0x356D, 0x00}, + {0x3576, 0x00}, + {0x3579, 0x03}, // Max Again + {0x356E, 0x00}, // AE cnt org H HB + {0x356F, 0x01}, // AE cnt org H LB + {0x3570, 0x00}, // AE cnt org V HB + {0x3571, 0x02}, // AE cnt org V LB + {0x3572, 0x00}, // AE cnt st H HB + {0x3573, 0x1F}, // AE cnt st H LB + {0x3574, 0x00}, // AE cnt st V HB + {0x3575, 0x17}, // AE cnt st V LB + {0x3577, 0x04}, + {0x3578, 0x24}, + {0x3584, 0x01}, + {0x3585, 0x04}, + {0x3586, 0x01}, + {0x3587, 0x38}, + {0x3588, 0x02}, // FR stage 1 H + {0x3589, 0x12}, // FR stage 1 L + {0x358A, 0x04}, // FR stage 2 H + {0x358B, 0x24}, // FR stage 2 L + {0x358C, 0x06}, // FR stage 3 H + {0x358D, 0x36}, // FR stage 3 L + {0x35A5, 0x21}, // [7:1]MD_light_coef [0] MD enable + {0x35A6, 0x01}, // MD_block_num_th + {0x35A7, 0xD0}, // [7:4]md_roi_end_v [3:0]md_roi_start_v + {0x35A8, 0xF0}, // [7:4]md_roi_end_h [3:0]md_roi_start_h + {0x35A9, 0x10}, // [6:0] md_th_str_HCG + {0x35AA, 0x10}, // [5:0] md_th_str_LCG + {0x35AB, 0x10}, // [5:0] md_th_str_HDR + {0x35AC, 0x20}, // md_flick_skip_th_adj_N + {0x35AD, 0x10}, // md_flick_skip_th_adj_P + {0x35AE, 0x01}, + {0x35AF, 0x06}, + {0x35B0, 0x0C}, + {0x35B1, 0x12}, + {0x35B2, 0x1C}, + {0x35B3, 0x30}, + {0x35A3, 0x00}, // full trigger H + {0x35A4, 0xEA}, // full trigger L + {REGLIST_TAIL, 0x00}, -}; \ No newline at end of file +}; From cf09e5a5d6119d9b5acec8eada1750a338e27e32 Mon Sep 17 00:00:00 2001 From: Lesords <2385342343@qq.com> Date: Wed, 4 Dec 2024 14:42:19 +0800 Subject: [PATCH 45/90] style: update code format --- sensors/hm0360.c | 167 +++++++++++++++++------------------------------ 1 file changed, 61 insertions(+), 106 deletions(-) diff --git a/sensors/hm0360.c b/sensors/hm0360.c index 1c96022e3e..a3acea47f8 100755 --- a/sensors/hm0360.c +++ b/sensors/hm0360.c @@ -29,8 +29,7 @@ static int read_reg(uint8_t slv_addr, const uint16_t reg) { int ret = SCCB_Read16(slv_addr, reg); #ifdef REG_DEBUG_ON - if (ret < 0) - { + if (ret < 0) { ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret); } #endif @@ -45,20 +44,18 @@ static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask) static int read_reg16(uint8_t slv_addr, const uint16_t reg) { int ret = 0, ret2 = 0; + ret = read_reg(slv_addr, reg); - if (ret >= 0) - { + if (ret >= 0) { ret = (ret & 0xFF) << 8; ret2 = read_reg(slv_addr, reg + 1); - if (ret2 < 0) - { + if (ret2 < 0) { ret = ret2; - } - else - { + } else { ret |= ret2 & 0xFF; } } + return ret; } @@ -69,22 +66,18 @@ static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value) ret = SCCB_Write16(slv_addr, reg, value); #else int old_value = read_reg(slv_addr, reg); - if (old_value < 0) - { + if (old_value < 0) { return old_value; } - if ((uint8_t)old_value != value) - { + + if ((uint8_t)old_value != value) { ESP_LOGD(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value); ret = SCCB_Write16(slv_addr, reg, value); - } - else - { + } else { ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value); ret = SCCB_Write16(slv_addr, reg, value); // maybe not? } - if (ret < 0) - { + if (ret < 0) { ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret); } #endif @@ -95,11 +88,12 @@ static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t { int ret = 0; uint8_t c_value, new_value; + ret = read_reg(slv_addr, reg); - if (ret < 0) - { + 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); @@ -109,25 +103,22 @@ static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t 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) - { + + while (!ret && regs[i][0] != REGLIST_TAIL) { + if (regs[i][0] == REG_DLY) { vTaskDelay(regs[i][1] / portTICK_PERIOD_MS); - } - else - { + } else { ret = write_reg(slv_addr, regs[i][0], regs[i][1]); } i++; } + return ret; } static int write_reg16(uint8_t slv_addr, const uint16_t reg, uint16_t value) { - if (write_reg(slv_addr, reg, value >> 8) || write_reg(slv_addr, reg + 1, value)) - { + if (write_reg(slv_addr, reg, value >> 8) || write_reg(slv_addr, reg + 1, value)) { return -1; } return 0; @@ -135,8 +126,7 @@ static int write_reg16(uint8_t slv_addr, const uint16_t reg, uint16_t value) static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value, uint16_t y_value) { - if (write_reg16(slv_addr, reg, x_value) || write_reg16(slv_addr, reg + 2, y_value)) - { + if (write_reg16(slv_addr, reg, x_value) || write_reg16(slv_addr, reg + 2, y_value)) { return -1; } return 0; @@ -148,20 +138,21 @@ static int reset(sensor_t *sensor) { vTaskDelay(100 / portTICK_PERIOD_MS); int ret = 0; + // Software Reset: clear all registers and reset them to their default values ret = write_reg(sensor->slv_addr, SW_RESET, 0x00); - if (ret) - { + if (ret) { ESP_LOGE(TAG, "Software Reset FAILED!"); return ret; } + vTaskDelay(100 / portTICK_PERIOD_MS); ret = write_regs(sensor->slv_addr, sensor_default_regs); - if (ret == 0) - { + if (ret == 0) { ESP_LOGD(TAG, "Camera defaults loaded"); vTaskDelay(100 / portTICK_PERIOD_MS); } + return ret; } @@ -188,31 +179,23 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) sensor->status.framesize = framesize; ret = write_regs(sensor->slv_addr, sensor_default_regs); - if (framesize == FRAMESIZE_QQVGA) - { + if (framesize == FRAMESIZE_QQVGA) { ESP_LOGI(TAG, "Set FRAMESIZE_QQVGA"); ret |= write_regs(sensor->slv_addr, sensor_framesize_QQVGA); ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 1); - } - else if (framesize == FRAMESIZE_QVGA) - { + } else if (framesize == FRAMESIZE_QVGA) { ESP_LOGI(TAG, "Set FRAMESIZE_QVGA"); ret |= write_regs(sensor->slv_addr, sensor_framesize_QVGA); ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 1); - } - else if (framesize == FRAMESIZE_VGA) - { + } else if (framesize == FRAMESIZE_VGA) { ESP_LOGI(TAG, "Set FRAMESIZE_VGA"); ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 0); - } - else - { + } else { ESP_LOGI(TAG, "Dont suppost this size, Set FRAMESIZE_VGA"); ret |= set_reg_bits(sensor->slv_addr, 0x3024, 0, 0x01, 0); } - if (ret == 0) - { + if (ret == 0) { _set_pll(sensor, 0, 0, 0, 0, 0, 0, 0, 0); ret |= write_reg(sensor->slv_addr, 0x0104, 0x01); } @@ -291,28 +274,22 @@ static int set_brightness(sensor_t *sensor, int level) static int get_reg(sensor_t *sensor, int reg, int mask) { int ret = 0, ret2 = 0; - if (mask > 0xFF) - { + + if (mask > 0xFF) { ret = read_reg16(sensor->slv_addr, reg); - if (ret >= 0 && mask > 0xFFFF) - { + if (ret >= 0 && mask > 0xFFFF) { ret2 = read_reg(sensor->slv_addr, reg + 2); - if (ret2 >= 0) - { + if (ret2 >= 0) { ret = (ret << 8) | ret2; - } - else - { + } else { ret = ret2; } } - } - else - { + } else { ret = read_reg(sensor->slv_addr, reg); } - if (ret > 0) - { + + if (ret > 0) { ret &= mask; } return ret; @@ -321,47 +298,37 @@ static int get_reg(sensor_t *sensor, int reg, int mask) static int set_reg(sensor_t *sensor, int reg, int mask, int value) { int ret = 0, ret2 = 0; - if (mask > 0xFF) - { + + if (mask > 0xFF) { ret = read_reg16(sensor->slv_addr, reg); - if (ret >= 0 && mask > 0xFFFF) - { + if (ret >= 0 && mask > 0xFFFF) { ret2 = read_reg(sensor->slv_addr, reg + 2); - if (ret2 >= 0) - { + if (ret2 >= 0) { ret = (ret << 8) | ret2; - } - else - { + } else { ret = ret2; } } - } - else - { + } else { ret = read_reg(sensor->slv_addr, reg); } - if (ret < 0) - { + + if (ret < 0) { return ret; } + value = (ret & ~mask) | (value & mask); - if (mask > 0xFFFF) - { + if (mask > 0xFFFF) { ret = write_reg16(sensor->slv_addr, reg, value >> 8); - if (ret >= 0) - { + if (ret >= 0) { ret = write_reg(sensor->slv_addr, reg + 2, value & 0xFF); } - } - else if (mask > 0xFF) - { + } else if (mask > 0xFF) { ret = write_reg16(sensor->slv_addr, reg, value); - } - else - { + } else { ret = write_reg(sensor->slv_addr, reg, value); } + return ret; } @@ -370,8 +337,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) int ret = 0; sensor->xclk_freq_hz = xclk * 1000000U; ret = xclk_timer_conf(timer, sensor->xclk_freq_hz); - if (ret == 0) - { + if (ret == 0) { ESP_LOGD(TAG, "Set xclk to %d", xclk); } return ret; @@ -382,20 +348,13 @@ static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, i uint8_t value = 0; uint8_t pll_cfg = 0; - if (sensor->xclk_freq_hz <= 6000000) - { + if (sensor->xclk_freq_hz <= 6000000) { value = 0x03; - } - else if (sensor->xclk_freq_hz <= 12000000) - { + } else if (sensor->xclk_freq_hz <= 12000000) { value = 0x02; - } - else if (sensor->xclk_freq_hz <= 18000000) - { + } else if (sensor->xclk_freq_hz <= 18000000) { value = 0x01; - } - else - { // max is 48000000 + } else { // max is 48000000 value = 0x00; } @@ -416,19 +375,15 @@ static int init_status(sensor_t *sensor) int hm0360_detect(int slv_addr, sensor_id_t *id) { - if (HM1055_SCCB_ADDR == slv_addr) - { + if (HM1055_SCCB_ADDR == slv_addr) { uint8_t h = SCCB_Read16(slv_addr, MODEL_ID_H); uint8_t l = SCCB_Read16(slv_addr, MODEL_ID_L); uint16_t PID = (h << 8) | l; - if (HM0360_PID == PID) - { + if (HM0360_PID == PID) { id->PID = PID; id->VER = SCCB_Read16(slv_addr, SILICON_REV); return PID; - } - else - { + } else { ESP_LOGD(TAG, "Mismatch PID=0x%x", PID); } } From 3591f25efb355461c2ab015f1d71604b51d51855 Mon Sep 17 00:00:00 2001 From: Lesords <2385342343@qq.com> Date: Wed, 4 Dec 2024 15:43:40 +0800 Subject: [PATCH 46/90] docs: update sensor information for hm0360 and hm1055 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 092f893f8a..def7aa1fff 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi | SC101IOT| 1280 x 720 | color | YUV/YCbCr422
Raw RGB | 1/4.2" | | SC030IOT| 640 x 480 | color | YUV/YCbCr422
RAW Bayer | 1/6.5" | | SC031GS | 640 x 480 | monochrome | RAW MONO
Grayscale | 1/6" | +| HM0360 | 656 x 496 | monochrome | RAW MONO
Grayscale | 1/6" | +| HM1055 | 1280 x 720 | color | 8/10-bit Raw
YUV/YCbCr422
RGB565/555/444 | 1/6" | ## Important to Remember From 59f1d083ea9c949251e786b29781a927afd6911e Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Wed, 12 Feb 2025 13:37:36 +0100 Subject: [PATCH 47/90] Initial esp_camera_available_frames implementation --- driver/cam_hal.c | 5 +++++ driver/esp_camera.c | 7 +++++++ driver/include/esp_camera.h | 5 +++++ driver/private_include/cam_hal.h | 2 ++ 4 files changed, 19 insertions(+) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 86b430aa72..4daaee7264 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -535,3 +535,8 @@ void cam_give_all(void) { cam_obj->frames[x].en = 1; } } + +bool cam_get_available_frames(void) +{ + return 0 < uxQueueMessagesWaiting(cam_obj->frame_buffer_queue); +} \ No newline at end of file diff --git a/driver/esp_camera.c b/driver/esp_camera.c index 7c693a6957..20a0367749 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -490,3 +490,10 @@ void esp_camera_return_all(void) { cam_give_all(); } +bool esp_camera_available_frames(void) +{ + if (s_state == NULL) { + return false; + } + return cam_get_available_frames(); +} diff --git a/driver/include/esp_camera.h b/driver/include/esp_camera.h index ff459cbd65..01af844d79 100755 --- a/driver/include/esp_camera.h +++ b/driver/include/esp_camera.h @@ -240,6 +240,11 @@ esp_err_t esp_camera_load_from_nvs(const char *key); */ void esp_camera_return_all(void); +/** + * @brief Check if there are available frames to be immediately acquired + */ +bool esp_camera_available_frames(void); + #ifdef __cplusplus } diff --git a/driver/private_include/cam_hal.h b/driver/private_include/cam_hal.h index cd0d3aaeec..b6f812ffce 100644 --- a/driver/private_include/cam_hal.h +++ b/driver/private_include/cam_hal.h @@ -57,6 +57,8 @@ void cam_give(camera_fb_t *dma_buffer); void cam_give_all(void); +bool cam_get_available_frames(void); + #ifdef __cplusplus } #endif From 80747678ab3895756e8938a531a25d4fd3d8f02f Mon Sep 17 00:00:00 2001 From: Peter Hoddie Date: Thu, 17 Apr 2025 17:44:36 -0700 Subject: [PATCH 48/90] contrast fixes - update sensor->status.contrast when setting contrast (consistent with most other settings like hmirror) - initialize sensor->status.contrast to the actual default (50) rather than 0, which is invalid --- sensors/gc0308.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sensors/gc0308.c b/sensors/gc0308.c index 09d333183a..337626cc14 100644 --- a/sensors/gc0308.c +++ b/sensors/gc0308.c @@ -279,7 +279,8 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) static int set_contrast(sensor_t *sensor, int contrast) { - if (contrast != 0) { + if (contrast > 0) { + sensor->status.contrast = contrast; write_reg(sensor->slv_addr, 0xfe, 0x00); write_reg(sensor->slv_addr, 0xb3, contrast); } @@ -370,7 +371,7 @@ static int init_status(sensor_t *sensor) { write_reg(sensor->slv_addr, 0xfe, 0x00); sensor->status.brightness = 0; - sensor->status.contrast = 0; + sensor->status.contrast = 50; sensor->status.saturation = 0; sensor->status.sharpness = 0; sensor->status.denoise = 0; From 6b684158003d9a6f771d6b3a82f570b1dee11694 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Fri, 2 May 2025 16:51:00 +0200 Subject: [PATCH 49/90] feat: Use esp_jpeg component for decoding --- .gitignore | 3 +- CMakeLists.txt | 15 +- conversions/esp_jpg_decode.c | 140 ---- conversions/include/esp_jpg_decode.h | 43 -- conversions/include/img_converters.h | 12 +- conversions/to_bmp.c | 225 ++----- idf_component.yml | 4 + target/jpeg_include/tjpgd.h | 99 --- target/tjpgd.c | 970 --------------------------- test/test_camera.c | 2 +- 10 files changed, 89 insertions(+), 1424 deletions(-) delete mode 100644 conversions/esp_jpg_decode.c delete mode 100644 conversions/include/esp_jpg_decode.h delete mode 100644 target/jpeg_include/tjpgd.h delete mode 100755 target/tjpgd.c diff --git a/.gitignore b/.gitignore index f39d5fda1b..77f2db2e6e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ **/build **/sdkconfig **/sdkconfig.old -**/dependencies.lock \ No newline at end of file +**/dependencies.lock +**/managed_components/** \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ebc992074..36cefe0f74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,6 @@ set(srcs conversions/to_jpg.cpp conversions/to_bmp.c conversions/jpge.cpp - conversions/esp_jpg_decode.c ) set(priv_include_dirs @@ -61,7 +60,6 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST list(APPEND srcs target/xclk.c target/esp32s2/ll_cam.c - target/tjpgd.c ) list(APPEND priv_include_dirs @@ -83,7 +81,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST endif() # include the SCCB I2C driver - # this uses either the legacy I2C API or the newwer version from IDF v5.4 + # this uses either the legacy I2C API or the newer version from IDF v5.4 # as this features a method to obtain the I2C driver from a port number if (idf_version VERSION_GREATER_EQUAL "5.4") list(APPEND srcs driver/sccb-ng.c) @@ -93,17 +91,6 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST endif() -# CONFIG_ESP_ROM_HAS_JPEG_DECODE is available from IDF v4.4 but -# previous IDF supported chips already support JPEG decoder, hence okay to use this -if(idf_version VERSION_GREATER_EQUAL "4.4" AND NOT CONFIG_ESP_ROM_HAS_JPEG_DECODE) - list(APPEND srcs - target/tjpgd.c - ) - list(APPEND priv_include_dirs - target/jpeg_include/ - ) -endif() - idf_component_register( SRCS ${srcs} INCLUDE_DIRS ${include_dirs} diff --git a/conversions/esp_jpg_decode.c b/conversions/esp_jpg_decode.c deleted file mode 100644 index cd9a3558bd..0000000000 --- a/conversions/esp_jpg_decode.c +++ /dev/null @@ -1,140 +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 "esp_jpg_decode.h" - -#include "esp_system.h" -#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+ -#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 -#include "esp32/rom/tjpgd.h" -#elif CONFIG_IDF_TARGET_ESP32S3 -#include "esp32s3/rom/tjpgd.h" -#elif CONFIG_IDF_TARGET_ESP32C3 -#include "esp32c3/rom/tjpgd.h" -#elif CONFIG_ESP_ROM_HAS_JPEG_DECODE // available since IDF 4.4 -#include "rom/tjpgd.h" // latest IDFs have `rom/` includes available -#else -#include "tjpgd.h" // using software decoder -#endif -#else // ESP32 Before IDF 4.0 -#include "rom/tjpgd.h" -#endif - -#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#define TAG "" -#else -#include "esp_log.h" -static const char* TAG = "esp_jpg_decode"; -#endif - -typedef struct { - jpg_scale_t scale; - jpg_reader_cb reader; - jpg_writer_cb writer; - void * arg; - size_t len; - size_t index; -} esp_jpg_decoder_t; - -static const char * jd_errors[] = { - "Succeeded", - "Interrupted by output function", - "Device error or wrong termination of input stream", - "Insufficient memory pool for the image", - "Insufficient stream input buffer", - "Parameter error", - "Data format error", - "Right format but not supported", - "Not supported JPEG standard" -}; - -static unsigned int _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect) -{ - uint16_t x = rect->left; - uint16_t y = rect->top; - uint16_t w = rect->right + 1 - x; - uint16_t h = rect->bottom + 1 - y; - uint8_t *data = (uint8_t *)bitmap; - - esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device; - - if (jpeg->writer) { - return jpeg->writer(jpeg->arg, x, y, w, h, data); - } - return 0; -} - -static unsigned int _jpg_read(JDEC *decoder, uint8_t *buf, unsigned int len) -{ - esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device; - if (jpeg->len && len > (jpeg->len - jpeg->index)) { - len = jpeg->len - jpeg->index; - } - if (len) { - len = jpeg->reader(jpeg->arg, jpeg->index, buf, len); - if (!len) { - ESP_LOGE(TAG, "Read Fail at %u/%u", jpeg->index, jpeg->len); - } - jpeg->index += len; - } - return len; -} - -esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg) -{ - static uint8_t work[3100]; - JDEC decoder; - esp_jpg_decoder_t jpeg; - - jpeg.len = len; - jpeg.reader = reader; - jpeg.writer = writer; - jpeg.arg = arg; - jpeg.scale = scale; - jpeg.index = 0; - - JRESULT jres = jd_prepare(&decoder, _jpg_read, work, 3100, &jpeg); - if(jres != JDR_OK){ - ESP_LOGE(TAG, "JPG Header Parse Failed! %s", jd_errors[jres]); - return ESP_FAIL; - } - - uint16_t output_width = decoder.width / (1 << (uint8_t)(jpeg.scale)); - uint16_t output_height = decoder.height / (1 << (uint8_t)(jpeg.scale)); - - //output start - if (!writer(arg, 0, 0, output_width, output_height, NULL)) { - ESP_LOGE(TAG, "JPG Writer Start Failed!"); - return ESP_FAIL; - } - //output write - jres = jd_decomp(&decoder, _jpg_write, (uint8_t)jpeg.scale); - //output end - if (!writer(arg, output_width, output_height, output_width, output_height, NULL)) { - ESP_LOGE(TAG, "JPG Writer End Failed!"); - return ESP_FAIL; - } - - if (jres != JDR_OK) { - ESP_LOGE(TAG, "JPG Decompression Failed! %s", jd_errors[jres]); - return ESP_FAIL; - } - //check if all data has been consumed. - if (len && jpeg.index < len) { - _jpg_read(&decoder, NULL, len - jpeg.index); - } - - return ESP_OK; -} - diff --git a/conversions/include/esp_jpg_decode.h b/conversions/include/esp_jpg_decode.h deleted file mode 100644 index f13536edf4..0000000000 --- a/conversions/include/esp_jpg_decode.h +++ /dev/null @@ -1,43 +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. -#ifndef _ESP_JPG_DECODE_H_ -#define _ESP_JPG_DECODE_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include "esp_err.h" - -typedef enum { - JPG_SCALE_NONE, - JPG_SCALE_2X, - JPG_SCALE_4X, - JPG_SCALE_8X, - JPG_SCALE_MAX = JPG_SCALE_8X -} jpg_scale_t; - -typedef size_t (* jpg_reader_cb)(void * arg, size_t index, uint8_t *buf, size_t len); -typedef bool (* jpg_writer_cb)(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data); - -esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg); - -#ifdef __cplusplus -} -#endif - -#endif /* _ESP_JPG_DECODE_H_ */ diff --git a/conversions/include/img_converters.h b/conversions/include/img_converters.h index f736200a9b..6e71ce93fd 100644 --- a/conversions/include/img_converters.h +++ b/conversions/include/img_converters.h @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2025 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. @@ -22,7 +22,7 @@ extern "C" { #include #include #include "esp_camera.h" -#include "esp_jpg_decode.h" +#include "jpeg_decoder.h" typedef size_t (* jpg_out_cb)(void * arg, size_t index, const void* data, size_t len); @@ -121,7 +121,13 @@ 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); +// Macros for backwards compatibility +#define JPG_SCALE_NONE JPEG_IMAGE_SCALE_0 +#define JPG_SCALE_2X JPEG_IMAGE_SCALE_1_2 +#define JPG_SCALE_4X JPEG_IMAGE_SCALE_1_4 +#define JPG_SCALE_8X JPEG_IMAGE_SCALE_1_8 +#define JPG_SCALE_MAX JPEG_IMAGE_SCALE_1_8 +bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, esp_jpeg_image_scale_t scale); #ifdef __cplusplus } diff --git a/conversions/to_bmp.c b/conversions/to_bmp.c index 9f016aa97d..8c1a6f05a0 100644 --- a/conversions/to_bmp.c +++ b/conversions/to_bmp.c @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2025 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. @@ -18,7 +18,7 @@ #include "esp_heap_caps.h" #include "yuv.h" #include "sdkconfig.h" -#include "esp_jpg_decode.h" +#include "jpeg_decoder.h" #include "esp_system.h" @@ -31,6 +31,7 @@ static const char* TAG = "to_bmp"; #endif static const int BMP_HEADER_LEN = 54; +static uint8_t work[3100]; // 3.1kB for JPEG decoder, static for legacy reasons typedef struct { uint32_t filesize; @@ -49,14 +50,6 @@ typedef struct { uint32_t mostimpcolor; } bmp_header_t; -typedef struct { - uint16_t width; - uint16_t height; - uint16_t data_offset; - const uint8_t *input; - uint8_t *output; -} rgb_jpg_decoder; - static void *_malloc(size_t size) { // check if SPIRAM is enabled and allocate on SPIRAM if allocatable @@ -67,178 +60,104 @@ static void *_malloc(size_t size) return malloc(size); } -//output buffer and image width -static bool _rgb_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data) +static bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, esp_jpeg_image_scale_t scale) { - 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){ - size_t out_size = (w*h*3)+jpeg->data_offset; - jpeg->output = (uint8_t *)_malloc(out_size); - if(!jpeg->output){ - ESP_LOGE(TAG, "_malloc failed! %zu", out_size); - return false; - } - } - } else { - //write end - } - return true; - } - - size_t jw = jpeg->width*3; - size_t t = y * jw; - size_t b = t + (h * jw); - size_t l = x * 3; - uint8_t *out = jpeg->output+jpeg->data_offset; - uint8_t *o = out; - size_t iy, ix; - - w = w * 3; - - for(iy=t; iywidth = w; - jpeg->height = h; - //if output is null, this is BMP - if(!jpeg->output){ - size_t out_size = (w*h*3)+jpeg->data_offset; - jpeg->output = (uint8_t *)_malloc(out_size); - if(!jpeg->output){ - ESP_LOGE(TAG, "_malloc failed! %zu", out_size); - 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; + esp_jpeg_image_cfg_t jpeg_cfg = { + .indata = (uint8_t *)src, + .indata_size = src_len, + .outbuf = out, + .outbuf_size = UINT32_MAX, // @todo: this is very bold assumption, keeping this like this for now, not to break existing code + .out_format = JPEG_IMAGE_FORMAT_RGB565, + .out_scale = scale, + .flags.swap_color_bytes = 0, + .advanced.working_buffer = work, + .advanced.working_buffer_size = sizeof(work), + }; + + esp_jpeg_image_output_t output_img = {}; + + if(esp_jpeg_decode(&jpeg_cfg, &output_img) != ESP_OK){ + return false; } return true; } -//input buffer -static unsigned int _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len) -{ - rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg; - if(buf) { - memcpy(buf, jpeg->input + index, len); - } - return len; -} - -static bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale) +bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_len) { - rgb_jpg_decoder jpeg; - jpeg.width = 0; - jpeg.height = 0; - jpeg.input = src; - jpeg.output = out; - jpeg.data_offset = 0; - - if(esp_jpg_decode(src_len, scale, _jpg_read, _rgb_write, (void*)&jpeg) != ESP_OK){ + esp_jpeg_image_cfg_t jpeg_cfg = { + .indata = (uint8_t *)src, + .indata_size = src_len, + .out_format = JPEG_IMAGE_FORMAT_RGB888, + .out_scale = JPEG_IMAGE_SCALE_0, + .flags.swap_color_bytes = 0, + .advanced.working_buffer = work, + .advanced.working_buffer_size = sizeof(work), + }; + + esp_jpeg_image_output_t output_img = {}; + if (esp_jpeg_get_image_info(&jpeg_cfg, &output_img) != ESP_OK) { + ESP_LOGE(TAG, "Failed to get image info"); return false; } - return true; -} - -bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale) -{ - rgb_jpg_decoder jpeg; - jpeg.width = 0; - jpeg.height = 0; - jpeg.input = src; - jpeg.output = out; - jpeg.data_offset = 0; - - if(esp_jpg_decode(src_len, scale, _jpg_read, _rgb565_write, (void*)&jpeg) != ESP_OK){ + + // @todo here we allocate memory and we assume that the user will free it + // this is not the best way to do it, but we need to keep the API + // compatible with the previous version + const size_t output_size = output_img.output_len + BMP_HEADER_LEN; + uint8_t *output = _malloc(output_size); + if (!output) { + ESP_LOGE(TAG, "Failed to allocate output buffer"); return false; } - return true; -} -bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_len) -{ - - rgb_jpg_decoder jpeg; - jpeg.width = 0; - jpeg.height = 0; - jpeg.input = src; - jpeg.output = NULL; - jpeg.data_offset = BMP_HEADER_LEN; - - if(esp_jpg_decode(src_len, JPG_SCALE_NONE, _jpg_read, _rgb_write, (void*)&jpeg) != ESP_OK){ + // Start writing decoded data after the BMP header + jpeg_cfg.outbuf = output + BMP_HEADER_LEN; + jpeg_cfg.outbuf_size = output_img.output_len; + if(esp_jpeg_decode(&jpeg_cfg, &output_img) != ESP_OK){ return false; } - size_t output_size = jpeg.width*jpeg.height*3; - - jpeg.output[0] = 'B'; - jpeg.output[1] = 'M'; - bmp_header_t * bitmap = (bmp_header_t*)&jpeg.output[2]; + output[0] = 'B'; + output[1] = 'M'; + bmp_header_t * bitmap = (bmp_header_t*)&output[2]; bitmap->reserved = 0; - bitmap->filesize = output_size+BMP_HEADER_LEN; + bitmap->filesize = output_size; bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN; bitmap->dibheadersize = 40; - bitmap->width = jpeg.width; - bitmap->height = -jpeg.height;//set negative for top to bottom + bitmap->width = output_img.width; + bitmap->height = -output_img.height; //set negative for top to bottom bitmap->planes = 1; bitmap->bitsperpixel = 24; bitmap->compression = 0; - bitmap->imagesize = output_size; + bitmap->imagesize = output_img.output_len; bitmap->ypixelpermeter = 0x0B13 ; //2835 , 72 DPI bitmap->xpixelpermeter = 0x0B13 ; //2835 , 72 DPI bitmap->numcolorspallette = 0; bitmap->mostimpcolor = 0; - *out = jpeg.output; - *out_len = output_size+BMP_HEADER_LEN; + *out = output; + *out_len = output_size; return true; } @@ -247,7 +166,7 @@ bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint { int pix_count = 0; if(format == PIXFORMAT_JPEG) { - return jpg2rgb888(src_buf, src_len, rgb_buf, JPG_SCALE_NONE); + return jpg2rgb888(src_buf, src_len, rgb_buf, JPEG_IMAGE_SCALE_0); } else if(format == PIXFORMAT_RGB888) { memcpy(rgb_buf, src_buf, src_len); } else if(format == PIXFORMAT_RGB565) { diff --git a/idf_component.yml b/idf_component.yml index d51d3691b7..8b454fcd01 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -3,3 +3,7 @@ url: https://github.com/espressif/esp32-camera issues: https://github.com/espressif/esp32-camera/issues documentation: https://github.com/espressif/esp32-camera/tree/main/README.md repository: https://github.com/espressif/esp32-camera.git +dependencies: + esp_jpeg: + version: "^1.3.0" + public: true diff --git a/target/jpeg_include/tjpgd.h b/target/jpeg_include/tjpgd.h deleted file mode 100644 index 31fbc97cce..0000000000 --- a/target/jpeg_include/tjpgd.h +++ /dev/null @@ -1,99 +0,0 @@ -/*----------------------------------------------------------------------------/ -/ 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/target/tjpgd.c b/target/tjpgd.c deleted file mode 100755 index 5a983c4c7f..0000000000 --- a/target/tjpgd.c +++ /dev/null @@ -1,970 +0,0 @@ -/*----------------------------------------------------------------------------/ -/ 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/test/test_camera.c b/test/test_camera.c index 41a89b022f..c080509d3a 100644 --- a/test/test_camera.c +++ b/test/test_camera.c @@ -382,7 +382,7 @@ static void print_rgb888_img(uint8_t *img, int width, int height) static void tjpgd_decode_rgb565(uint8_t *mjpegbuffer, uint32_t size, uint8_t *outbuffer) { - jpg2rgb565(mjpegbuffer, size, outbuffer, JPG_SCALE_NONE); + jpg2rgb565(mjpegbuffer, size, outbuffer, JPEG_IMAGE_SCALE_0); } static void tjpgd_decode_rgb888(uint8_t *mjpegbuffer, uint32_t size, uint8_t *outbuffer) From b659d4c925984f3068dfc614db31395731667641 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Tue, 6 May 2025 15:17:07 +0200 Subject: [PATCH 50/90] fix(test): Update test app for latest esp-idf --- test/CMakeLists.txt | 3 ++- test/test_camera.c | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a8c3d16b75..1e68ddcc1e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,5 @@ idf_component_register(SRC_DIRS . PRIV_INCLUDE_DIRS . - PRIV_REQUIRES test_utils esp32-camera nvs_flash + PRIV_REQUIRES test_utils esp32-camera nvs_flash mbedtls esp_timer EMBED_TXTFILES pictures/testimg.jpeg pictures/test_outside.jpeg pictures/test_inside.jpeg) +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/test/test_camera.c b/test/test_camera.c index c080509d3a..c78dd1474b 100644 --- a/test/test_camera.c +++ b/test/test_camera.c @@ -7,6 +7,7 @@ #include #include "esp_log.h" #include "driver/i2c.h" +#include "esp_timer.h" #include "esp_camera.h" @@ -18,6 +19,8 @@ #define BOARD_CAMERA_MODEL_ESP32_S3_EYE 1 #endif +#define portTICK_RATE_MS portTICK_PERIOD_MS + // WROVER-KIT PIN Map #if BOARD_WROVER_KIT From cda34fe48d6c87e85a90460209a78498bb0cc0a5 Mon Sep 17 00:00:00 2001 From: Paulo Prates <147115682+PauloPrates-20@users.noreply.github.com> Date: Sat, 10 May 2025 01:23:40 -0300 Subject: [PATCH 51/90] GOOUU TECH ESP32S3 board pin mapping Added pin mapping definition for the chinese GOOUU esp32-s3 cam boards --- examples/camera_example/main/take_picture.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/examples/camera_example/main/take_picture.c b/examples/camera_example/main/take_picture.c index 15147407e1..f9df7f7b1a 100644 --- a/examples/camera_example/main/take_picture.c +++ b/examples/camera_example/main/take_picture.c @@ -8,6 +8,7 @@ // #define BOARD_WROVER_KIT // #define BOARD_ESP32CAM_AITHINKER // #define BOARD_ESP32S3_WROOM +// #define BOARD_ESP32S3_GOOUUU /** * 2. Kconfig setup @@ -112,6 +113,25 @@ #define CAM_PIN_D6 17 #define CAM_PIN_D7 16 #endif +// ESP32S3 (GOOUU TECH) +#ifdef BOARD_ESP32S3_GOOUUU +#define CAM_PIN_PWDN -1 +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_VSYNC 6 +#define CAM_PIN_HREF 7 +#define CAM_PIN_PCLK 13 +#define CAM_PIN_XCLK 15 +#define CAM_PIN_SIOD 4 +#define CAM_PIN_SIOC 5 +#define CAM_PIN_D0 11 +#define CAM_PIN_D1 9 +#define CAM_PIN_D2 8 +#define CAM_PIN_D3 10 +#define CAM_PIN_D4 12 +#define CAM_PIN_D5 18 +#define CAM_PIN_D6 17 +#define CAM_PIN_D7 16 +#endif static const char *TAG = "example:take_picture"; #if ESP_CAMERA_SUPPORTED From 0fe33c0346b9a348bb68190d2b81e830fc46a58b Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Tue, 20 May 2025 11:38:15 -0500 Subject: [PATCH 52/90] feat: Allow users to configure new/legacy I2C driver for SCCB hardware --- CMakeLists.txt | 2 +- Kconfig | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ebc992074..e021df0bea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,7 +85,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST # include the SCCB I2C driver # this uses either the legacy I2C API or the newwer version from IDF v5.4 # as this features a method to obtain the I2C driver from a port number - if (idf_version VERSION_GREATER_EQUAL "5.4") + if (idf_version VERSION_GREATER_EQUAL "5.4" AND NOT CONFIG_SCCB_HARDWARE_I2C_DRIVER_LEGACY) list(APPEND srcs driver/sccb-ng.c) else() list(APPEND srcs driver/sccb.c) diff --git a/Kconfig b/Kconfig index 2285e43b11..124a597b11 100755 --- a/Kconfig +++ b/Kconfig @@ -124,6 +124,20 @@ menu "Camera configuration" Enable this option if you want to use the MEGA CCM. Disable this option to save memory. + choice SCCB_HARDWARE_I2C_DRIVER_SELECTION + prompt "I2C driver selection for SCCB" + default SCCB_HARDWARE_I2C_DRIVER_NEW + help + Select the I2C driver to use for SCCB communication. + NOTE: new driver is only supported for ESP-IDF >= 5.4. + + config SCCB_HARDWARE_I2C_DRIVER_LEGACY + bool "Legacy I2C driver" + config SCCB_HARDWARE_I2C_DRIVER_NEW + bool "New I2C driver" + + endchoice + choice SCCB_HARDWARE_I2C_PORT bool "I2C peripheral to use for SCCB" default SCCB_HARDWARE_I2C_PORT1 From b6a43ef96035c74607fa302228b7a435bda959fe Mon Sep 17 00:00:00 2001 From: angel <1374600351@qq.com> Date: Sun, 29 Jun 2025 14:31:19 +0800 Subject: [PATCH 53/90] fix build error ```txt espressif__esp32-camera/sensors/private_include/sc031gs.h:6: error: header guard '__SC031GS_H__' followed by '#define' of a different macro [-Werror=header-guard] 6 | #ifndef __SC031GS_H__ espressif__esp32-camera/sensors/private_include/sc031gs.h:7: note: '__SC030GS_H__' is defined here; did you mean '__SC031GS_H__'? 7 | #define __SC030GS_H__ ``` --- sensors/private_include/sc031gs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensors/private_include/sc031gs.h b/sensors/private_include/sc031gs.h index bf3cdecef8..93652d58a1 100644 --- a/sensors/private_include/sc031gs.h +++ b/sensors/private_include/sc031gs.h @@ -4,7 +4,7 @@ * */ #ifndef __SC031GS_H__ -#define __SC030GS_H__ +#define __SC031GS_H__ #include "sensor.h" From 724c14d46afed87243a40d27888488fe397122ce Mon Sep 17 00:00:00 2001 From: rjoly Date: Mon, 30 Jun 2025 10:19:48 +0200 Subject: [PATCH 54/90] Fix camera probe detecting non-camera module --- driver/esp_camera.c | 49 +++++++++++++++++++---------------- driver/private_include/sccb.h | 2 +- driver/sccb-ng.c | 27 +++++-------------- driver/sccb.c | 27 ++++++------------- 4 files changed, 43 insertions(+), 62 deletions(-) diff --git a/driver/esp_camera.c b/driver/esp_camera.c index 7c693a6957..0187aaba83 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -212,34 +212,39 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out ESP_LOGD(TAG, "Searching for camera address"); vTaskDelay(10 / portTICK_PERIOD_MS); - uint8_t slv_addr = SCCB_Probe(); + int camera_model_id = 0; + uint8_t slave_addr = 0x0; - if (slv_addr == 0) { - ret = ESP_ERR_NOT_FOUND; - goto err; - } + for(camera_model_id ; *out_camera_model == CAMERA_NONE && camera_model_id < CAMERA_MODEL_MAX ; camera_model_id++) { + slave_addr = camera_sensor[camera_model_id].sccb_addr; - 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 (ESP_OK != SCCB_PROBE(slave_addr)) { + continue; + } + + 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; + } } } } + ESP_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr); + if (CAMERA_NONE == *out_camera_model) { //If no supported sensors are detected ESP_LOGE(TAG, "Detected camera not supported."); ret = ESP_ERR_NOT_SUPPORTED; diff --git a/driver/private_include/sccb.h b/driver/private_include/sccb.h index f000fba112..04dd95a6bb 100755 --- a/driver/private_include/sccb.h +++ b/driver/private_include/sccb.h @@ -12,7 +12,7 @@ int SCCB_Init(int pin_sda, int pin_scl); int SCCB_Use_Port(int sccb_i2c_port); int SCCB_Deinit(void); -uint8_t SCCB_Probe(void); +int SCCB_Probe(uint8_t slave_addr); uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg); int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data); uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg); diff --git a/driver/sccb-ng.c b/driver/sccb-ng.c index 9272c37c1b..0559db5b66 100755 --- a/driver/sccb-ng.c +++ b/driver/sccb-ng.c @@ -198,9 +198,8 @@ int SCCB_Deinit(void) return ESP_OK; } -uint8_t SCCB_Probe(void) +int SCCB_Probe(uint8_t slave_addr) { - uint8_t slave_addr = 0x0; esp_err_t ret; i2c_master_bus_handle_t bus_handle; @@ -211,26 +210,14 @@ uint8_t SCCB_Probe(void) return ret; } - 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; + ret = i2c_master_probe(bus_handle, slave_addr, TIMEOUT_MS); - ret = i2c_master_probe(bus_handle, slave_addr, TIMEOUT_MS); - - if (ret == ESP_OK) - { - if (SCCB_Install_Device(slave_addr) != 0) - { - return 0; - } - return slave_addr; - } + if (ret == ESP_OK) + { + return SCCB_Install_Device(slave_addr); } - return 0; + + return ret; } uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) diff --git a/driver/sccb.c b/driver/sccb.c index 7007883c15..2fb253c910 100755 --- a/driver/sccb.c +++ b/driver/sccb.c @@ -93,26 +93,15 @@ int SCCB_Deinit(void) return i2c_driver_delete(sccb_i2c_port); } -uint8_t SCCB_Probe(void) +int SCCB_Probe(uint8_t slave_addr) { - uint8_t slave_addr = 0x0; - - 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); - i2c_master_stop(cmd); - esp_err_t ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS); - i2c_cmd_link_delete(cmd); - if( ret == ESP_OK) { - return slave_addr; - } - } - return 0; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( slave_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + return ret; } uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) From e7d2ff69c79408d8e0a97ead33897c0deb1d7b70 Mon Sep 17 00:00:00 2001 From: rjoly Date: Mon, 30 Jun 2025 11:25:48 +0200 Subject: [PATCH 55/90] Fix naming --- driver/esp_camera.c | 18 +++++++++--------- driver/sccb-ng.c | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/driver/esp_camera.c b/driver/esp_camera.c index 0187aaba83..21f3772623 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -212,13 +212,13 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out ESP_LOGD(TAG, "Searching for camera address"); vTaskDelay(10 / portTICK_PERIOD_MS); - int camera_model_id = 0; - uint8_t slave_addr = 0x0; + int camera_model_id; + uint8_t slv_addr = 0x0; - for(camera_model_id ; *out_camera_model == CAMERA_NONE && camera_model_id < CAMERA_MODEL_MAX ; camera_model_id++) { - slave_addr = camera_sensor[camera_model_id].sccb_addr; + for(camera_model_id = 0; (*out_camera_model == CAMERA_NONE && camera_model_id < CAMERA_MODEL_MAX) ; camera_model_id++) { + slv_addr = camera_sensor[camera_model_id].sccb_addr; - if (ESP_OK != SCCB_PROBE(slave_addr)) { + if (ESP_OK != SCCB_Probe(slv_addr)) { continue; } @@ -230,6 +230,9 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out * 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; + ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x", + id->PID, id->VER, id->MIDH, id->MIDL); + 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); @@ -243,16 +246,13 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out } } - ESP_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr); - if (CAMERA_NONE == *out_camera_model) { //If no supported sensors are detected ESP_LOGE(TAG, "Detected camera not supported."); ret = ESP_ERR_NOT_SUPPORTED; goto err; } - 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_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr); ESP_LOGD(TAG, "Doing SW reset of sensor"); vTaskDelay(10 / portTICK_PERIOD_MS); diff --git a/driver/sccb-ng.c b/driver/sccb-ng.c index 0559db5b66..e8701ca088 100755 --- a/driver/sccb-ng.c +++ b/driver/sccb-ng.c @@ -198,7 +198,7 @@ int SCCB_Deinit(void) return ESP_OK; } -int SCCB_Probe(uint8_t slave_addr) +int SCCB_Probe(uint8_t slv_addr) { esp_err_t ret; i2c_master_bus_handle_t bus_handle; @@ -210,11 +210,11 @@ int SCCB_Probe(uint8_t slave_addr) return ret; } - ret = i2c_master_probe(bus_handle, slave_addr, TIMEOUT_MS); + ret = i2c_master_probe(bus_handle, slv_addr, TIMEOUT_MS); if (ret == ESP_OK) { - return SCCB_Install_Device(slave_addr); + return SCCB_Install_Device(slv_addr); } return ret; From 023468a8faf0410ab4067f43dc9a87862fd19da9 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Mon, 30 Jun 2025 20:03:07 +0300 Subject: [PATCH 56/90] Update CI target IDF versions --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5ecd283102..e242973103 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "latest"] + idf_ver: ["release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "latest"] idf_target: ["esp32", "esp32s2", "esp32s3", "esp32c2", "esp32c3"] container: espressif/idf:${{ matrix.idf_ver }} steps: From 64e8b613e27dcefde10b68e736e0d2b9208bc826 Mon Sep 17 00:00:00 2001 From: Lesords Date: Tue, 1 Jul 2025 10:33:46 +0800 Subject: [PATCH 57/90] fix: fix some compilation errors --- sensors/hm0360.c | 25 ++++++++++++++++++++++++- sensors/hm1055.c | 8 ++------ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/sensors/hm0360.c b/sensors/hm0360.c index a3acea47f8..c73549aa6c 100755 --- a/sensors/hm0360.c +++ b/sensors/hm0360.c @@ -368,8 +368,31 @@ static int set_dummy(sensor_t *sensor, int val) return -1; } +static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val) +{ + ESP_LOGW(TAG, "Unsupported"); + return -1; +} + static int init_status(sensor_t *sensor) { + (void) write_addr_reg; + + 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.awb = 0; + sensor->status.aec = 0; + sensor->status.hmirror = check_reg_mask(sensor->slv_addr, 0x101, 0x01); + sensor->status.vflip = check_reg_mask(sensor->slv_addr, 0x101, 0x02); + sensor->status.lenc = 0; + sensor->status.awb_gain = 0; + sensor->status.agc_gain = 0; + sensor->status.aec_value = 0; + return 0; } @@ -399,7 +422,7 @@ int hm0360_init(sensor_t *sensor) sensor->set_brightness = set_brightness; sensor->set_saturation = set_dummy; sensor->set_sharpness = set_dummy; - sensor->set_gainceiling = set_dummy; + sensor->set_gainceiling = set_gainceiling_dummy; sensor->set_quality = set_dummy; sensor->set_colorbar = set_colorbar; sensor->set_gain_ctrl = set_dummy; diff --git a/sensors/hm1055.c b/sensors/hm1055.c index d3318a7f30..66b9e4259a 100755 --- a/sensors/hm1055.c +++ b/sensors/hm1055.c @@ -407,11 +407,6 @@ static int set_ae_level(sensor_t *sensor, int level) return 0; } -static int set_awb_gain_dsp(sensor_t *sensor, int enable) -{ - return 0; -} - static int set_brightness(sensor_t *sensor, int level) { int ret = 0; @@ -467,7 +462,6 @@ static int set_contrast(sensor_t *sensor, int level) { int ret = 0; uint8_t ispctrl5 = read_reg(sensor->slv_addr, ISPCTRL5); - uint8_t value1 = 0; ispctrl5 |= 0x80; // enable contrast ret = write_reg(sensor->slv_addr, ISPCTRL5, ispctrl5); @@ -755,6 +749,8 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) static int init_status(sensor_t *sensor) { + (void) write_addr_reg; + sensor->status.brightness = 0; sensor->status.contrast = 0; sensor->status.saturation = 0; From aa9e228040045d2a946360a9e511f6603311f807 Mon Sep 17 00:00:00 2001 From: rjoly Date: Tue, 1 Jul 2025 10:33:44 +0200 Subject: [PATCH 58/90] Implemented github copilot suggestion --- driver/esp_camera.c | 9 ++++++--- driver/private_include/sccb.h | 2 +- driver/sccb.c | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/driver/esp_camera.c b/driver/esp_camera.c index 782ff0f708..a807d11376 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -215,7 +215,10 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out int camera_model_id; uint8_t slv_addr = 0x0; - for(camera_model_id = 0; (*out_camera_model == CAMERA_NONE && camera_model_id < CAMERA_MODEL_MAX) ; camera_model_id++) { + /** + * This loop probes each known sensor until a supported camera is detected + */ + for(camera_model_id = 0; *out_camera_model == CAMERA_NONE && camera_model_id < CAMERA_MODEL_MAX ; camera_model_id++) { slv_addr = camera_sensor[camera_model_id].sccb_addr; if (ESP_OK != SCCB_Probe(slv_addr)) { @@ -230,11 +233,11 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out * 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; - ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x", - id->PID, id->VER, id->MIDH, id->MIDL); for (size_t i = 0; i < sizeof(g_sensors) / sizeof(sensor_func_t); i++) { if (g_sensors[i].detect(slv_addr, id)) { + ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x", + id->PID, id->VER, id->MIDH, id->MIDL); camera_sensor_info_t *info = esp_camera_sensor_get_info(id); if (NULL != info) { *out_camera_model = info->model; diff --git a/driver/private_include/sccb.h b/driver/private_include/sccb.h index 04dd95a6bb..c8613da124 100755 --- a/driver/private_include/sccb.h +++ b/driver/private_include/sccb.h @@ -12,7 +12,7 @@ int SCCB_Init(int pin_sda, int pin_scl); int SCCB_Use_Port(int sccb_i2c_port); int SCCB_Deinit(void); -int SCCB_Probe(uint8_t slave_addr); +int SCCB_Probe(uint8_t slv_addr); uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg); int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data); uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg); diff --git a/driver/sccb.c b/driver/sccb.c index 2fb253c910..63bddac957 100755 --- a/driver/sccb.c +++ b/driver/sccb.c @@ -93,11 +93,11 @@ int SCCB_Deinit(void) return i2c_driver_delete(sccb_i2c_port); } -int SCCB_Probe(uint8_t slave_addr) +int SCCB_Probe(uint8_t slv_addr) { i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); - i2c_master_write_byte(cmd, ( slave_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN); i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS); i2c_cmd_link_delete(cmd); From c4204d31bf2c422b2b46e2f27026deb6bb1cd261 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Mon, 30 Jun 2025 20:45:45 +0200 Subject: [PATCH 59/90] =?UTF-8?q?fix(cam=5Fhal):=20replace=20recursive=20c?= =?UTF-8?q?am=5Ftake()=20with=20bounded=20loop=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=E2=80=93=20prevents=20stack-overflow=20in?= =?UTF-8?q?=20cam=5Ftask?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old cam_take() used recursion to retry if * no JPEG EOI was found or * a NULL frame pointer was returned (GDMA glitch on ESP32-S3). Under heavy loss conditions this could overflow the cam_task stack and reboot the whole system. * Re-wrote cam_take() as a single while-loop – stack depth is now constant and independent of retry count. * Added strict timeout tracking (`remaining = timeout - elapsed`); function can never block longer than the caller’s timeout. * ESP32-S3 only * capped GDMA reset storms to 3 attempts (`MAX_GDMA_RESETS`) * logs one “giving up” warning, then yields (`vTaskDelay(1)`) to avoid busy-spin after hardware is wedged. * Non-S3 targets * emit early `ESP_LOGW` when a NULL frame ever appears, then yield one tick per loop to prevent CPU thrash. * Maintained existing JPEG-EOI trimming and YUV→GRAY memcpy paths; behaviour unchanged on successful frames. * Inline comment links to esp32-camera commit 984999f / issue #620 for future context. --- driver/cam_hal.c | 80 +++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 4daaee7264..1890218ae4 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -477,47 +477,65 @@ void cam_start(void) 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); + const TickType_t start = xTaskGetTickCount(); #if CONFIG_IDF_TARGET_ESP32S3 - // Currently (22.01.2024) there is a bug in ESP-IDF v5.2, that causes - // GDMA to fall into a strange state if it is running while WiFi STA is connecting. - // This code tries to reset GDMA if frame is not received, to try and help with - // this case. It is possible to have some side effects too, though none come to mind - if (!dma_buffer) { - ll_cam_dma_reset(cam_obj); - xQueueReceive(cam_obj->frame_buffer_queue, (void *)&dma_buffer, timeout); - } + uint16_t dma_reset_counter = 0; + static const uint8_t MAX_GDMA_RESETS = 3; +#endif + + for (;;) + { + TickType_t elapsed = xTaskGetTickCount() - start; /* TickType_t is unsigned so rollover is safe */ + if (elapsed >= timeout) { + ESP_LOGW(TAG, "Failed to get frame: timeout"); + return NULL; + } + TickType_t remaining = timeout - elapsed; + + if (xQueueReceive(cam_obj->frame_buffer_queue, (void *)&dma_buffer, remaining) == pdFALSE) { + continue; + } + + if (!dma_buffer) { + /* Work-around for ESP32-S3 GDMA freeze when Wi-Fi STA starts. + * See esp32-camera commit 984999f (issue #620). */ +#if CONFIG_IDF_TARGET_ESP32S3 + if (dma_reset_counter < MAX_GDMA_RESETS) { + ll_cam_dma_reset(cam_obj); + dma_reset_counter++; + continue; /* retry with queue timeout */ + } + if (dma_reset_counter == MAX_GDMA_RESETS) { + ESP_LOGW(TAG, "Giving up GDMA reset after %u tries", dma_reset_counter); + dma_reset_counter++; /* suppress further logs */ + } +#else + /* Early warning for misbehaving sensors on other chips */ + ESP_LOGW(TAG, "Unexpected NULL frame on %s", CONFIG_IDF_TARGET); #endif - if (dma_buffer) { - if(cam_obj->jpeg_mode){ - // find the end marker for JPEG. Data after that can be discarded + vTaskDelay(1); /* immediate yield once resets are done */ + continue; /* go to top of loop */ + } + + 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); - TickType_t ticks_spent = xTaskGetTickCount() - start; - if (ticks_spent >= timeout) { - return NULL; /* We are out of time */ - } - return cam_take(timeout - ticks_spent);//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 + + ESP_LOGW(TAG, "NO-EOI"); + cam_give(dma_buffer); + continue; /* wait for another frame */ + } else if (cam_obj->psram_mode && + cam_obj->in_bytes_per_pixel != cam_obj->fb_bytes_per_pixel) { + /* currently 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!"); -// #if CONFIG_IDF_TARGET_ESP32S3 -// ll_cam_dma_print_state(cam_obj); -// #endif } - return NULL; } void cam_give(camera_fb_t *dma_buffer) @@ -539,4 +557,4 @@ void cam_give_all(void) { bool cam_get_available_frames(void) { return 0 < uxQueueMessagesWaiting(cam_obj->frame_buffer_queue); -} \ No newline at end of file +} From ee438a2a26b5da8dd1dc8c2f07df68fd4ad21ed4 Mon Sep 17 00:00:00 2001 From: Gao Wei Date: Wed, 2 Jul 2025 20:01:15 +0800 Subject: [PATCH 60/90] fix: Remove unused Kconfig option --- Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/Kconfig b/Kconfig index 3ef56f1d43..f692e59358 100755 --- a/Kconfig +++ b/Kconfig @@ -130,8 +130,6 @@ menu "Camera configuration" help Enable this option if you want to use the HM0360. Disable this option to save memory. - - config SCCB_I2C_PORT config MEGA_CCM_SUPPORT bool "Support MEGA CCM 5MP" From 3e50b59f3da3fdf9a22ed4b7f307e5b5bd8787fb Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Tue, 1 Jul 2025 20:01:20 +0200 Subject: [PATCH 61/90] fix(cam_hal): prevent SOI scan from (1) running on length<3 and (2) over-reading the last 2 bytes Changes: * Store SOI as a 3-byte array (0xFF D8 FF) * Early-exit when length < 3 to avoid over-reading * calculate end index correctly, to avoid over-reading --- driver/cam_hal.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 1890218ae4..fa668b3b74 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -42,13 +42,20 @@ 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 +/* JPEG markers in little-endian order (ESP32). */ +static const uint8_t JPEG_SOI_MARKER[] = {0xFF, 0xD8, 0xFF}; /* SOI = FF D8 FF */ +#define JPEG_SOI_MARKER_LEN (3) +static const uint16_t JPEG_EOI_MARKER = 0xD9FF; /* EOI = FF D9 */ static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length) { - for (uint32_t i = 0; i < length; i++) { - if (memcmp(&inbuf[i], &JPEG_SOI_MARKER, 3) == 0) { + if (length < JPEG_SOI_MARKER_LEN) { + ESP_LOGW(TAG, "NO-SOI"); + return -1; + } + + for (uint32_t i = 0; i <= length - JPEG_SOI_MARKER_LEN; i++) { + if (memcmp(&inbuf[i], JPEG_SOI_MARKER, JPEG_SOI_MARKER_LEN) == 0) { //ESP_LOGW(TAG, "SOI: %d", (int) i); return i; } From ee92090425c2c5234ec697105b3d3453e14a2ec0 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Tue, 1 Jul 2025 18:55:50 +0200 Subject: [PATCH 62/90] fix(cam_hal): guard cam_verify_jpeg_eoi() against buffer-underflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If DMA returns a frame shorter than two bytes, the previous code did: dptr = inbuf + length - 2; which under-flows the pointer and produces undefined behaviour. Behaviour for valid frames (length ≥ 2) is unchanged; damaged or empty buffers are now discarded safely. --- driver/cam_hal.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index fa668b3b74..f244e2f871 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -46,6 +46,7 @@ static cam_obj_t *cam_obj = NULL; static const uint8_t JPEG_SOI_MARKER[] = {0xFF, 0xD8, 0xFF}; /* SOI = FF D8 FF */ #define JPEG_SOI_MARKER_LEN (3) static const uint16_t JPEG_EOI_MARKER = 0xD9FF; /* EOI = FF D9 */ +#define JPEG_EOI_MARKER_LEN (2) static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length) { @@ -66,10 +67,14 @@ static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length) static int cam_verify_jpeg_eoi(const uint8_t *inbuf, uint32_t length) { + if (length < JPEG_EOI_MARKER_LEN) { + return -1; + } + int offset = -1; - uint8_t *dptr = (uint8_t *)inbuf + length - 2; + uint8_t *dptr = (uint8_t *)inbuf + length - JPEG_EOI_MARKER_LEN; while (dptr > inbuf) { - if (memcmp(dptr, &JPEG_EOI_MARKER, 2) == 0) { + if (memcmp(dptr, &JPEG_EOI_MARKER, JPEG_EOI_MARKER_LEN) == 0) { offset = dptr - inbuf; //ESP_LOGW(TAG, "EOI: %d", length - (offset + 2)); return offset; From f8fad50da54ff3aeb83fb3eb74ee92099a90d874 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Tue, 1 Jul 2025 23:32:25 +0200 Subject: [PATCH 63/90] cam_hal: shrink ISR stack, silence spam, strip logs at low levels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Replace ESP_LOGx in ISRs/tight loops with CAM_WARN_THROTTLE(counter,msg) → uses ROM-resident ets_printf(); ~300 B less stack per hit. * At CONFIG_LOG_DEFAULT_LEVEL < 2 the macro compiles to a no-op, so *all* warning code is dropped from the binary. * First miss logs immediately, then every 100th; counter wraps at 10 000 (reset to 1 to skip the “first miss” banner after wrap). * New static uint16_t counters per call-site keep totals without globals. * Kconfig switch CAM_LOG_SPAM_EVERY_FRAME (=0) restores old per-frame debug. No functional change to capture path—just less stack, fewer cycles, smaller image, and a much quieter UART. --- driver/cam_hal.c | 54 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index f244e2f871..815810343f 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -42,6 +42,30 @@ static const char *TAG = "cam_hal"; static cam_obj_t *cam_obj = NULL; +/* At top of cam_hal.c – one switch for noisy ISR prints */ +#ifndef CAM_LOG_SPAM_EVERY_FRAME +#define CAM_LOG_SPAM_EVERY_FRAME 0 /* set to 1 to restore old behaviour */ +#endif + +/* Throttle repeated warnings printed from tight loops / ISRs. + * + * counter – static DRAM/IRAM uint16_t you pass in + * first – literal C string shown on first hit and as prefix of summaries + */ +#if CONFIG_LOG_DEFAULT_LEVEL >= 2 +#define CAM_WARN_THROTTLE(counter, first) \ + do { \ + if (++(counter) == 1) { \ + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: %s\r\n"), first); \ + } else if ((counter) % 100 == 0) { \ + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: %s - 100 additional misses\r\n"), first); \ + } \ + if ((counter) == 10000) (counter) = 1; \ + } while (0) +#else +#define CAM_WARN_THROTTLE(counter, first) do { (void)(counter); } while (0) +#endif + /* JPEG markers in little-endian order (ESP32). */ static const uint8_t JPEG_SOI_MARKER[] = {0xFF, 0xD8, 0xFF}; /* SOI = FF D8 FF */ #define JPEG_SOI_MARKER_LEN (3) @@ -50,8 +74,10 @@ static const uint16_t JPEG_EOI_MARKER = 0xD9FF; /* EOI = FF D9 */ static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length) { + static uint16_t warn_soi_miss_cnt = 0; if (length < JPEG_SOI_MARKER_LEN) { - ESP_LOGW(TAG, "NO-SOI"); + CAM_WARN_THROTTLE(warn_soi_miss_cnt, + "NO-SOI - JPEG start marker missing (len < 3b)"); return -1; } @@ -61,7 +87,9 @@ static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length) return i; } } - ESP_LOGW(TAG, "NO-SOI"); + + CAM_WARN_THROTTLE(warn_soi_miss_cnt, + "NO-SOI - JPEG start marker missing"); return -1; } @@ -119,7 +147,13 @@ void IRAM_ATTR ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType if (xQueueSendFromISR(cam->event_queue, (void *)&cam_event, HPTaskAwoken) != pdTRUE) { ll_cam_stop(cam); cam->state = CAM_STATE_IDLE; - ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: EV-%s-OVF\r\n"), cam_event==CAM_IN_SUC_EOF_EVENT ? DRAM_STR("EOF") : DRAM_STR("VSYNC")); +#if CAM_LOG_SPAM_EVERY_FRAME + ESP_DRAM_LOGD(TAG, "EV-%s-OVF", cam_event==CAM_IN_SUC_EOF_EVENT ? "EOF" : "VSYNC"); +#else + static uint16_t ovf_cnt = 0; + CAM_WARN_THROTTLE(ovf_cnt, + cam_event==CAM_IN_SUC_EOF_EVENT ? "EV-EOF-OVF" : "EV-VSYNC-OVF"); +#endif } } @@ -493,7 +527,12 @@ camera_fb_t *cam_take(TickType_t timeout) #if CONFIG_IDF_TARGET_ESP32S3 uint16_t dma_reset_counter = 0; static const uint8_t MAX_GDMA_RESETS = 3; +#else + /* throttle repeated NULL frame warnings */ + static uint16_t warn_null_cnt = 0; #endif + /* throttle repeated NO-EOI warnings */ + static uint16_t warn_eoi_miss_cnt = 0; for (;;) { @@ -518,12 +557,14 @@ camera_fb_t *cam_take(TickType_t timeout) continue; /* retry with queue timeout */ } if (dma_reset_counter == MAX_GDMA_RESETS) { - ESP_LOGW(TAG, "Giving up GDMA reset after %u tries", dma_reset_counter); + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: Giving up GDMA reset after %u tries\r\n"), + (unsigned) dma_reset_counter); dma_reset_counter++; /* suppress further logs */ } #else /* Early warning for misbehaving sensors on other chips */ - ESP_LOGW(TAG, "Unexpected NULL frame on %s", CONFIG_IDF_TARGET); + CAM_WARN_THROTTLE(warn_null_cnt, + "Unexpected NULL frame on " CONFIG_IDF_TARGET); #endif vTaskDelay(1); /* immediate yield once resets are done */ continue; /* go to top of loop */ @@ -537,7 +578,8 @@ camera_fb_t *cam_take(TickType_t timeout) return dma_buffer; } - ESP_LOGW(TAG, "NO-EOI"); + CAM_WARN_THROTTLE(warn_eoi_miss_cnt, + "NO-EOI - JPEG end marker missing"); cam_give(dma_buffer); continue; /* wait for another frame */ } else if (cam_obj->psram_mode && From 225a4db4d6e1e6772b53acc0a205676185cf4b34 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Mon, 14 Jul 2025 17:57:44 +0200 Subject: [PATCH 64/90] jpg2bmp: fix memory leak on error in case the jpeg decode did fail we would never return the memory we allocated for the output buffer and thus create a memory leak. --- conversions/to_bmp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conversions/to_bmp.c b/conversions/to_bmp.c index 8c1a6f05a0..d8f228d0a5 100644 --- a/conversions/to_bmp.c +++ b/conversions/to_bmp.c @@ -13,6 +13,7 @@ // limitations under the License. #include #include +#include #include "img_converters.h" #include "soc/efuse_reg.h" #include "esp_heap_caps.h" @@ -135,6 +136,7 @@ bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_le jpeg_cfg.outbuf = output + BMP_HEADER_LEN; jpeg_cfg.outbuf_size = output_img.output_len; if(esp_jpeg_decode(&jpeg_cfg, &output_img) != ESP_OK){ + free(output); return false; } From 0c72514363cb64169d060e782fa6bc8d15fe453b Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Mon, 14 Jul 2025 20:30:20 +0200 Subject: [PATCH 65/90] jpg2bmp: unified rework error handling Consolidated all error handling in jpg2bmp into a single fail path so memory is freed reliably --- conversions/to_bmp.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/conversions/to_bmp.c b/conversions/to_bmp.c index d8f228d0a5..7199b228db 100644 --- a/conversions/to_bmp.c +++ b/conversions/to_bmp.c @@ -115,29 +115,31 @@ bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_le .advanced.working_buffer = work, .advanced.working_buffer_size = sizeof(work), }; - + + bool ret = false; + uint8_t *output = NULL; esp_jpeg_image_output_t output_img = {}; if (esp_jpeg_get_image_info(&jpeg_cfg, &output_img) != ESP_OK) { ESP_LOGE(TAG, "Failed to get image info"); - return false; + goto fail; } - + // @todo here we allocate memory and we assume that the user will free it // this is not the best way to do it, but we need to keep the API // compatible with the previous version const size_t output_size = output_img.output_len + BMP_HEADER_LEN; - uint8_t *output = _malloc(output_size); + output = _malloc(output_size); if (!output) { ESP_LOGE(TAG, "Failed to allocate output buffer"); - return false; + goto fail; } // Start writing decoded data after the BMP header jpeg_cfg.outbuf = output + BMP_HEADER_LEN; jpeg_cfg.outbuf_size = output_img.output_len; if(esp_jpeg_decode(&jpeg_cfg, &output_img) != ESP_OK){ - free(output); - return false; + ESP_LOGE(TAG, "JPEG decode failed"); + goto fail; } output[0] = 'B'; @@ -160,8 +162,13 @@ bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_le *out = output; *out_len = output_size; + ret = true; - return true; +fail: + if (!ret && output) { + free(output); + } + return ret; } bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf) From be37adc2dab1388fcdfd7d4a3ffb6ccdfad7518a Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Tue, 15 Jul 2025 06:26:40 +0200 Subject: [PATCH 66/90] hm0360: _set_pll(): detect read_reg failure before writing PLL1CFG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old code stored the raw return value of read_reg() in an 8-bit variable and wrote it straight back to PLL1CFG. A negative I²C/SCCB error therefore became 0xFF (or similar), silently corrupting the sensor’s clock tree while still returning “success”. * Read PLL1CFG into an int (`ret`) and return immediately if `ret < 0`. * Cast to uint8_t only after the error check, then proceed with the masked write. This propagates read failures to the caller and guarantees we never write garbage to the PLL register under fault conditions. --- sensors/hm0360.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sensors/hm0360.c b/sensors/hm0360.c index c73549aa6c..fcd0db0be0 100755 --- a/sensors/hm0360.c +++ b/sensors/hm0360.c @@ -358,7 +358,12 @@ static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, i value = 0x00; } - pll_cfg = read_reg(sensor->slv_addr, PLL1CFG); + int ret = read_reg(sensor->slv_addr, PLL1CFG); + if (ret < 0) { + return ret; + } + + pll_cfg = (uint8_t)ret; return write_reg(sensor->slv_addr, PLL1CFG, (pll_cfg & 0xFC) | value); } From c12517e46731591ffe69fb55c796751df25f9c92 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Tue, 15 Jul 2025 06:34:13 +0200 Subject: [PATCH 67/90] hm0360: _set_pll(): guard against wide reads and silence unused-param warnings * Mark all currently unused PLL parameters with (void) to remove -Wunused-parameter noise. * After a successful read_reg(), reject any value > 0xFF and return -ERANGE. Ensures only 8-bit data is written to PLL1CFG. --- sensors/hm0360.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sensors/hm0360.c b/sensors/hm0360.c index fcd0db0be0..faf6d22580 100755 --- a/sensors/hm0360.c +++ b/sensors/hm0360.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "sccb.h" #include "xclk.h" #include "hm0360.h" @@ -345,6 +346,14 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) 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) { + (void)bypass; + (void)multiplier; + (void)sys_div; + (void)root_2x; + (void)pre_div; + (void)seld5; + (void)pclk_manual; + (void)pclk_div; uint8_t value = 0; uint8_t pll_cfg = 0; @@ -362,6 +371,14 @@ static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, i if (ret < 0) { return ret; } + if (ret > 0xFF) { + /* + * Guard against unexpected wide register values. If read_reg + * ever returns a 16-bit result, reject values that don't fit + * in a single byte to avoid truncation. + */ + return -ERANGE; + } pll_cfg = (uint8_t)ret; return write_reg(sensor->slv_addr, PLL1CFG, (pll_cfg & 0xFC) | value); From 7ffdae8c47e96d6e6e5859485c4b4caa32608fe0 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Thu, 17 Jul 2025 07:39:51 +0200 Subject: [PATCH 68/90] hm1055: fix set_brightness error handling & logging The old code always wrote BRIGHT even if enabling ISPCTRL5 failed, while also overwriting the first error code. It also used `if (ret != 0)` to log/update status, so we reported success when the write actually failed. Guard the BRIGHT write behind a successful ISPCTRL5 write and only log / update `sensor->status.brightness` when both writes succeed. This keeps the original error, and prevents false success reports. --- sensors/hm1055.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sensors/hm1055.c b/sensors/hm1055.c index 66b9e4259a..97c6e082dc 100755 --- a/sensors/hm1055.c +++ b/sensors/hm1055.c @@ -449,8 +449,11 @@ static int set_brightness(sensor_t *sensor, int level) ispctrl5 |= 0x40; // enable brightness ret = write_reg(sensor->slv_addr, ISPCTRL5, ispctrl5); - ret = write_reg(sensor->slv_addr, BRIGHT, brightness); - if (ret != 0) + if (ret == 0) + { + ret = write_reg(sensor->slv_addr, BRIGHT, brightness); + } + if (ret == 0) { ESP_LOGD(TAG, "Set brightness to: %d", level); sensor->status.brightness = level; From 05decdbce71d0c1332d0380e96dc3828b3ab5bbb Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Thu, 17 Jul 2025 14:32:35 +0200 Subject: [PATCH 69/90] sccb-ng: fix off-by-one device capacity check to prevent overflow SCCB_Install_Device() rejected new devices only when device_count > MAX_DEVICES. When device_count == MAX_DEVICES the function still proceeded to install the device and wrote to devices[device_count], i.e. devices[MAX_DEVICES], which is one element past the end of the devices[] array (valid indices 0..MAX_DEVICES-1). This off-by-one results in a buffer overflow / write outside the designated memory area and then increments device_count to MAX_DEVICES+1. Change the guard to `device_count >= MAX_DEVICES` so we refuse installation once the array is full and prevent the out-of-bounds write/read chain. --- driver/sccb-ng.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/sccb-ng.c b/driver/sccb-ng.c index e8701ca088..ddf5746101 100755 --- a/driver/sccb-ng.c +++ b/driver/sccb-ng.c @@ -80,7 +80,7 @@ int SCCB_Install_Device(uint8_t slv_addr) esp_err_t ret; i2c_master_bus_handle_t bus_handle; - if (device_count > MAX_DEVICES) + if (device_count >= MAX_DEVICES) { ESP_LOGE(TAG, "cannot add more than %d devices", MAX_DEVICES); return ESP_FAIL; From 7673d3ee22088e01fbe2ce5ea6ed70ef18cb913b Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Thu, 17 Jul 2025 19:46:57 +0200 Subject: [PATCH 70/90] docs(readme): harden jpg_stream_httpd_handler example & fix bugs Changes: * Use `%zu` in _STREAM_PART for size_t-safe Content-Length formatting. * Fix `part_buf` type: `char part_buf[64]` (was array of char pointers). * initialize local JPEG buffer vars: `jpg_buf_len`/`jpg_buf` * drop leading underscores on them for consistency with other locals * Bound `snprintf()` by `sizeof(part_buf)`; capture `int hlen`. * Detect and log header truncation with required/available sizes; fail gracefully. * Break out of loop on JPEG compression failure to avoid invalid buffer use. * Send header chunk only when formatting succeeds. * Guard FPS calculation against divide-by-zero; compute once as `fps`. * Remove unsafe/needless pointer cast. * Minor style cleanups for readability. --- README.md | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index def7aa1fff..774bf423a7 100644 --- a/README.md +++ b/README.md @@ -249,14 +249,14 @@ esp_err_t jpg_httpd_handler(httpd_req_t *req){ #define PART_BOUNDARY "123456789000000000000987654321" static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; -static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; +static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %zu\r\n\r\n"; esp_err_t jpg_stream_httpd_handler(httpd_req_t *req){ camera_fb_t * fb = NULL; esp_err_t res = ESP_OK; - size_t _jpg_buf_len; - uint8_t * _jpg_buf; - char * part_buf[64]; + size_t jpg_buf_len = 0; + uint8_t * jpg_buf = NULL; + char part_buf[64]; static int64_t last_frame = 0; if(!last_frame) { last_frame = esp_timer_get_time(); @@ -275,30 +275,36 @@ esp_err_t jpg_stream_httpd_handler(httpd_req_t *req){ break; } if(fb->format != PIXFORMAT_JPEG){ - bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len); + bool jpeg_converted = frame2jpg(fb, 80, &jpg_buf, &jpg_buf_len); if(!jpeg_converted){ ESP_LOGE(TAG, "JPEG compression failed"); esp_camera_fb_return(fb); res = ESP_FAIL; + break; } } else { - _jpg_buf_len = fb->len; - _jpg_buf = fb->buf; + jpg_buf_len = fb->len; + jpg_buf = fb->buf; } if(res == ESP_OK){ res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); } if(res == ESP_OK){ - size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len); - - res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); + int hlen = snprintf(part_buf, sizeof(part_buf), _STREAM_PART, jpg_buf_len); + if(hlen < 0 || hlen >= sizeof(part_buf)){ + ESP_LOGE(TAG, "Header truncated (%d bytes needed >= %zu buffer)", + hlen, sizeof(part_buf)); + res = ESP_FAIL; + } else { + res = httpd_resp_send_chunk(req, part_buf, (size_t)hlen); + } } if(res == ESP_OK){ - res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len); + res = httpd_resp_send_chunk(req, (const char *)jpg_buf, jpg_buf_len); } if(fb->format != PIXFORMAT_JPEG){ - free(_jpg_buf); + free(jpg_buf); } esp_camera_fb_return(fb); if(res != ESP_OK){ @@ -308,9 +314,10 @@ esp_err_t jpg_stream_httpd_handler(httpd_req_t *req){ int64_t frame_time = fr_end - last_frame; last_frame = fr_end; frame_time /= 1000; + float fps = frame_time > 0 ? 1000.0f / (float)frame_time : 0.0f; ESP_LOGI(TAG, "MJPG: %uKB %ums (%.1ffps)", - (uint32_t)(_jpg_buf_len/1024), - (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time); + (uint32_t)(jpg_buf_len/1024), + (uint32_t)frame_time, fps); } last_frame = 0; From 1484db7dba96871128a2addce603207c25739699 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Thu, 17 Jul 2025 20:24:17 +0200 Subject: [PATCH 71/90] feat: add PSRAM-DMA flag + runtime API for ESP32-S2/S3; remove 16MHz heuristic Add a Kconfig switch (CONFIG_CAMERA_PSRAM_DMA) to enable camera to PSRAM DMA copy on ESP32-S2 and ESP32-S3 targets, in favor of removing the undocumented "set XCLK=16MHz to enable PSRAM DMA" heuristic. cam_hal.c: - Select psram_mode = true when CONFIG_CAMERA_PSRAM_DMA && target is S2/S3. - Otherwise psram_mode = false. - Log whether PSRAM DMA mode is enabled. Kconfig: - New bool CAMERA_PSRAM_DMA (default n) under Camera configuration. Runtime control: - New public API: - esp_camera_set_psram_mode(bool enable) - esp_camera_get_psram_mode(void) - esp_camera_reconfigure(const camera_config_t *cfg) README/test: - Remove stale 16MHz XCLK comments. - Add short note describing CONFIG_CAMERA_PSRAM_DMA. --- Kconfig | 8 ++++++++ README.md | 4 +++- driver/cam_hal.c | 28 +++++++++++++++++++++++++--- driver/esp_camera.c | 31 +++++++++++++++++++++++++++++++ driver/include/esp_camera.h | 31 ++++++++++++++++++++++++++++++- driver/private_include/cam_hal.h | 3 +++ test/test_camera.c | 1 - 7 files changed, 100 insertions(+), 6 deletions(-) diff --git a/Kconfig b/Kconfig index f692e59358..d4701dc7cf 100755 --- a/Kconfig +++ b/Kconfig @@ -214,6 +214,14 @@ menu "Camera configuration" Maximum value of DMA buffer Larger values may fail to allocate due to insufficient contiguous memory blocks, and smaller value may cause DMA interrupt to be too frequent. + config CAMERA_PSRAM_DMA + bool "Enable PSRAM DMA mode by default" + depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default n + help + Enable DMA transfers directly from PSRAM on supported targets + (ESP32-S2 and ESP32-S3) by default. + choice CAMERA_JPEG_MODE_FRAME_SIZE_OPTION prompt "JPEG mode frame size option" default CAMERA_JPEG_MODE_FRAME_SIZE_AUTO diff --git a/README.md b/README.md index 774bf423a7..ea71806286 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi - Using YUV or RGB puts a lot of strain on the chip because writing to PSRAM is not particularly fast. The result is that image data might be missing. This is particularly true if WiFi is enabled. If you need RGB data, it is recommended that JPEG is captured and then turned into RGB using `fmt2rgb888` or `fmt2bmp`/`frame2bmp`. - When 1 frame buffer is used, the driver will wait for the current frame to finish (VSYNC) and start I2S DMA. After the frame is acquired, I2S will be stopped and the frame buffer returned to the application. This approach gives more control over the system, but results in longer time to get the frame. - When 2 or more frame bufers are used, I2S is running in continuous mode and each frame is pushed to a queue that the application can access. This approach puts more strain on the CPU/Memory, but allows for double the frame rate. Please use only with JPEG. +- The Kconfig option `CONFIG_CAMERA_PSRAM_DMA` enables PSRAM DMA mode on ESP32-S2 and ESP32-S3 devices. This flag defaults to false. +- You can switch PSRAM DMA mode at runtime using `esp_camera_set_psram_mode()`. ## Installation Instructions @@ -135,7 +137,7 @@ static camera_config_t camera_config = { .pin_href = CAM_PIN_HREF, .pin_pclk = CAM_PIN_PCLK, - .xclk_freq_hz = 20000000,//EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode + .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 815810343f..a15cacdffb 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -16,6 +16,7 @@ #include #include #include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" #include "ll_cam.h" #include "cam_hal.h" @@ -41,6 +42,14 @@ static const char *TAG = "cam_hal"; static cam_obj_t *cam_obj = NULL; +#if defined(CONFIG_CAMERA_PSRAM_DMA) +#define CAMERA_PSRAM_DMA_ENABLED CONFIG_CAMERA_PSRAM_DMA +#else +#define CAMERA_PSRAM_DMA_ENABLED 0 +#endif + +static volatile bool g_psram_dma_mode = CAMERA_PSRAM_DMA_ENABLED; +static portMUX_TYPE g_psram_dma_lock = portMUX_INITIALIZER_UNLOCKED; /* At top of cam_hal.c – one switch for noisy ISR prints */ #ifndef CAM_LOG_SPAM_EVERY_FRAME @@ -411,11 +420,12 @@ esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint CAM_CHECK_GOTO(ret == ESP_OK, "ll_cam_set_sample_mode failed", err); cam_obj->jpeg_mode = config->pixel_format == PIXFORMAT_JPEG; -#if CONFIG_IDF_TARGET_ESP32 - cam_obj->psram_mode = false; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + cam_obj->psram_mode = g_psram_dma_mode; #else - cam_obj->psram_mode = (config->xclk_freq_hz == 16000000); + cam_obj->psram_mode = false; #endif + ESP_LOGI(TAG, "PSRAM DMA mode %s", cam_obj->psram_mode ? "enabled" : "disabled"); cam_obj->frame_cnt = config->fb_count; cam_obj->width = resolution[frame_size].width; cam_obj->height = resolution[frame_size].height; @@ -612,3 +622,15 @@ bool cam_get_available_frames(void) { return 0 < uxQueueMessagesWaiting(cam_obj->frame_buffer_queue); } + +void cam_set_psram_mode(bool enable) +{ + portENTER_CRITICAL(&g_psram_dma_lock); + g_psram_dma_mode = enable; + portEXIT_CRITICAL(&g_psram_dma_lock); +} + +bool cam_get_psram_mode(void) +{ + return g_psram_dma_mode; +} diff --git a/driver/esp_camera.c b/driver/esp_camera.c index 54a8cfc490..6fb7a98024 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -95,6 +95,7 @@ typedef struct { static const char *CAMERA_SENSOR_NVS_KEY = "sensor"; static const char *CAMERA_PIXFORMAT_NVS_KEY = "pixformat"; static camera_state_t *s_state = NULL; +static camera_config_t s_saved_config; #if CONFIG_IDF_TARGET_ESP32S3 // LCD_CAM module of ESP32-S3 will generate xclk #define CAMERA_ENABLE_OUT_CLOCK(v) @@ -298,6 +299,7 @@ static pixformat_t get_output_data_format(camera_conv_mode_t conv_mode) esp_err_t esp_camera_init(const camera_config_t *config) { esp_err_t err; + s_saved_config = *config; err = cam_init(config); if (err != ESP_OK) { ESP_LOGE(TAG, "Camera init failed with error 0x%x", err); @@ -517,3 +519,32 @@ bool esp_camera_available_frames(void) } return cam_get_available_frames(); } + +esp_err_t esp_camera_reconfigure(const camera_config_t *config) +{ + if (!config) { + return ESP_ERR_INVALID_ARG; + } + if (s_state) { + esp_err_t err = esp_camera_deinit(); + if (err != ESP_OK) { + return err; + } + } + s_saved_config = *config; + return esp_camera_init(&s_saved_config); +} + +esp_err_t esp_camera_set_psram_mode(bool enable) +{ + cam_set_psram_mode(enable); + if (!s_state) { + return ESP_ERR_INVALID_STATE; + } + return esp_camera_reconfigure(&s_saved_config); +} + +bool esp_camera_get_psram_mode(void) +{ + return cam_get_psram_mode(); +} diff --git a/driver/include/esp_camera.h b/driver/include/esp_camera.h index 01af844d79..fa2f24cda9 100755 --- a/driver/include/esp_camera.h +++ b/driver/include/esp_camera.h @@ -138,7 +138,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. EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode */ + int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. */ 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 */ @@ -245,6 +245,35 @@ void esp_camera_return_all(void); */ bool esp_camera_available_frames(void); +/** + * @brief Enable or disable PSRAM DMA mode at runtime. + * + * @param enable True to enable PSRAM DMA mode, false to disable it. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if the camera is not initialized + * - Propagated error from reinitialization on failure + */ +esp_err_t esp_camera_set_psram_mode(bool enable); + +/** + * @brief Reinitialize the camera with a new configuration. + * + * @param config Updated camera configuration structure + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if config is NULL + * - Propagated error from deinit or init if they fail + */ +esp_err_t esp_camera_reconfigure(const camera_config_t *config); + +/** + * @brief Get current PSRAM DMA mode state. + * + * @return True if PSRAM DMA is enabled, false otherwise. + */ +bool esp_camera_get_psram_mode(void); + #ifdef __cplusplus } diff --git a/driver/private_include/cam_hal.h b/driver/private_include/cam_hal.h index b6f812ffce..8751c53e2c 100644 --- a/driver/private_include/cam_hal.h +++ b/driver/private_include/cam_hal.h @@ -59,6 +59,9 @@ void cam_give_all(void); bool cam_get_available_frames(void); +void cam_set_psram_mode(bool enable); +bool cam_get_psram_mode(void); + #ifdef __cplusplus } #endif diff --git a/test/test_camera.c b/test/test_camera.c index c78dd1474b..5a9db9caf7 100644 --- a/test/test_camera.c +++ b/test/test_camera.c @@ -144,7 +144,6 @@ static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, fr .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, From 309999afba04740020c003be7f92f472c572ab1d Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Thu, 17 Jul 2025 16:49:37 +0200 Subject: [PATCH 72/90] cam_hal: add guard in cam_task() so DMA transfers cannot exceed image slot size Previously there is no limit how much data would be transferred via DMA from the camera. Added a DMA overflow detection block in cam_task() so the driver stops if the number of frame copies exceeds the is reaching the maximum amount of storage allocated in PSRAM. --- driver/cam_hal.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 815810343f..dda8383895 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -193,7 +193,6 @@ static void cam_task(void *arg) 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, @@ -207,6 +206,14 @@ static void cam_task(void *arg) cam_obj->state = CAM_STATE_IDLE; } cnt++; + // stop when too many DMA copies occur so the PSRAM + // framebuffer slot doesn't overflow from runaway transfers + if (cnt >= cam_obj->frame_copy_cnt) { + ESP_LOGE(TAG, "DMA overflow"); + ll_cam_stop(cam_obj); + cam_obj->state = CAM_STATE_IDLE; + continue; + } } else if (cam_event == CAM_VSYNC_EVENT) { //DBG_PIN_SET(1); From 1c0f7bd611cc635cc0b9550371a154bdee09a6f3 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Mon, 21 Jul 2025 16:35:44 +0200 Subject: [PATCH 73/90] fix jpeg soi check for psram_mode (DMA) - Added a configurable probe length for PSRAM JPEG validation via CAM_SOI_PROBE_BYTES - For PSRAM mode, CAM_SOI_PROBE_BYTES is copied from the first DMA block into a stack buffer and validated by cam_verify_jpeg_soi() to verify that it's a valid jpeg image before continuing the capture. - Reading from PSRAM directly with cam_verify_jpeg_soi() is here avoided due to latency of small operations done by cam_verify_jpeg_soi(). --- CMakeLists.txt | 2 +- driver/cam_hal.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46d3c1164b..502011a20a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST ) endif() - set(priv_requires freertos nvs_flash) + set(priv_requires freertos nvs_flash esp_mm) set(min_version_for_esp_timer "4.2") if (idf_version VERSION_GREATER_EQUAL min_version_for_esp_timer) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 877971afc8..33038fce58 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -24,6 +24,13 @@ #include "rom/ets_sys.h" #else #include "esp_timer.h" +#include "esp_cache.h" +#include "hal/cache_hal.h" +#include "hal/cache_ll.h" +#include "esp_idf_version.h" +#ifndef ESP_CACHE_MSYNC_FLAG_DIR_M2C +#define ESP_CACHE_MSYNC_FLAG_DIR_M2C 0 +#endif #if CONFIG_IDF_TARGET_ESP32 #include "esp32/rom/ets_sys.h" // will be removed in idf v5.0 #elif CONFIG_IDF_TARGET_ESP32S2 @@ -56,6 +63,29 @@ static portMUX_TYPE g_psram_dma_lock = portMUX_INITIALIZER_UNLOCKED; #define CAM_LOG_SPAM_EVERY_FRAME 0 /* set to 1 to restore old behaviour */ #endif +/* Number of bytes copied to SRAM for SOI validation when capturing + * directly to PSRAM. Tunable to probe more of the frame start if needed. */ +#ifndef CAM_SOI_PROBE_BYTES +#define CAM_SOI_PROBE_BYTES 32 +#endif + +/* + * PSRAM DMA may bypass the CPU cache. Always call esp_cache_msync() on the + * SOI probe region so cached reads see the data written by DMA. + */ + +static inline size_t dcache_line_size(void) +{ +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) + /* cache_hal_get_cache_line_size() added extra argument from IDF 5.2 */ + return cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); +#else + /* Older releases only expose the ROM helper, all current targets + * have a 32‑byte DCache line */ + return 32; +#endif +} + /* Throttle repeated warnings printed from tight loops / ISRs. * * counter – static DRAM/IRAM uint16_t you pass in @@ -209,11 +239,61 @@ static void cam_task(void *arg) &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; + if (cam_obj->jpeg_mode && cnt == 0) { + if (cam_obj->psram_mode) { + /* dma_half_buffer_size already in BYTES (see ll_cam_memcpy()) */ + size_t probe_len = cam_obj->dma_half_buffer_size; + /* clamp to avoid copying past the end of soi_probe */ + if (probe_len > CAM_SOI_PROBE_BYTES) { + probe_len = CAM_SOI_PROBE_BYTES; + } + /* Invalidate cache lines for the DMA buffer before probing */ + size_t line = dcache_line_size(); + if (line == 0) { + line = 32; /* sane fallback */ + } + uintptr_t addr = (uintptr_t)frame_buffer_event->buf; + uintptr_t start = addr & ~(line - 1); + size_t sync_len = (probe_len + (addr - start) + line - 1) & ~(line - 1); + esp_cache_msync((void *)start, sync_len, + ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + + uint8_t soi_probe[CAM_SOI_PROBE_BYTES]; + memcpy(soi_probe, frame_buffer_event->buf, probe_len); + int soi_off = cam_verify_jpeg_soi(soi_probe, probe_len); + if (soi_off != 0) { + static uint16_t warn_psram_soi_cnt = 0; + if (soi_off > 0) { + CAM_WARN_THROTTLE(warn_psram_soi_cnt, + "NO-SOI - JPEG start marker not at pos 0 (PSRAM)"); + } else { + CAM_WARN_THROTTLE(warn_psram_soi_cnt, + "NO-SOI - JPEG start marker missing (PSRAM)"); + } + ll_cam_stop(cam_obj); + cam_obj->state = CAM_STATE_IDLE; + continue; + } + } else { + int soi_off = cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len); + if (soi_off != 0) { + static uint16_t warn_soi_bad_cnt = 0; + if (soi_off > 0) { + CAM_WARN_THROTTLE(warn_soi_bad_cnt, + "NO-SOI - JPEG start marker not at pos 0"); + } else { + CAM_WARN_THROTTLE(warn_soi_bad_cnt, + "NO-SOI - JPEG start marker missing"); + } + ll_cam_stop(cam_obj); + cam_obj->state = CAM_STATE_IDLE; + continue; + } + } } + cnt++; // stop when too many DMA copies occur so the PSRAM // framebuffer slot doesn't overflow from runaway transfers From cf96213968e1e3be38394e2fe10fd938bd731e08 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Tue, 22 Jul 2025 21:08:22 +0200 Subject: [PATCH 74/90] fix jpeg eoi check for psram_mode (DMA) - Added a configurable probe length for PSRAM JPEG validation via CAM_EOI_PROBE_BYTES - For PSRAM mode, CAM_EOI_PROBE_BYTES is copied from the first DMA block into a stack buffer and validated by cam_verify_jpeg_eoi() to verify that it's a valid jpeg image. - Reading from PSRAM directly with cam_verify_jpeg_eoi() is here avoided due to latency of small operations done by cam_verify_jpeg_eoi(). --- driver/cam_hal.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 33038fce58..73049d51ec 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -69,6 +69,12 @@ static portMUX_TYPE g_psram_dma_lock = portMUX_INITIALIZER_UNLOCKED; #define CAM_SOI_PROBE_BYTES 32 #endif +/* Number of bytes copied to SRAM for EOI validation when capturing + * directly to PSRAM. Tunable to probe more of the frame tail if needed. */ +#ifndef CAM_EOI_PROBE_BYTES +#define CAM_EOI_PROBE_BYTES 32 +#endif + /* * PSRAM DMA may bypass the CPU cache. Always call esp_cache_msync() on the * SOI probe region so cached reads see the data written by DMA. @@ -669,7 +675,37 @@ camera_fb_t *cam_take(TickType_t timeout) 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); + int offset_e = -1; + if (cam_obj->psram_mode) { + size_t probe_len = dma_buffer->len; + if (probe_len > CAM_EOI_PROBE_BYTES) { + probe_len = CAM_EOI_PROBE_BYTES; + } + if (probe_len == 0) { + goto skip_eoi_check; + } + size_t line = dcache_line_size(); + if (line == 0) { + line = 32; /* sane fallback */ + } + uintptr_t addr = (uintptr_t)(dma_buffer->buf + dma_buffer->len - probe_len); + uintptr_t start = addr & ~(line - 1); + size_t sync_len = (probe_len + (addr - start) + line - 1) & ~(line - 1); + esp_cache_msync((void *)start, sync_len, + ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + + uint8_t eoi_probe[CAM_EOI_PROBE_BYTES]; + memcpy(eoi_probe, dma_buffer->buf + dma_buffer->len - probe_len, probe_len); + int off = cam_verify_jpeg_eoi(eoi_probe, probe_len); + if (off >= 0) { + offset_e = dma_buffer->len - probe_len + off; + } + } else { + offset_e = cam_verify_jpeg_eoi(dma_buffer->buf, dma_buffer->len); + } + +skip_eoi_check: + if (offset_e >= 0) { dma_buffer->len = offset_e + sizeof(JPEG_EOI_MARKER); return dma_buffer; From bea12e75560121a1c88a2b87ec342685198fd0be Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Wed, 23 Jul 2025 18:56:17 +0200 Subject: [PATCH 75/90] cam_hal: drop caches for psram_mode frames before hand-off to application MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Invalidate data-cache lines for the image captured via dma (in psram_mode) before the buffer is handed off to the application. This ensures the the CPU will read the correct data from the PSRAM instead of cached segments from the previous image stored in this buffer. Other work: - The cache invalidation was refactored into `cam_drop_psram_cache()` Performance consideration: On an ESP32-S3 @ 240 MHz with 32-byte cache lines: |------------------------------------------------| | Image size | Lines flushed | Cycles | Time | |------------|---------------|--------|----------| | 100 KiB | 3 200 | 16 000 | 66.7 µs | | 300 KiB | 9 600 | 48 000 | 200 µs | |________________________________________________| --- driver/cam_hal.c | 51 +++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 73049d51ec..cf9f1ace9a 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -92,6 +92,24 @@ static inline size_t dcache_line_size(void) #endif } +/* + * Invalidate CPU data cache lines that cover a region in PSRAM which + * has just been written by DMA. This guarantees subsequent CPU reads + * fetch the fresh data from PSRAM rather than stale cache contents. + * Both address and length are aligned to the data cache line size. + */ +static inline void cam_drop_psram_cache(void *addr, size_t len) +{ + size_t line = dcache_line_size(); + if (line == 0) { + line = 32; /* sane fallback */ + } + uintptr_t start = (uintptr_t)addr & ~(line - 1); + size_t sync_len = (len + ((uintptr_t)addr - start) + line - 1) & ~(line - 1); + esp_cache_msync((void *)start, sync_len, + ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_INVALIDATE); +} + /* Throttle repeated warnings printed from tight loops / ISRs. * * counter – static DRAM/IRAM uint16_t you pass in @@ -256,15 +274,7 @@ static void cam_task(void *arg) probe_len = CAM_SOI_PROBE_BYTES; } /* Invalidate cache lines for the DMA buffer before probing */ - size_t line = dcache_line_size(); - if (line == 0) { - line = 32; /* sane fallback */ - } - uintptr_t addr = (uintptr_t)frame_buffer_event->buf; - uintptr_t start = addr & ~(line - 1); - size_t sync_len = (probe_len + (addr - start) + line - 1) & ~(line - 1); - esp_cache_msync((void *)start, sync_len, - ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + cam_drop_psram_cache(frame_buffer_event->buf, probe_len); uint8_t soi_probe[CAM_SOI_PROBE_BYTES]; memcpy(soi_probe, frame_buffer_event->buf, probe_len); @@ -684,15 +694,7 @@ camera_fb_t *cam_take(TickType_t timeout) if (probe_len == 0) { goto skip_eoi_check; } - size_t line = dcache_line_size(); - if (line == 0) { - line = 32; /* sane fallback */ - } - uintptr_t addr = (uintptr_t)(dma_buffer->buf + dma_buffer->len - probe_len); - uintptr_t start = addr & ~(line - 1); - size_t sync_len = (probe_len + (addr - start) + line - 1) & ~(line - 1); - esp_cache_msync((void *)start, sync_len, - ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + cam_drop_psram_cache(dma_buffer->buf + dma_buffer->len - probe_len, probe_len); uint8_t eoi_probe[CAM_EOI_PROBE_BYTES]; memcpy(eoi_probe, dma_buffer->buf + dma_buffer->len - probe_len, probe_len); @@ -704,13 +706,17 @@ camera_fb_t *cam_take(TickType_t timeout) offset_e = cam_verify_jpeg_eoi(dma_buffer->buf, dma_buffer->len); } -skip_eoi_check: - if (offset_e >= 0) { dma_buffer->len = offset_e + sizeof(JPEG_EOI_MARKER); + if (cam_obj->psram_mode) { + /* DMA may bypass cache, ensure full frame is visible */ + cam_drop_psram_cache(dma_buffer->buf, dma_buffer->len); + } return dma_buffer; } +skip_eoi_check: + CAM_WARN_THROTTLE(warn_eoi_miss_cnt, "NO-EOI - JPEG end marker missing"); cam_give(dma_buffer); @@ -721,6 +727,11 @@ camera_fb_t *cam_take(TickType_t timeout) dma_buffer->len = ll_cam_memcpy(cam_obj, dma_buffer->buf, dma_buffer->buf, dma_buffer->len); } + if (cam_obj->psram_mode) { + /* DMA may bypass cache, ensure full frame is visible to the app */ + cam_drop_psram_cache(dma_buffer->buf, dma_buffer->len); + } + return dma_buffer; } } From 5bb9bfe5e4230c4045c8a75fc8c9876197851b0e Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Thu, 31 Jul 2025 11:21:35 +0200 Subject: [PATCH 76/90] rework psram_mode frame buffer overflow check - Do this check only in psram_mode - Improve code comment, to make clear that it will stop early on copy operations - Increase fb size by one dma_half_buffer_size in psram_mode Rationale: In psram_mode we will be called after each memory copy operation is completed, by the CAM_IN_SUC_EOF event. Since we cannot predict at this point, if the camera will copy another dma_half_buffer_size into the fb, we need to abort here, if there's not enough size in the fb for the next copy operation. This assumption means, we will abort even if this is indeed the last copy operation the camera did, and the next event will be a CAM_VSYNC, notifying us, that the copy operation is completed. Since we cannot predict this, we need one dma_half_buffer_size more space in the fb in psram_mode to allow images of the same size in psram_mode as in the regular mode. --- driver/cam_hal.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 877971afc8..cc3ddf97ad 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -17,6 +17,7 @@ #include #include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "ll_cam.h" #include "cam_hal.h" @@ -208,6 +209,18 @@ static void cam_task(void *arg) &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); + } else { + // stop if the next DMA copy would exceed the framebuffer slot + // size, since we're called only after the copy occurs + // This effectively reduces maximum usable frame buffer size + // by one DMA operation, as we can't predict here, if the next + // cam event will be a VSYNC + if (cnt + 1 >= cam_obj->frame_copy_cnt) { + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: DMA overflow\r\n")); + ll_cam_stop(cam_obj); + cam_obj->state = CAM_STATE_IDLE; + continue; + } } //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) { @@ -215,14 +228,6 @@ static void cam_task(void *arg) cam_obj->state = CAM_STATE_IDLE; } cnt++; - // stop when too many DMA copies occur so the PSRAM - // framebuffer slot doesn't overflow from runaway transfers - if (cnt >= cam_obj->frame_copy_cnt) { - ESP_LOGE(TAG, "DMA overflow"); - ll_cam_stop(cam_obj); - cam_obj->state = CAM_STATE_IDLE; - continue; - } } else if (cam_event == CAM_VSYNC_EVENT) { //DBG_PIN_SET(1); @@ -320,6 +325,9 @@ static esp_err_t cam_dma_config(const camera_config_t *config) 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 + if (cam_obj->psram_mode) { + cam_obj->frame_copy_cnt++; + } ESP_LOGI(TAG, "buffer_size: %d, half_buffer_size: %d, node_buffer_size: %d, node_cnt: %d, total_cnt: %d", (int) cam_obj->dma_buffer_size, (int) cam_obj->dma_half_buffer_size, (int) cam_obj->dma_node_buffer_size, @@ -338,6 +346,7 @@ static esp_err_t cam_dma_config(const camera_config_t *config) if (cam_obj->fb_size < cam_obj->recv_size) { fb_size = cam_obj->recv_size; } + fb_size += cam_obj->dma_half_buffer_size; } /* Allocate memory for frame buffer */ From c1c0da435eac3bb7037342991d88ec3ec1f3d4b8 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Mon, 4 Aug 2025 19:42:06 +0200 Subject: [PATCH 77/90] replace Log message handler with a more lightweight version in an effort to reduce stack usage --- driver/cam_hal.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 877971afc8..d391d95da5 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -17,6 +17,7 @@ #include #include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "ll_cam.h" #include "cam_hal.h" @@ -200,7 +201,7 @@ static void cam_task(void *arg) 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"); + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: FB-OVF\r\n")); ll_cam_stop(cam_obj); continue; } @@ -232,7 +233,7 @@ static void cam_task(void *arg) 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"); + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: FB-OVF\r\n")); cnt--; } else { frame_buffer_event->len += ll_cam_memcpy(cam_obj, @@ -255,7 +256,7 @@ static void cam_task(void *arg) } 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, (unsigned) cam_obj->fb_size); + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: FB-SIZE: %u != %u\r\n"), frame_buffer_event->len, (unsigned) cam_obj->fb_size); } } //send frame @@ -266,14 +267,14 @@ static void cam_task(void *arg) //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"); + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: FBQ-SND\r\n")); } //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"); + ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: FBQ-RCV\r\n")); } } } From 181004ceb39fd8703aaa97faa951a89d7e8c3b2d Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Mon, 4 Aug 2025 19:50:37 +0200 Subject: [PATCH 78/90] increase camera task stack size --- Kconfig | 2 +- driver/cam_hal.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Kconfig b/Kconfig index d4701dc7cf..5961e61d3d 100755 --- a/Kconfig +++ b/Kconfig @@ -187,7 +187,7 @@ menu "Camera configuration" config CAMERA_TASK_STACK_SIZE int "CAM task stack size" - default 2048 + default 4096 help Camera task stack size diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 877971afc8..bf7168316f 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -37,7 +37,7 @@ #if CONFIG_CAMERA_TASK_STACK_SIZE #define CAM_TASK_STACK CONFIG_CAMERA_TASK_STACK_SIZE #else -#define CAM_TASK_STACK (2*1024) +#define CAM_TASK_STACK (4*1024) #endif static const char *TAG = "cam_hal"; From 61a7e37b92819d06d760ee2d75d87f3c53cef016 Mon Sep 17 00:00:00 2001 From: Vilem Zavodny Date: Tue, 5 Aug 2025 09:59:13 +0200 Subject: [PATCH 79/90] fix: Fixed GPIO implicit declaration --- CMakeLists.txt | 4 ++++ target/esp32/ll_cam.c | 1 + target/esp32s2/ll_cam.c | 1 + target/esp32s3/ll_cam.c | 1 + target/xclk.c | 5 ++++- 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46d3c1164b..80ac598709 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,6 +91,10 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST list(APPEND srcs driver/sccb.c) endif() + if (idf_version VERSION_GREATER_EQUAL "6.0") + list(APPEND priv_requires esp_driver_gpio) + endif() + endif() idf_component_register( diff --git a/target/esp32/ll_cam.c b/target/esp32/ll_cam.c index 73c979768b..414c79595e 100644 --- a/target/esp32/ll_cam.c +++ b/target/esp32/ll_cam.c @@ -39,6 +39,7 @@ static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num) #endif #if (ESP_IDF_VERSION_MAJOR >= 5) +#include "driver/gpio.h" #define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE #define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE #define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c) diff --git a/target/esp32s2/ll_cam.c b/target/esp32s2/ll_cam.c index b02116162d..0d5f59a35d 100644 --- a/target/esp32s2/ll_cam.c +++ b/target/esp32s2/ll_cam.c @@ -26,6 +26,7 @@ #endif #if (ESP_IDF_VERSION_MAJOR >= 5) +#include "driver/gpio.h" #define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE #define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE #define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c) diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index f5c1aa0e7a..b54445521b 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -27,6 +27,7 @@ #include "esp_rom_gpio.h" #if (ESP_IDF_VERSION_MAJOR >= 5) +#include "driver/gpio.h" #include "soc/gpio_sig_map.h" #include "soc/gpio_periph.h" #include "soc/io_mux_reg.h" diff --git a/target/xclk.c b/target/xclk.c index 7ee155b1fe..b8ae9af0ae 100755 --- a/target/xclk.c +++ b/target/xclk.c @@ -51,7 +51,10 @@ esp_err_t camera_enable_out_clock(const camera_config_t* config) ch_conf.gpio_num = config->pin_xclk; ch_conf.speed_mode = LEDC_LOW_SPEED_MODE; ch_conf.channel = config->ledc_channel; - ch_conf.intr_type = LEDC_INTR_DISABLE; +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6,0,0) + // no need to explicitly configure interrupt, handled in the driver (IDF v6.0 and above) + ch_conf.intr_type = LEDC_INTR_DISABLE; +#endif ch_conf.timer_sel = config->ledc_timer; ch_conf.duty = 1; ch_conf.hpoint = 0; From 38bb948445b4cea1931a68aa42b113d9ceffdcf3 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Mon, 4 Aug 2025 00:43:19 +0200 Subject: [PATCH 80/90] cam_hal: PSRAM: forward-scan last half-buffer to find JPEG EOI reliably When `psram_mode` is active, `cam_take()` used to copy only 32 bytes from PSRAM and scan backward for the JPEG EOI (FF D9). Because DMA copies in dma_node_buffer_size length, the true EOI often falls earlier and was missed, leading to repeated "NO-EOI" resets. This change: - Drops the 32-byte SRAM copy; scan directly in PSRAM after cache invalidation. - Searches the last dma_node_buffer_size **plus 1 byte** (marker_len-1), starting 1 byte before the final block to catch split EOIs. - Scans **forward** so we pick the earliest EOI in the current tail and avoid stale markers from a larger previous frame. - Uses a fast byte-hunt for 0xFF with a 2-byte verification for "FF D9". SRAM DMA mode keeps the simple backward check (we only accept EOI at offset 0 there), so no extra complexity or perf work is added. Net effect: robust EOI detection without garbage tails and fewer spurious "NO-EOI" resets in PSRAM captures. --- driver/cam_hal.c | 97 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 66f3782110..b65cdac8d0 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -69,16 +69,10 @@ static portMUX_TYPE g_psram_dma_lock = portMUX_INITIALIZER_UNLOCKED; #ifndef CAM_SOI_PROBE_BYTES #define CAM_SOI_PROBE_BYTES 32 #endif - -/* Number of bytes copied to SRAM for EOI validation when capturing - * directly to PSRAM. Tunable to probe more of the frame tail if needed. */ -#ifndef CAM_EOI_PROBE_BYTES -#define CAM_EOI_PROBE_BYTES 32 -#endif - /* - * PSRAM DMA may bypass the CPU cache. Always call esp_cache_msync() on the - * SOI probe region so cached reads see the data written by DMA. + * PSRAM DMA may bypass the CPU cache. Always call esp_cache_msync() on + * PSRAM regions that the CPU will read so cached reads see the data written + * by DMA. */ static inline size_t dcache_line_size(void) @@ -130,12 +124,19 @@ static inline void cam_drop_psram_cache(void *addr, size_t len) #define CAM_WARN_THROTTLE(counter, first) do { (void)(counter); } while (0) #endif -/* JPEG markers in little-endian order (ESP32). */ +/* JPEG markers (byte-order independent). */ static const uint8_t JPEG_SOI_MARKER[] = {0xFF, 0xD8, 0xFF}; /* SOI = FF D8 FF */ #define JPEG_SOI_MARKER_LEN (3) -static const uint16_t JPEG_EOI_MARKER = 0xD9FF; /* EOI = FF D9 */ +static const uint8_t JPEG_EOI_BYTES[] = {0xFF, 0xD9}; /* EOI = FF D9 */ #define JPEG_EOI_MARKER_LEN (2) +/* Compute the scan window for JPEG EOI detection in PSRAM. */ +static inline size_t eoi_probe_window(size_t half, size_t frame_len) +{ + size_t w = half + (JPEG_EOI_MARKER_LEN - 1); + return w > frame_len ? frame_len : w; +} + static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length) { static uint16_t warn_soi_miss_cnt = 0; @@ -157,19 +158,54 @@ static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length) return -1; } -static int cam_verify_jpeg_eoi(const uint8_t *inbuf, uint32_t length) +static int cam_verify_jpeg_eoi(const uint8_t *inbuf, uint32_t length, bool search_forward) { if (length < JPEG_EOI_MARKER_LEN) { return -1; } - int offset = -1; - uint8_t *dptr = (uint8_t *)inbuf + length - JPEG_EOI_MARKER_LEN; - while (dptr > inbuf) { - if (memcmp(dptr, &JPEG_EOI_MARKER, JPEG_EOI_MARKER_LEN) == 0) { - offset = dptr - inbuf; - //ESP_LOGW(TAG, "EOI: %d", length - (offset + 2)); - return offset; + if (search_forward) { + /* Scan forward to honor the earliest marker in the buffer. This avoids + * returning an EOI that belongs to a larger previous frame when the tail + * of that frame still resides in PSRAM. JPEG data is pseudo random, so + * the first marker byte appears rarely; test four positions per load to + * reduce memory traffic. */ + const uint8_t *pat = JPEG_EOI_BYTES; + const uint32_t A = pat[0] * 0x01010101u; + const uint32_t ONE = 0x01010101u; + const uint32_t HIGH = 0x80808080u; + uint32_t i = 0; + while (i + 4 <= length) { + uint32_t w; + memcpy(&w, inbuf + i, 4); /* unaligned load is allowed */ + uint32_t x = w ^ A; /* identify bytes equal to first marker byte */ + uint32_t m = (~x & (x - ONE)) & HIGH; /* mask has high bit set for candidate bytes */ + while (m) { /* handle only candidates to avoid unnecessary memcmp calls */ + unsigned off = __builtin_ctz(m) >> 3; + uint32_t pos = i + off; + if (pos + JPEG_EOI_MARKER_LEN <= length && + memcmp(inbuf + pos, pat, JPEG_EOI_MARKER_LEN) == 0) { + return pos; + } + m &= m - 1; /* clear processed candidate */ + } + i += 4; + } + for (; i + JPEG_EOI_MARKER_LEN <= length; i++) { + if (memcmp(inbuf + i, pat, JPEG_EOI_MARKER_LEN) == 0) { + return i; + } + } + return -1; + } + + const uint8_t *dptr = inbuf + length - JPEG_EOI_MARKER_LEN; + while (dptr >= inbuf) { + if (memcmp(dptr, JPEG_EOI_BYTES, JPEG_EOI_MARKER_LEN) == 0) { + return dptr - inbuf; + } + if (dptr == inbuf) { + break; } dptr--; } @@ -696,27 +732,26 @@ camera_fb_t *cam_take(TickType_t timeout) /* find the end marker for JPEG. Data after that can be discarded */ int offset_e = -1; if (cam_obj->psram_mode) { - size_t probe_len = dma_buffer->len; - if (probe_len > CAM_EOI_PROBE_BYTES) { - probe_len = CAM_EOI_PROBE_BYTES; - } - if (probe_len == 0) { + /* Search forward from (JPEG_EOI_MARKER_LEN - 1) bytes before the final + * DMA block. We prefer forward search to pick the earliest EOI in the + * last DMA node, avoiding stale markers from a larger prior frame. */ + size_t probe_len = eoi_probe_window(cam_obj->dma_node_buffer_size, + dma_buffer->len); + if (probe_len < JPEG_EOI_MARKER_LEN) { goto skip_eoi_check; } - cam_drop_psram_cache(dma_buffer->buf + dma_buffer->len - probe_len, probe_len); - - uint8_t eoi_probe[CAM_EOI_PROBE_BYTES]; - memcpy(eoi_probe, dma_buffer->buf + dma_buffer->len - probe_len, probe_len); - int off = cam_verify_jpeg_eoi(eoi_probe, probe_len); + uint8_t *probe_start = dma_buffer->buf + dma_buffer->len - probe_len; + cam_drop_psram_cache(probe_start, probe_len); + int off = cam_verify_jpeg_eoi(probe_start, probe_len, true); if (off >= 0) { offset_e = dma_buffer->len - probe_len + off; } } else { - offset_e = cam_verify_jpeg_eoi(dma_buffer->buf, dma_buffer->len); + offset_e = cam_verify_jpeg_eoi(dma_buffer->buf, dma_buffer->len, false); } if (offset_e >= 0) { - dma_buffer->len = offset_e + sizeof(JPEG_EOI_MARKER); + dma_buffer->len = offset_e + JPEG_EOI_MARKER_LEN; if (cam_obj->psram_mode) { /* DMA may bypass cache, ensure full frame is visible */ cam_drop_psram_cache(dma_buffer->buf, dma_buffer->len); From 91f9c993fbe514c9c42891f353c5ed6ac065cbfe Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Thu, 7 Aug 2025 10:12:35 +0200 Subject: [PATCH 81/90] fix: Define IDF >=5.1 requirement esp32-camera now depends on esp_mm which was introduced in IDF 5.1. --- idf_component.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/idf_component.yml b/idf_component.yml index 8b454fcd01..0dea1cc30b 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -4,6 +4,7 @@ issues: https://github.com/espressif/esp32-camera/issues documentation: https://github.com/espressif/esp32-camera/tree/main/README.md repository: https://github.com/espressif/esp32-camera.git dependencies: + idf: ">=5.1" esp_jpeg: version: "^1.3.0" public: true From 0d1c54206fb8cb31d96b5193db3bcdc25eb5316b Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Fri, 22 Aug 2025 10:54:45 +0300 Subject: [PATCH 82/90] Silence HAL logs if logs are disabled --- driver/cam_hal.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index b65cdac8d0..768d93ae87 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -40,7 +40,12 @@ #include "esp32s3/rom/ets_sys.h" #endif #endif // ESP_IDF_VERSION_MAJOR -#define ESP_CAMERA_ETS_PRINTF ets_printf + +#if CONFIG_LOG_DEFAULT_LEVEL_NONE +#define ESP_CAMERA_ETS_PRINTF(f, ...) +#else +#define ESP_CAMERA_ETS_PRINTF(f, ...) ets_printf(f, ##__VA_ARGS__) +#endif #if CONFIG_CAMERA_TASK_STACK_SIZE #define CAM_TASK_STACK CONFIG_CAMERA_TASK_STACK_SIZE From d5a80f4d096254cebec88fe90449c177a92171da Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 18 Sep 2025 19:05:55 +1200 Subject: [PATCH 83/90] Use renamed sdkconfig option --- conversions/jpge.cpp | 2 +- conversions/to_bmp.c | 2 +- conversions/to_jpg.cpp | 2 +- examples/camera_example/sdkconfig.defaults | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/conversions/jpge.cpp b/conversions/jpge.cpp index dd6790e63b..a8586cea91 100644 --- a/conversions/jpge.cpp +++ b/conversions/jpge.cpp @@ -30,7 +30,7 @@ namespace jpge { return b; } // check if SPIRAM is enabled and allocate on SPIRAM if allocatable -#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) +#if ((CONFIG_SPIRAM || CONFIG_SPIRAM_SUPPORT) && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) return heap_caps_malloc(nSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); #else return NULL; diff --git a/conversions/to_bmp.c b/conversions/to_bmp.c index 7199b228db..97e790b555 100644 --- a/conversions/to_bmp.c +++ b/conversions/to_bmp.c @@ -54,7 +54,7 @@ typedef struct { static void *_malloc(size_t size) { // check if SPIRAM is enabled and allocate on SPIRAM if allocatable -#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) +#if ((CONFIG_SPIRAM || CONFIG_SPIRAM_SUPPORT) && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); #endif // try allocating in internal memory diff --git a/conversions/to_jpg.cpp b/conversions/to_jpg.cpp index 24cc2989cd..a4ac804d1d 100644 --- a/conversions/to_jpg.cpp +++ b/conversions/to_jpg.cpp @@ -37,7 +37,7 @@ static void *_malloc(size_t size) } // check if SPIRAM is enabled and is allocatable -#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) +#if ((CONFIG_SPIRAM || CONFIG_SPIRAM_SUPPORT) && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)) return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); #endif return NULL; diff --git a/examples/camera_example/sdkconfig.defaults b/examples/camera_example/sdkconfig.defaults index e5ac4557ac..000a453e65 100644 --- a/examples/camera_example/sdkconfig.defaults +++ b/examples/camera_example/sdkconfig.defaults @@ -9,9 +9,9 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_SPIRAM=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 - From b3c2aa273a88c7debf2b1683448933ca2b28aad9 Mon Sep 17 00:00:00 2001 From: Vilem Zavodny <98878239+espzav@users.noreply.github.com> Date: Thu, 9 Oct 2025 12:51:23 +0200 Subject: [PATCH 84/90] fix: Fix requires by IDF6 (#807) * fix: Fix requires by IDF6 * Refactor CMakeLists.txt for dependency handling * Update CMakeLists to require 'driver' component * Modify priv_requires to use list append * Remove esp_driver_ledc from private requirements --------- Co-authored-by: Me No Dev --- CMakeLists.txt | 16 +++++++++------- idf_component.yml | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 905687ae75..53e29fb10e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ # get IDF version for comparison set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}") +set(priv_requires "") + # set conversion sources set(srcs conversions/yuv.c @@ -18,8 +20,6 @@ set(include_dirs conversions/include ) -set(COMPONENT_REQUIRES driver) - # set driver sources only for supported platforms if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3") list(APPEND srcs @@ -75,7 +75,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST ) endif() - set(priv_requires freertos nvs_flash esp_mm) + list(APPEND priv_requires freertos nvs_flash esp_mm) set(min_version_for_esp_timer "4.2") if (idf_version VERSION_GREATER_EQUAL min_version_for_esp_timer) @@ -91,16 +91,18 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST list(APPEND srcs driver/sccb.c) endif() - if (idf_version VERSION_GREATER_EQUAL "6.0") - list(APPEND priv_requires esp_driver_gpio) - endif() +endif() +set(req driver) +if (idf_version VERSION_GREATER_EQUAL "6.0") + list(APPEND priv_requires esp_driver_gpio esp_driver_spi esp_driver_i2c) + list(APPEND req esp_driver_ledc) endif() idf_component_register( SRCS ${srcs} INCLUDE_DIRS ${include_dirs} PRIV_INCLUDE_DIRS ${priv_include_dirs} - REQUIRES driver # due to include of driver/gpio.h in esp_camera.h + REQUIRES ${req} PRIV_REQUIRES ${priv_requires} ) diff --git a/idf_component.yml b/idf_component.yml index 0dea1cc30b..0ebeddb392 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -6,5 +6,5 @@ repository: https://github.com/espressif/esp32-camera.git dependencies: idf: ">=5.1" esp_jpeg: - version: "^1.3.0" + version: "^1.3.1" public: true From 329476784499fd9b9548c6dd2b98f40f7585b631 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Thu, 6 Nov 2025 10:22:55 +0200 Subject: [PATCH 85/90] Fix compilation of GDMA and periph enable in IDF 6 (#816) --- target/esp32/ll_cam.c | 10 ++++++++++ target/esp32s2/ll_cam.c | 9 +++++++++ target/esp32s3/ll_cam.c | 30 ++++++++++++++++++++++-------- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/target/esp32/ll_cam.c b/target/esp32/ll_cam.c index 414c79595e..1d31dc60b9 100644 --- a/target/esp32/ll_cam.c +++ b/target/esp32/ll_cam.c @@ -45,6 +45,11 @@ static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num) #define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c) #endif +#if (ESP_IDF_VERSION_MAJOR > 5) +#include "soc/dport_access.h" +#include "soc/dport_reg.h" +#endif + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 2) #define ets_delay_us esp_rom_delay_us #endif @@ -289,7 +294,12 @@ 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) { // Enable and configure I2S peripheral +#if ESP_IDF_VERSION_MAJOR > 5 + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST); +#else periph_module_enable(PERIPH_I2S0_MODULE); +#endif I2S0.conf.rx_reset = 1; I2S0.conf.rx_reset = 0; diff --git a/target/esp32s2/ll_cam.c b/target/esp32s2/ll_cam.c index 0d5f59a35d..c708dc3fbb 100644 --- a/target/esp32s2/ll_cam.c +++ b/target/esp32s2/ll_cam.c @@ -33,6 +33,10 @@ #define ets_delay_us(a) esp_rom_delay_us(a) #endif +#if (ESP_IDF_VERSION_MAJOR > 5) +#include "soc/dport_access.h" +#endif + static const char *TAG = "s2 ll_cam"; #define I2S_ISR_ENABLE(i) {I2S0.int_clr.i = 1;I2S0.int_ena.i = 1;} @@ -136,7 +140,12 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config) if(err != ESP_OK) { return err; } +#if ESP_IDF_VERSION_MAJOR > 5 + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN0_REG, DPORT_I2S0_RST); +#else periph_module_enable(PERIPH_I2S0_MODULE); +#endif // Configure the clock I2S0.clkm_conf.clkm_div_num = 2; // 160MHz / 2 = 80MHz I2S0.clkm_conf.clkm_div_b = 0; diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index b54445521b..a3b474dfad 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -18,7 +18,6 @@ #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 "hal/clk_gate_ll.h" #include "esp_private/gdma.h" @@ -36,6 +35,12 @@ #define ets_delay_us(a) esp_rom_delay_us(a) #endif +#if (ESP_IDF_VERSION_MAJOR > 5) +#include "soc/dport_access.h" +#else +#include "soc/gdma_periph.h" +#endif + #if !defined(SOC_GDMA_PAIRS_PER_GROUP) && defined(SOC_GDMA_PAIRS_PER_GROUP_MAX) #define SOC_GDMA_PAIRS_PER_GROUP SOC_GDMA_PAIRS_PER_GROUP_MAX #endif @@ -235,16 +240,20 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam) // } // } +#if ESP_IDF_VERSION_MAJOR > 5 + if (!(DPORT_REG_GET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST) == 0 && + DPORT_REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN) != 0)) { + DPORT_CLEAR_PERI_REG_MASK(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN); + DPORT_SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST); + DPORT_SET_PERI_REG_MASK(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST); + } +#else if (!periph_ll_periph_enabled(PERIPH_GDMA_MODULE)) { periph_ll_disable_clk_set_rst(PERIPH_GDMA_MODULE); periph_ll_enable_clk_clear_rst(PERIPH_GDMA_MODULE); } - // 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); - // } +#endif ll_cam_dma_reset(cam); return ESP_OK; } @@ -405,7 +414,12 @@ 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) { esp_err_t ret = ESP_OK; - ret = esp_intr_alloc_intrstatus(gdma_periph_signals.groups[0].pairs[cam->dma_num].rx_irq_id, + ret = esp_intr_alloc_intrstatus( +#if (ESP_IDF_VERSION_MAJOR > 5) + ETS_DMA_IN_CH0_INTR_SOURCE + cam->dma_num, +#else + gdma_periph_signals.groups[0].pairs[cam->dma_num].rx_irq_id, +#endif ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | CAMERA_ISR_IRAM_FLAG, (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); From 00c304226e38a4f179682894e87b8798b5531a15 Mon Sep 17 00:00:00 2001 From: zhufangluo <110405564+jimforr@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:28:00 +0800 Subject: [PATCH 86/90] fix: add prefix for sensor functions name (#815) Co-authored-by: zhufangluo Co-authored-by: Me No Dev --- driver/esp_camera.c | 34 +++++++++++++++--------------- sensors/bf20a6.c | 4 ++-- sensors/bf3005.c | 4 ++-- sensors/gc0308.c | 4 ++-- sensors/gc032a.c | 4 ++-- sensors/gc2145.c | 4 ++-- sensors/hm0360.c | 4 ++-- sensors/hm1055.c | 4 ++-- sensors/mega_ccm.c | 4 ++-- sensors/nt99141.c | 4 ++-- sensors/ov2640.c | 4 ++-- sensors/ov3660.c | 4 ++-- sensors/ov5640.c | 4 ++-- sensors/ov7670.c | 4 ++-- sensors/ov7725.c | 4 ++-- sensors/private_include/bf20a6.h | 4 ++-- sensors/private_include/bf3005.h | 4 ++-- sensors/private_include/gc0308.h | 4 ++-- sensors/private_include/gc032a.h | 4 ++-- sensors/private_include/gc2145.h | 4 ++-- sensors/private_include/hm0360.h | 4 ++-- sensors/private_include/hm1055.h | 4 ++-- sensors/private_include/mega_ccm.h | 4 ++-- sensors/private_include/nt99141.h | 4 ++-- sensors/private_include/ov2640.h | 4 ++-- sensors/private_include/ov3660.h | 4 ++-- sensors/private_include/ov5640.h | 4 ++-- sensors/private_include/ov7670.h | 4 ++-- sensors/private_include/ov7725.h | 4 ++-- sensors/private_include/sc030iot.h | 4 ++-- sensors/private_include/sc031gs.h | 4 ++-- sensors/private_include/sc101iot.h | 4 ++-- sensors/sc030iot.c | 4 ++-- sensors/sc031gs.c | 4 ++-- sensors/sc101iot.c | 4 ++-- 35 files changed, 85 insertions(+), 85 deletions(-) diff --git a/driver/esp_camera.c b/driver/esp_camera.c index 6fb7a98024..4dc8f5a443 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -112,55 +112,55 @@ typedef struct { static const sensor_func_t g_sensors[] = { #if CONFIG_OV7725_SUPPORT - {ov7725_detect, ov7725_init}, + {esp32_camera_ov7725_detect, esp32_camera_ov7725_init}, #endif #if CONFIG_OV7670_SUPPORT - {ov7670_detect, ov7670_init}, + {esp32_camera_ov7670_detect, esp32_camera_ov7670_init}, #endif #if CONFIG_OV2640_SUPPORT - {ov2640_detect, ov2640_init}, + {esp32_camera_ov2640_detect, esp32_camera_ov2640_init}, #endif #if CONFIG_OV3660_SUPPORT - {ov3660_detect, ov3660_init}, + {esp32_camera_ov3660_detect, esp32_camera_ov3660_init}, #endif #if CONFIG_OV5640_SUPPORT - {ov5640_detect, ov5640_init}, + {esp32_camera_ov5640_detect, esp32_camera_ov5640_init}, #endif #if CONFIG_NT99141_SUPPORT - {nt99141_detect, nt99141_init}, + {esp32_camera_nt99141_detect, esp32_camera_nt99141_init}, #endif #if CONFIG_GC2145_SUPPORT - {gc2145_detect, gc2145_init}, + {esp32_camera_gc2145_detect, esp32_camera_gc2145_init}, #endif #if CONFIG_GC032A_SUPPORT - {gc032a_detect, gc032a_init}, + {esp32_camera_gc032a_detect, esp32_camera_gc032a_init}, #endif #if CONFIG_GC0308_SUPPORT - {gc0308_detect, gc0308_init}, + {esp32_camera_gc0308_detect, esp32_camera_gc0308_init}, #endif #if CONFIG_BF3005_SUPPORT - {bf3005_detect, bf3005_init}, + {esp32_camera_bf3005_detect, esp32_camera_bf3005_init}, #endif #if CONFIG_BF20A6_SUPPORT - {bf20a6_detect, bf20a6_init}, + {esp32_camera_bf20a6_detect, esp32_camera_bf20a6_init}, #endif #if CONFIG_SC101IOT_SUPPORT - {sc101iot_detect, sc101iot_init}, + {esp32_camera_sc101iot_detect, esp32_camera_sc101iot_init}, #endif #if CONFIG_SC030IOT_SUPPORT - {sc030iot_detect, sc030iot_init}, + {esp32_camera_sc030iot_detect, esp32_camera_sc030iot_init}, #endif #if CONFIG_SC031GS_SUPPORT - {sc031gs_detect, sc031gs_init}, + {esp32_camera_sc031gs_detect, esp32_camera_sc031gs_init}, #endif #if CONFIG_MEGA_CCM_SUPPORT - {mega_ccm_detect, mega_ccm_init}, + {esp32_camera_mega_ccm_detect, esp32_camera_mega_ccm_init}, #endif #if CONFIG_HM1055_SUPPORT - {hm1055_detect, hm1055_init}, + {esp32_camera_hm1055_detect, esp32_camera_hm1055_init}, #endif #if CONFIG_HM0360_SUPPORT - {hm0360_detect, hm0360_init}, + {esp32_camera_hm0360_detect, esp32_camera_hm0360_init}, #endif }; diff --git a/sensors/bf20a6.c b/sensors/bf20a6.c index f8eb127d54..99c48f9f80 100644 --- a/sensors/bf20a6.c +++ b/sensors/bf20a6.c @@ -347,7 +347,7 @@ static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val) return -1; } -int bf20a6_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_bf20a6_detect(int slv_addr, sensor_id_t *id) { if (BF20A6_SCCB_ADDR == slv_addr) { uint8_t MIDL = SCCB_Read(slv_addr, SENSOR_ID_LOW); @@ -363,7 +363,7 @@ int bf20a6_detect(int slv_addr, sensor_id_t *id) return 0; } -int bf20a6_init(sensor_t *sensor) +int esp32_camera_bf20a6_init(sensor_t *sensor) { sensor->init_status = init_status; sensor->reset = reset; diff --git a/sensors/bf3005.c b/sensors/bf3005.c index 5a6166e49d..b492f35331 100644 --- a/sensors/bf3005.c +++ b/sensors/bf3005.c @@ -476,7 +476,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int bf3005_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_bf3005_detect(int slv_addr, sensor_id_t *id) { if (BF3005_SCCB_ADDR == slv_addr) { uint16_t PID = SCCB_Read(slv_addr, 0xFC); @@ -493,7 +493,7 @@ int bf3005_detect(int slv_addr, sensor_id_t *id) return 0; } -int bf3005_init(sensor_t *sensor) +int esp32_camera_bf3005_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; diff --git a/sensors/gc0308.c b/sensors/gc0308.c index 337626cc14..469f0385e2 100644 --- a/sensors/gc0308.c +++ b/sensors/gc0308.c @@ -411,7 +411,7 @@ static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val) return -1; } -int gc0308_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_gc0308_detect(int slv_addr, sensor_id_t *id) { if (GC0308_SCCB_ADDR == slv_addr) { write_reg(slv_addr, 0xfe, 0x00); @@ -426,7 +426,7 @@ int gc0308_detect(int slv_addr, sensor_id_t *id) return 0; } -int gc0308_init(sensor_t *sensor) +int esp32_camera_gc0308_init(sensor_t *sensor) { sensor->init_status = init_status; sensor->reset = reset; diff --git a/sensors/gc032a.c b/sensors/gc032a.c index 612e17b1ed..669960dc64 100644 --- a/sensors/gc032a.c +++ b/sensors/gc032a.c @@ -328,7 +328,7 @@ static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val) return -1; } -int gc032a_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_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); @@ -344,7 +344,7 @@ int gc032a_detect(int slv_addr, sensor_id_t *id) return 0; } -int gc032a_init(sensor_t *sensor) +int esp32_camera_gc032a_init(sensor_t *sensor) { sensor->init_status = init_status; sensor->reset = reset; diff --git a/sensors/gc2145.c b/sensors/gc2145.c index 3a066cd941..1ab50c21f5 100644 --- a/sensors/gc2145.c +++ b/sensors/gc2145.c @@ -414,7 +414,7 @@ static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val) return -1; } -int gc2145_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_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); @@ -430,7 +430,7 @@ int gc2145_detect(int slv_addr, sensor_id_t *id) return 0; } -int gc2145_init(sensor_t *sensor) +int esp32_camera_gc2145_init(sensor_t *sensor) { sensor->init_status = init_status; sensor->reset = reset; diff --git a/sensors/hm0360.c b/sensors/hm0360.c index faf6d22580..7013902e92 100755 --- a/sensors/hm0360.c +++ b/sensors/hm0360.c @@ -418,7 +418,7 @@ static int init_status(sensor_t *sensor) return 0; } -int hm0360_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_hm0360_detect(int slv_addr, sensor_id_t *id) { if (HM1055_SCCB_ADDR == slv_addr) { uint8_t h = SCCB_Read16(slv_addr, MODEL_ID_H); @@ -435,7 +435,7 @@ int hm0360_detect(int slv_addr, sensor_id_t *id) return 0; } -int hm0360_init(sensor_t *sensor) +int esp32_camera_hm0360_init(sensor_t *sensor) { sensor->reset = reset; sensor->set_pixformat = set_pixformat; diff --git a/sensors/hm1055.c b/sensors/hm1055.c index 97c6e082dc..2529297fd5 100755 --- a/sensors/hm1055.c +++ b/sensors/hm1055.c @@ -772,7 +772,7 @@ static int init_status(sensor_t *sensor) return 0; } -int hm1055_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_hm1055_detect(int slv_addr, sensor_id_t *id) { if (HM1055_SCCB_ADDR == slv_addr) { @@ -792,7 +792,7 @@ int hm1055_detect(int slv_addr, sensor_id_t *id) return 0; } -int hm1055_init(sensor_t *sensor) +int esp32_camera_hm1055_init(sensor_t *sensor) { sensor->reset = reset; sensor->set_pixformat = set_pixformat; diff --git a/sensors/mega_ccm.c b/sensors/mega_ccm.c index 159c3922be..bdd1903542 100644 --- a/sensors/mega_ccm.c +++ b/sensors/mega_ccm.c @@ -344,7 +344,7 @@ static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val) return -1; } -int mega_ccm_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_mega_ccm_detect(int slv_addr, sensor_id_t *id) { if (MEGA_CCM_SCCB_ADDR == slv_addr) { uint8_t h = read_reg(slv_addr, SENSOR_ID_HIGH); @@ -360,7 +360,7 @@ int mega_ccm_detect(int slv_addr, sensor_id_t *id) return 0; } -int mega_ccm_init(sensor_t *sensor) +int esp32_camera_mega_ccm_init(sensor_t *sensor) { sensor->init_status = init_status; sensor->reset = reset; diff --git a/sensors/nt99141.c b/sensors/nt99141.c index 86a8b8a0b5..b440cb3bae 100644 --- a/sensors/nt99141.c +++ b/sensors/nt99141.c @@ -934,7 +934,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int nt99141_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_nt99141_detect(int slv_addr, sensor_id_t *id) { if (NT99141_SCCB_ADDR == slv_addr) { SCCB_Write16(slv_addr, 0x3008, 0x01);//bank sensor @@ -981,7 +981,7 @@ static int init_status(sensor_t *sensor) return 0; } -int nt99141_init(sensor_t *sensor) +int esp32_camera_nt99141_init(sensor_t *sensor) { sensor->reset = reset; sensor->set_pixformat = set_pixformat; diff --git a/sensors/ov2640.c b/sensors/ov2640.c index 7e3d77174d..d35aceee31 100755 --- a/sensors/ov2640.c +++ b/sensors/ov2640.c @@ -545,7 +545,7 @@ static int init_status(sensor_t *sensor){ return 0; } -int ov2640_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_ov2640_detect(int slv_addr, sensor_id_t *id) { if (OV2640_SCCB_ADDR == slv_addr) { SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor @@ -563,7 +563,7 @@ int ov2640_detect(int slv_addr, sensor_id_t *id) return 0; } -int ov2640_init(sensor_t *sensor) +int esp32_camera_ov2640_init(sensor_t *sensor) { sensor->reset = reset; sensor->init_status = init_status; diff --git a/sensors/ov3660.c b/sensors/ov3660.c index b9ebdba3ba..9bbcd6f8d7 100755 --- a/sensors/ov3660.c +++ b/sensors/ov3660.c @@ -996,7 +996,7 @@ static int init_status(sensor_t *sensor) return 0; } -int ov3660_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_ov3660_detect(int slv_addr, sensor_id_t *id) { if (OV3660_SCCB_ADDR == slv_addr) { uint8_t h = SCCB_Read16(slv_addr, 0x300A); @@ -1012,7 +1012,7 @@ int ov3660_detect(int slv_addr, sensor_id_t *id) return 0; } -int ov3660_init(sensor_t *sensor) +int esp32_camera_ov3660_init(sensor_t *sensor) { sensor->reset = reset; sensor->set_pixformat = set_pixformat; diff --git a/sensors/ov5640.c b/sensors/ov5640.c index a32b374f54..ffa5b5a97c 100755 --- a/sensors/ov5640.c +++ b/sensors/ov5640.c @@ -1073,7 +1073,7 @@ static int init_status(sensor_t *sensor) return 0; } -int ov5640_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_ov5640_detect(int slv_addr, sensor_id_t *id) { if (OV5640_SCCB_ADDR == slv_addr) { uint8_t h = SCCB_Read16(slv_addr, 0x300A); @@ -1089,7 +1089,7 @@ int ov5640_detect(int slv_addr, sensor_id_t *id) return 0; } -int ov5640_init(sensor_t *sensor) +int esp32_camera_ov5640_init(sensor_t *sensor) { sensor->reset = reset; sensor->set_pixformat = set_pixformat; diff --git a/sensors/ov7670.c b/sensors/ov7670.c index c683eda041..043df65cbd 100644 --- a/sensors/ov7670.c +++ b/sensors/ov7670.c @@ -393,7 +393,7 @@ 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) +int esp32_camera_ov7670_detect(int slv_addr, sensor_id_t *id) { if (OV7670_SCCB_ADDR == slv_addr) { SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor @@ -411,7 +411,7 @@ int ov7670_detect(int slv_addr, sensor_id_t *id) return 0; } -int ov7670_init(sensor_t *sensor) +int esp32_camera_ov7670_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; diff --git a/sensors/ov7725.c b/sensors/ov7725.c index 9418a82f3f..dd0ea62fcb 100755 --- a/sensors/ov7725.c +++ b/sensors/ov7725.c @@ -502,7 +502,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int ov7725_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_ov7725_detect(int slv_addr, sensor_id_t *id) { if (OV7725_SCCB_ADDR == slv_addr) { SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor @@ -520,7 +520,7 @@ int ov7725_detect(int slv_addr, sensor_id_t *id) return 0; } -int ov7725_init(sensor_t *sensor) +int esp32_camera_ov7725_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; diff --git a/sensors/private_include/bf20a6.h b/sensors/private_include/bf20a6.h index 8c925eb5e5..743138be7b 100644 --- a/sensors/private_include/bf20a6.h +++ b/sensors/private_include/bf20a6.h @@ -13,7 +13,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int bf20a6_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_bf20a6_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -22,6 +22,6 @@ int bf20a6_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int bf20a6_init(sensor_t *sensor); +int esp32_camera_bf20a6_init(sensor_t *sensor); #endif // __BF20A6_H__ diff --git a/sensors/private_include/bf3005.h b/sensors/private_include/bf3005.h index 3d1bfdf00d..bd562a7050 100644 --- a/sensors/private_include/bf3005.h +++ b/sensors/private_include/bf3005.h @@ -19,7 +19,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int bf3005_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_bf3005_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -28,6 +28,6 @@ int bf3005_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int bf3005_init(sensor_t *sensor); +int esp32_camera_bf3005_init(sensor_t *sensor); #endif // __BF3005_H__ \ No newline at end of file diff --git a/sensors/private_include/gc0308.h b/sensors/private_include/gc0308.h index edffca1e00..eae260320a 100644 --- a/sensors/private_include/gc0308.h +++ b/sensors/private_include/gc0308.h @@ -15,7 +15,7 @@ extern "C" { * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int gc0308_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_gc0308_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -24,7 +24,7 @@ int gc0308_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int gc0308_init(sensor_t *sensor); +int esp32_camera_gc0308_init(sensor_t *sensor); #ifdef __cplusplus } diff --git a/sensors/private_include/gc032a.h b/sensors/private_include/gc032a.h index 7679f07089..8955ed4731 100644 --- a/sensors/private_include/gc032a.h +++ b/sensors/private_include/gc032a.h @@ -17,7 +17,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int gc032a_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_gc032a_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -26,6 +26,6 @@ int gc032a_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int gc032a_init(sensor_t *sensor); +int esp32_camera_gc032a_init(sensor_t *sensor); #endif // __GC032A_H__ diff --git a/sensors/private_include/gc2145.h b/sensors/private_include/gc2145.h index 6c5b60f703..db649f9198 100644 --- a/sensors/private_include/gc2145.h +++ b/sensors/private_include/gc2145.h @@ -13,7 +13,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int gc2145_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_gc2145_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -22,6 +22,6 @@ int gc2145_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int gc2145_init(sensor_t *sensor); +int esp32_camera_gc2145_init(sensor_t *sensor); #endif // __GC2145_H__ diff --git a/sensors/private_include/hm0360.h b/sensors/private_include/hm0360.h index 822ecab2d4..2163cbe467 100755 --- a/sensors/private_include/hm0360.h +++ b/sensors/private_include/hm0360.h @@ -13,7 +13,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int hm0360_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_hm0360_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -22,6 +22,6 @@ int hm0360_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int hm0360_init(sensor_t *sensor); +int esp32_camera_hm0360_init(sensor_t *sensor); #endif // __HM1055_H__ diff --git a/sensors/private_include/hm1055.h b/sensors/private_include/hm1055.h index dd6d44d753..4ddf1f8171 100755 --- a/sensors/private_include/hm1055.h +++ b/sensors/private_include/hm1055.h @@ -13,7 +13,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int hm1055_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_hm1055_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -22,6 +22,6 @@ int hm1055_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int hm1055_init(sensor_t *sensor); +int esp32_camera_hm1055_init(sensor_t *sensor); #endif // __HM1055_H__ diff --git a/sensors/private_include/mega_ccm.h b/sensors/private_include/mega_ccm.h index bbad247b53..0f5efb21ec 100644 --- a/sensors/private_include/mega_ccm.h +++ b/sensors/private_include/mega_ccm.h @@ -17,7 +17,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int mega_ccm_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_mega_ccm_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -26,6 +26,6 @@ int mega_ccm_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int mega_ccm_init(sensor_t *sensor); +int esp32_camera_mega_ccm_init(sensor_t *sensor); #endif // __MEGA_CCM_H__ diff --git a/sensors/private_include/nt99141.h b/sensors/private_include/nt99141.h index 8b0c562b94..0e7cb52a01 100644 --- a/sensors/private_include/nt99141.h +++ b/sensors/private_include/nt99141.h @@ -20,7 +20,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int nt99141_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_nt99141_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -29,6 +29,6 @@ int nt99141_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int nt99141_init(sensor_t *sensor); +int esp32_camera_nt99141_init(sensor_t *sensor); #endif // __NT99141_H__ diff --git a/sensors/private_include/ov2640.h b/sensors/private_include/ov2640.h index 342ab21328..4622c1a004 100755 --- a/sensors/private_include/ov2640.h +++ b/sensors/private_include/ov2640.h @@ -18,7 +18,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int ov2640_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_ov2640_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -27,6 +27,6 @@ int ov2640_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int ov2640_init(sensor_t *sensor); +int esp32_camera_ov2640_init(sensor_t *sensor); #endif // __OV2640_H__ diff --git a/sensors/private_include/ov3660.h b/sensors/private_include/ov3660.h index 341d688616..1bb5beb537 100755 --- a/sensors/private_include/ov3660.h +++ b/sensors/private_include/ov3660.h @@ -20,7 +20,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int ov3660_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_ov3660_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -29,6 +29,6 @@ int ov3660_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int ov3660_init(sensor_t *sensor); +int esp32_camera_ov3660_init(sensor_t *sensor); #endif // __OV3660_H__ diff --git a/sensors/private_include/ov5640.h b/sensors/private_include/ov5640.h index 120ae72054..307f19df0c 100755 --- a/sensors/private_include/ov5640.h +++ b/sensors/private_include/ov5640.h @@ -13,7 +13,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int ov5640_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_ov5640_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -22,6 +22,6 @@ int ov5640_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int ov5640_init(sensor_t *sensor); +int esp32_camera_ov5640_init(sensor_t *sensor); #endif // __OV5640_H__ diff --git a/sensors/private_include/ov7670.h b/sensors/private_include/ov7670.h index b3a645a70d..cfd8b4792d 100644 --- a/sensors/private_include/ov7670.h +++ b/sensors/private_include/ov7670.h @@ -19,7 +19,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int ov7670_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_ov7670_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -28,6 +28,6 @@ int ov7670_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int ov7670_init(sensor_t *sensor); +int esp32_camera_ov7670_init(sensor_t *sensor); #endif // __OV7670_H__ diff --git a/sensors/private_include/ov7725.h b/sensors/private_include/ov7725.h index 291b266800..6ab41db8e5 100755 --- a/sensors/private_include/ov7725.h +++ b/sensors/private_include/ov7725.h @@ -19,7 +19,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int ov7725_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_ov7725_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -28,6 +28,6 @@ int ov7725_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int ov7725_init(sensor_t *sensor); +int esp32_camera_ov7725_init(sensor_t *sensor); #endif // __OV7725_H__ diff --git a/sensors/private_include/sc030iot.h b/sensors/private_include/sc030iot.h index 19298b76c2..824d2d6cf4 100644 --- a/sensors/private_include/sc030iot.h +++ b/sensors/private_include/sc030iot.h @@ -17,7 +17,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int sc030iot_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_sc030iot_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -26,6 +26,6 @@ int sc030iot_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int sc030iot_init(sensor_t *sensor); +int esp32_camera_sc030iot_init(sensor_t *sensor); #endif // __SC030IOT_H__ diff --git a/sensors/private_include/sc031gs.h b/sensors/private_include/sc031gs.h index 93652d58a1..4a8c300926 100644 --- a/sensors/private_include/sc031gs.h +++ b/sensors/private_include/sc031gs.h @@ -17,7 +17,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int sc031gs_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_sc031gs_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -26,6 +26,6 @@ int sc031gs_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int sc031gs_init(sensor_t *sensor); +int esp32_camera_sc031gs_init(sensor_t *sensor); #endif // __SC031GS_H__ diff --git a/sensors/private_include/sc101iot.h b/sensors/private_include/sc101iot.h index 85858498ec..c6c9eea609 100644 --- a/sensors/private_include/sc101iot.h +++ b/sensors/private_include/sc101iot.h @@ -17,7 +17,7 @@ * 0: Can't detect this sensor * Nonzero: This sensor has been detected */ -int sc101iot_detect(int slv_addr, sensor_id_t *id); +int esp32_camera_sc101iot_detect(int slv_addr, sensor_id_t *id); /** * @brief initialize sensor function pointers @@ -26,6 +26,6 @@ int sc101iot_detect(int slv_addr, sensor_id_t *id); * @return * Always 0 */ -int sc101iot_init(sensor_t *sensor); +int esp32_camera_sc101iot_init(sensor_t *sensor); #endif // __SC101IOT_H__ diff --git a/sensors/sc030iot.c b/sensors/sc030iot.c index 86f525f376..e7054c1aee 100644 --- a/sensors/sc030iot.c +++ b/sensors/sc030iot.c @@ -284,7 +284,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int sc030iot_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_sc030iot_detect(int slv_addr, sensor_id_t *id) { if (SC030IOT_SCCB_ADDR == slv_addr) { uint8_t MIDL = SCCB_Read(slv_addr, SC030_SENSOR_ID_LOW_REG); @@ -300,7 +300,7 @@ int sc030iot_detect(int slv_addr, sensor_id_t *id) return 0; } -int sc030iot_init(sensor_t *sensor) +int esp32_camera_sc030iot_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; diff --git a/sensors/sc031gs.c b/sensors/sc031gs.c index 627b70136d..307fa4232b 100644 --- a/sensors/sc031gs.c +++ b/sensors/sc031gs.c @@ -292,7 +292,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int sc031gs_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_sc031gs_detect(int slv_addr, sensor_id_t *id) { if (SC031GS_SCCB_ADDR == slv_addr) { uint8_t MIDL = SCCB_Read16(slv_addr, SC031GS_PID_HIGH_REG); @@ -308,7 +308,7 @@ int sc031gs_detect(int slv_addr, sensor_id_t *id) return 0; } -int sc031gs_init(sensor_t *sensor) +int esp32_camera_sc031gs_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; diff --git a/sensors/sc101iot.c b/sensors/sc101iot.c index 310a04768b..e27e4793ff 100644 --- a/sensors/sc101iot.c +++ b/sensors/sc101iot.c @@ -291,7 +291,7 @@ static int set_xclk(sensor_t *sensor, int timer, int xclk) return ret; } -int sc101iot_detect(int slv_addr, sensor_id_t *id) +int esp32_camera_sc101iot_detect(int slv_addr, sensor_id_t *id) { if (SC101IOT_SCCB_ADDR == slv_addr) { uint8_t MIDL = SCCB_Read(slv_addr, SC101_SENSOR_ID_LOW_REG); @@ -307,7 +307,7 @@ int sc101iot_detect(int slv_addr, sensor_id_t *id) return 0; } -int sc101iot_init(sensor_t *sensor) +int esp32_camera_sc101iot_init(sensor_t *sensor) { // Set function pointers sensor->reset = reset; From 9befde107551c5d9f4614fe971f5708ef645b1bf Mon Sep 17 00:00:00 2001 From: Justin Brady Date: Thu, 6 Nov 2025 00:28:58 -0800 Subject: [PATCH 87/90] add pin mappings for XIAO SENSE esp32s3 per (#811) https://github.com/limengdu/SeeedStudio-XIAO-ESP32S3-Sense-camera/ --- examples/camera_example/main/take_picture.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/examples/camera_example/main/take_picture.c b/examples/camera_example/main/take_picture.c index f9df7f7b1a..6ef6881652 100644 --- a/examples/camera_example/main/take_picture.c +++ b/examples/camera_example/main/take_picture.c @@ -9,6 +9,7 @@ // #define BOARD_ESP32CAM_AITHINKER // #define BOARD_ESP32S3_WROOM // #define BOARD_ESP32S3_GOOUUU +// #define BOARD_ESP32S3_XIAO /** * 2. Kconfig setup @@ -132,6 +133,24 @@ #define CAM_PIN_D6 17 #define CAM_PIN_D7 16 #endif +#ifdef BOARD_ESP32S3_XIAO +#define CAM_PIN_PWDN -1 +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_VSYNC 38 +#define CAM_PIN_HREF 47 +#define CAM_PIN_PCLK 13 +#define CAM_PIN_XCLK 10 +#define CAM_PIN_SIOD 40 +#define CAM_PIN_SIOC 39 +#define CAM_PIN_D0 15 +#define CAM_PIN_D1 17 +#define CAM_PIN_D2 18 +#define CAM_PIN_D3 16 +#define CAM_PIN_D4 14 +#define CAM_PIN_D5 12 +#define CAM_PIN_D6 11 +#define CAM_PIN_D7 48 +#endif static const char *TAG = "example:take_picture"; #if ESP_CAMERA_SUPPORTED From bcbb2ad691de0b987237f113f24b52616e91c682 Mon Sep 17 00:00:00 2001 From: Josh Cox Date: Thu, 6 Nov 2025 02:35:16 -0600 Subject: [PATCH 88/90] Adding esp32s3 XIAO pinout (#808) * Adding esp32s3 XIAO pinout, and placing the pinouts in a separate header file in the hopes that someone else can utilize the same pinout header file in their project Signed-off-by: Joshua Edward McLaughlin Cox * Correcting comment above the XIAO block Signed-off-by: Joshua Edward McLaughlin Cox * review changes Signed-off-by: Joshua Edward McLaughlin Cox --------- Signed-off-by: Joshua Edward McLaughlin Cox Co-authored-by: Me No Dev --- examples/camera_example/main/camera_pinout.h | 102 +++++++++++++++++++ examples/camera_example/main/take_picture.c | 102 +------------------ 2 files changed, 104 insertions(+), 100 deletions(-) create mode 100644 examples/camera_example/main/camera_pinout.h diff --git a/examples/camera_example/main/camera_pinout.h b/examples/camera_example/main/camera_pinout.h new file mode 100644 index 0000000000..97b5d2fd36 --- /dev/null +++ b/examples/camera_example/main/camera_pinout.h @@ -0,0 +1,102 @@ +// WROVER-KIT PIN Map +#ifdef BOARD_WROVER_KIT + +#define CAM_PIN_PWDN -1 //power down is not used +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_XCLK 21 +#define CAM_PIN_SIOD 26 +#define CAM_PIN_SIOC 27 + +#define CAM_PIN_D7 35 +#define CAM_PIN_D6 34 +#define CAM_PIN_D5 39 +#define CAM_PIN_D4 36 +#define CAM_PIN_D3 19 +#define CAM_PIN_D2 18 +#define CAM_PIN_D1 5 +#define CAM_PIN_D0 4 +#define CAM_PIN_VSYNC 25 +#define CAM_PIN_HREF 23 +#define CAM_PIN_PCLK 22 + +#endif + +// ESP32Cam (AiThinker) PIN Map +#ifdef BOARD_ESP32CAM_AITHINKER + +#define CAM_PIN_PWDN 32 +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_XCLK 0 +#define CAM_PIN_SIOD 26 +#define CAM_PIN_SIOC 27 + +#define CAM_PIN_D7 35 +#define CAM_PIN_D6 34 +#define CAM_PIN_D5 39 +#define CAM_PIN_D4 36 +#define CAM_PIN_D3 21 +#define CAM_PIN_D2 19 +#define CAM_PIN_D1 18 +#define CAM_PIN_D0 5 +#define CAM_PIN_VSYNC 25 +#define CAM_PIN_HREF 23 +#define CAM_PIN_PCLK 22 + +#endif +// ESP32S3 (WROOM) PIN Map +#ifdef BOARD_ESP32S3_WROOM +#define CAM_PIN_PWDN 38 +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_VSYNC 6 +#define CAM_PIN_HREF 7 +#define CAM_PIN_PCLK 13 +#define CAM_PIN_XCLK 15 +#define CAM_PIN_SIOD 4 +#define CAM_PIN_SIOC 5 +#define CAM_PIN_D0 11 +#define CAM_PIN_D1 9 +#define CAM_PIN_D2 8 +#define CAM_PIN_D3 10 +#define CAM_PIN_D4 12 +#define CAM_PIN_D5 18 +#define CAM_PIN_D6 17 +#define CAM_PIN_D7 16 +#endif +// ESP32S3 (GOOUU TECH) +#ifdef BOARD_ESP32S3_GOOUUU +#define CAM_PIN_PWDN -1 +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_VSYNC 6 +#define CAM_PIN_HREF 7 +#define CAM_PIN_PCLK 13 +#define CAM_PIN_XCLK 15 +#define CAM_PIN_SIOD 4 +#define CAM_PIN_SIOC 5 +#define CAM_PIN_D0 11 +#define CAM_PIN_D1 9 +#define CAM_PIN_D2 8 +#define CAM_PIN_D3 10 +#define CAM_PIN_D4 12 +#define CAM_PIN_D5 18 +#define CAM_PIN_D6 17 +#define CAM_PIN_D7 16 +#endif +// ESP32S3 (XIAO) +#ifdef BOARD_ESP32S3_XIAO +#define CAM_PIN_PWDN -1 +#define CAM_PIN_RESET -1 //software reset will be performed +#define CAM_PIN_VSYNC 38 +#define CAM_PIN_HREF 47 +#define CAM_PIN_PCLK 13 +#define CAM_PIN_XCLK 10 +#define CAM_PIN_SIOD 40 +#define CAM_PIN_SIOC 39 +#define CAM_PIN_D0 15 +#define CAM_PIN_D1 17 +#define CAM_PIN_D2 18 +#define CAM_PIN_D3 16 +#define CAM_PIN_D4 14 +#define CAM_PIN_D5 12 +#define CAM_PIN_D6 11 +#define CAM_PIN_D7 48 +#endif diff --git a/examples/camera_example/main/take_picture.c b/examples/camera_example/main/take_picture.c index 6ef6881652..e775ad7d80 100644 --- a/examples/camera_example/main/take_picture.c +++ b/examples/camera_example/main/take_picture.c @@ -8,6 +8,7 @@ // #define BOARD_WROVER_KIT // #define BOARD_ESP32CAM_AITHINKER // #define BOARD_ESP32S3_WROOM +// #define BOARD_ESP32S3_XIAO // #define BOARD_ESP32S3_GOOUUU // #define BOARD_ESP32S3_XIAO @@ -50,107 +51,8 @@ #define BOARD_WROVER_KIT 1 -// WROVER-KIT PIN Map -#ifdef BOARD_WROVER_KIT - -#define CAM_PIN_PWDN -1 //power down is not used -#define CAM_PIN_RESET -1 //software reset will be performed -#define CAM_PIN_XCLK 21 -#define CAM_PIN_SIOD 26 -#define CAM_PIN_SIOC 27 - -#define CAM_PIN_D7 35 -#define CAM_PIN_D6 34 -#define CAM_PIN_D5 39 -#define CAM_PIN_D4 36 -#define CAM_PIN_D3 19 -#define CAM_PIN_D2 18 -#define CAM_PIN_D1 5 -#define CAM_PIN_D0 4 -#define CAM_PIN_VSYNC 25 -#define CAM_PIN_HREF 23 -#define CAM_PIN_PCLK 22 - -#endif - -// ESP32Cam (AiThinker) PIN Map -#ifdef BOARD_ESP32CAM_AITHINKER - -#define CAM_PIN_PWDN 32 -#define CAM_PIN_RESET -1 //software reset will be performed -#define CAM_PIN_XCLK 0 -#define CAM_PIN_SIOD 26 -#define CAM_PIN_SIOC 27 - -#define CAM_PIN_D7 35 -#define CAM_PIN_D6 34 -#define CAM_PIN_D5 39 -#define CAM_PIN_D4 36 -#define CAM_PIN_D3 21 -#define CAM_PIN_D2 19 -#define CAM_PIN_D1 18 -#define CAM_PIN_D0 5 -#define CAM_PIN_VSYNC 25 -#define CAM_PIN_HREF 23 -#define CAM_PIN_PCLK 22 +#include "camera_pinout.h" -#endif -// ESP32S3 (WROOM) PIN Map -#ifdef BOARD_ESP32S3_WROOM -#define CAM_PIN_PWDN 38 -#define CAM_PIN_RESET -1 //software reset will be performed -#define CAM_PIN_VSYNC 6 -#define CAM_PIN_HREF 7 -#define CAM_PIN_PCLK 13 -#define CAM_PIN_XCLK 15 -#define CAM_PIN_SIOD 4 -#define CAM_PIN_SIOC 5 -#define CAM_PIN_D0 11 -#define CAM_PIN_D1 9 -#define CAM_PIN_D2 8 -#define CAM_PIN_D3 10 -#define CAM_PIN_D4 12 -#define CAM_PIN_D5 18 -#define CAM_PIN_D6 17 -#define CAM_PIN_D7 16 -#endif -// ESP32S3 (GOOUU TECH) -#ifdef BOARD_ESP32S3_GOOUUU -#define CAM_PIN_PWDN -1 -#define CAM_PIN_RESET -1 //software reset will be performed -#define CAM_PIN_VSYNC 6 -#define CAM_PIN_HREF 7 -#define CAM_PIN_PCLK 13 -#define CAM_PIN_XCLK 15 -#define CAM_PIN_SIOD 4 -#define CAM_PIN_SIOC 5 -#define CAM_PIN_D0 11 -#define CAM_PIN_D1 9 -#define CAM_PIN_D2 8 -#define CAM_PIN_D3 10 -#define CAM_PIN_D4 12 -#define CAM_PIN_D5 18 -#define CAM_PIN_D6 17 -#define CAM_PIN_D7 16 -#endif -#ifdef BOARD_ESP32S3_XIAO -#define CAM_PIN_PWDN -1 -#define CAM_PIN_RESET -1 //software reset will be performed -#define CAM_PIN_VSYNC 38 -#define CAM_PIN_HREF 47 -#define CAM_PIN_PCLK 13 -#define CAM_PIN_XCLK 10 -#define CAM_PIN_SIOD 40 -#define CAM_PIN_SIOC 39 -#define CAM_PIN_D0 15 -#define CAM_PIN_D1 17 -#define CAM_PIN_D2 18 -#define CAM_PIN_D3 16 -#define CAM_PIN_D4 14 -#define CAM_PIN_D5 12 -#define CAM_PIN_D6 11 -#define CAM_PIN_D7 48 -#endif static const char *TAG = "example:take_picture"; #if ESP_CAMERA_SUPPORTED From 12c147f9a2f2cc0b11917285eac3133ad38dc562 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 6 Nov 2025 16:36:53 +0800 Subject: [PATCH 89/90] Update framesize validation to check FRAMESIZE_5MP (#810) Change error logging from error to warning for invalid framesize and set framesize to FRAMESIZE_5MP if it exceeds the limit. --- sensors/ov5640.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sensors/ov5640.c b/sensors/ov5640.c index ffa5b5a97c..9186571057 100755 --- a/sensors/ov5640.c +++ b/sensors/ov5640.c @@ -384,9 +384,9 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) framesize_t old_framesize = sensor->status.framesize; sensor->status.framesize = framesize; - if(framesize > FRAMESIZE_QSXGA){ - ESP_LOGE(TAG, "Invalid framesize: %u", framesize); - return -1; + if (framesize > FRAMESIZE_5MP) { + ESP_LOGW(TAG, "Invalid framesize: %u", framesize); + framesize = FRAMESIZE_5MP; } uint16_t w = resolution[framesize].width; uint16_t h = resolution[framesize].height; From b817e9b81041980608d1a3d3e0b08d86e3666382 Mon Sep 17 00:00:00 2001 From: Christopher Nadler <147471517+cnadler86@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:43:03 +0100 Subject: [PATCH 90/90] Fix gdma: gdma_disconnect(309): no peripheral is connected to the channel (#804) Co-authored-by: Me No Dev --- target/esp32s3/ll_cam.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index a3b474dfad..ee1eacde41 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -191,11 +191,12 @@ esp_err_t ll_cam_deinit(cam_obj_t *cam) esp_intr_free(cam->dma_intr_handle); cam->dma_intr_handle = NULL; } - gdma_disconnect(cam->dma_channel_handle); - gdma_del_channel(cam->dma_channel_handle); - cam->dma_channel_handle = NULL; - // GDMA.channel[cam->dma_num].in.link.addr = 0x0; - + if (cam->dma_channel_handle) { + // gdma_disconnect(cam->dma_channel_handle); // not needed since code never calls gdma_connect() to connect channel to peripheral + gdma_del_channel(cam->dma_channel_handle); + cam->dma_channel_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;