Merge remote-tracking branch 'espressif/master' into HEAD

This commit is contained in:
Scott Shawcroft 2024-03-22 16:19:14 -07:00
commit 9abb23ed43
No known key found for this signature in database
GPG key ID: 0DFD512649C052DA
22 changed files with 370 additions and 195 deletions

View file

@ -11,30 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
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:
- 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: ["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"
@ -48,7 +25,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"

View file

@ -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}
)

View file

@ -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

View file

@ -22,7 +22,7 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi
| OV7725 | 640 x 480 | color | Raw RGB<br/>GRB 422<br/>RGB565/555/444<br/>YCbCr 422 | 1/4" |
| NT99141 | 1280 x 720 | color | YCbCr 422<br/>RGB565/555/444<br/>Raw<br/>CCIR656<br/>JPEG compression | 1/4" |
| GC032A | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/10" |
| GC0308 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/6.5" |
| GC0308 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565<br/>Grayscale | 1/6.5" |
| GC2145 | 1600 x 1200 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/5" |
| BF3005 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/4" |
| BF20A6 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>Only Y | 1/10" |
@ -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,42 +80,16 @@ 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)
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
```
Then in `platformio.ini` file
```
build_flags =
-I../lib/esp32-camera
```
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'?! </rant>
## 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 component comes with a basic example illustrating how to get frames from the camera. You can try out the example using the following command:
```
idf.py create-project-from-example "espressif/esp32-camera:camera_example"
```
This command will download the example into `camera_example` directory. It comes already pre-configured with the correct settings in menuconfig.
### Initialization
```c

View file

@ -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

View file

@ -14,6 +14,7 @@
#include <stdio.h>
#include <string.h>
#include <stdalign.h>
#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;
@ -438,7 +439,7 @@ esp_err_t cam_deinit(void)
}
ll_cam_deinit(cam_obj);
if (cam_obj->dma) {
free(cam_obj->dma);
}
@ -476,6 +477,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
@ -487,7 +498,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
@ -496,6 +511,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;
}

View file

@ -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

View file

@ -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)
project(camera_example)

View file

@ -0,0 +1,3 @@
idf_component_register(SRCS take_picture.c
PRIV_INCLUDE_DIRS .
PRIV_REQUIRES nvs_flash)

View file

@ -0,0 +1,5 @@
dependencies:
espressif/esp32-camera:
version: "*"
override_path: "../../../"

View file

@ -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,
};

View file

@ -1,3 +0,0 @@
set(COMPONENT_SRCS take_picture.c)
set(COMPONENT_ADD_INCLUDEDIRS .)
register_component()

View file

@ -1,5 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -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

View file

@ -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;
@ -253,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));

View file

@ -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},
};

View file

@ -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;

View file

@ -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;

View file

@ -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)
@ -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

View file

@ -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"
@ -39,7 +41,55 @@
static const char *TAG = "s3 ll_cam";
static void IRAM_ATTR ll_cam_vsync_isr(void *arg)
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 CAMERA_ISR_IRAM_ATTR ll_cam_vsync_isr(void *arg)
{
//DBG_PIN_SET(1);
cam_obj_t *cam = (cam_obj_t *)arg;
@ -62,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;
@ -93,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;
@ -143,48 +174,73 @@ bool ll_cam_start(cam_obj_t *cam, int frame_pos)
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_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)
{
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;
}
//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;
}
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);
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;
// }
// }
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;
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);
}
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
// 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;
}
@ -239,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;
@ -341,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) {
@ -350,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) {
@ -493,7 +553,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

View file

@ -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;
@ -142,6 +156,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);