mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-08 20:46:52 +03:00
Rolling 20220924
This commit is contained in:
@@ -69,6 +69,15 @@ In other cases you can contact the developer via email: <img src="https://raw.gi
|
||||
|
||||
------
|
||||
|
||||
##### Rolling (2022-09-24)
|
||||
|
||||
- Updated menue
|
||||
- Update tflite, esp32-cam-master, esp-nn (as of today 20220924)
|
||||
|
||||
##### Rolling (2022-09-21)
|
||||
|
||||
- Spelling corrections (**[cristianmitran](https://github.com/cristianmitran)**)
|
||||
|
||||
##### Rolling (2022-09-21)
|
||||
|
||||
- New update mechanism:
|
||||
|
||||
BIN
code/components/esp-nn_20220924.zip
Normal file
BIN
code/components/esp-nn_20220924.zip
Normal file
Binary file not shown.
@@ -8,26 +8,56 @@ on:
|
||||
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@latest
|
||||
uses: espressif/esp-idf-ci-action@main
|
||||
with:
|
||||
target: ${{ matrix.idf_target }}
|
||||
path: 'examples'
|
||||
|
||||
build-release-v4_0:
|
||||
build-release-v5_0:
|
||||
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"]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: esp-idf build
|
||||
uses: espressif/esp-idf-ci-action@release-v4.0
|
||||
uses: espressif/esp-idf-ci-action@main
|
||||
with:
|
||||
esp_idf_version: ${{ matrix.idf_ver }}
|
||||
target: ${{ matrix.idf_target }}
|
||||
path: 'examples'
|
||||
|
||||
build-release-v4_4:
|
||||
name: Build for ${{ matrix.idf_target }} on ${{ matrix.idf_ver }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["v4.4"]
|
||||
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:
|
||||
esp_idf_version: ${{ matrix.idf_ver }}
|
||||
target: ${{ matrix.idf_target }}
|
||||
path: 'examples'
|
||||
|
||||
build-release-v4_1:
|
||||
@@ -65,15 +95,3 @@ jobs:
|
||||
uses: espressif/esp-idf-ci-action@release-v4.3
|
||||
with:
|
||||
path: 'examples'
|
||||
|
||||
build-release-v3_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-v3.3
|
||||
with:
|
||||
path: 'examples'
|
||||
|
||||
@@ -10,12 +10,10 @@ jobs:
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Upload component to the component registry
|
||||
uses: espressif/github-actions/upload_components@master
|
||||
with:
|
||||
name: "esp32-camera"
|
||||
version: "git"
|
||||
namespace: "espressif"
|
||||
service_url: ${{ secrets.IDF_COMPONENT_API_URL }}
|
||||
version: ${{ github.ref_name }}
|
||||
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# get IDF version for comparison
|
||||
set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}")
|
||||
|
||||
# set conversion sources
|
||||
set(COMPONENT_SRCS
|
||||
conversions/yuv.c
|
||||
conversions/to_jpg.cpp
|
||||
conversions/to_bmp.c
|
||||
conversions/jpge.cpp
|
||||
conversions/esp_jpg_decode.c
|
||||
)
|
||||
|
||||
set(COMPONENT_PRIV_INCLUDEDIRS
|
||||
conversions/private_include
|
||||
)
|
||||
|
||||
set(COMPONENT_ADD_INCLUDEDIRS
|
||||
driver/include
|
||||
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")
|
||||
set(COMPONENT_SRCS
|
||||
list(APPEND COMPONENT_SRCS
|
||||
driver/esp_camera.c
|
||||
driver/cam_hal.c
|
||||
driver/sccb.c
|
||||
@@ -14,22 +38,14 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
|
||||
sensors/gc2145.c
|
||||
sensors/gc032a.c
|
||||
sensors/bf3005.c
|
||||
conversions/yuv.c
|
||||
conversions/to_jpg.cpp
|
||||
conversions/to_bmp.c
|
||||
conversions/jpge.cpp
|
||||
conversions/esp_jpg_decode.c
|
||||
sensors/bf20a6.c
|
||||
sensors/sc101iot.c
|
||||
sensors/sc030iot.c
|
||||
)
|
||||
|
||||
set(COMPONENT_ADD_INCLUDEDIRS
|
||||
driver/include
|
||||
conversions/include
|
||||
)
|
||||
|
||||
set(COMPONENT_PRIV_INCLUDEDIRS
|
||||
list(APPEND COMPONENT_PRIV_INCLUDEDIRS
|
||||
driver/private_include
|
||||
sensors/private_include
|
||||
conversions/private_include
|
||||
target/private_include
|
||||
)
|
||||
|
||||
@@ -58,8 +74,13 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
|
||||
)
|
||||
endif()
|
||||
|
||||
set(COMPONENT_REQUIRES driver)
|
||||
set(COMPONENT_PRIV_REQUIRES freertos nvs_flash)
|
||||
|
||||
register_component()
|
||||
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)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
register_component()
|
||||
|
||||
@@ -70,6 +70,45 @@ menu "Camera configuration"
|
||||
Enable this option if you want to use the BF3005.
|
||||
Disable this option to save memory.
|
||||
|
||||
config BF20A6_SUPPORT
|
||||
bool "Support BF20A6(BYD20A6) VGA"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the BF20A6.
|
||||
Disable this option to save memory.
|
||||
|
||||
config SC101IOT_SUPPORT
|
||||
bool "Support SC101IOT HD"
|
||||
default n
|
||||
help
|
||||
Enable this option if you want to use the SC101IOT.
|
||||
Disable this option to save memory.
|
||||
|
||||
choice SC101_REGS_SELECT
|
||||
prompt "SC101iot default regs"
|
||||
default SC101IOT_720P_15FPS_ENABLED
|
||||
depends on SC101IOT_SUPPORT
|
||||
help
|
||||
Currently SC010iot has several register sets available.
|
||||
Select the one that matches your needs.
|
||||
|
||||
config SC101IOT_720P_15FPS_ENABLED
|
||||
bool "xclk20M_720p_15fps"
|
||||
help
|
||||
Select this option means that when xclk is 20M, the frame rate is 15fps at 720p resolution.
|
||||
config SC101IOT_VGA_25FPS_ENABLED
|
||||
bool "xclk20M_VGA_25fps"
|
||||
help
|
||||
Select this option means that when xclk is 20M, the frame rate is 25fps at VGA resolution.
|
||||
endchoice
|
||||
|
||||
config SC030IOT_SUPPORT
|
||||
bool "Support SC030IOT VGA"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the SC030IOT.
|
||||
Disable this option to save memory.
|
||||
|
||||
choice SCCB_HARDWARE_I2C_PORT
|
||||
bool "I2C peripheral to use for SCCB"
|
||||
default SCCB_HARDWARE_I2C_PORT1
|
||||
@@ -103,6 +142,12 @@ menu "Camera configuration"
|
||||
bool "Subsample Mode"
|
||||
endchoice
|
||||
|
||||
config CAMERA_TASK_STACK_SIZE
|
||||
int "CAM task stack size"
|
||||
default 2048
|
||||
help
|
||||
Camera task stack size
|
||||
|
||||
choice CAMERA_TASK_PINNED_TO_CORE
|
||||
bool "Camera task pinned to core"
|
||||
default CAMERA_CORE0
|
||||
@@ -124,6 +169,37 @@ menu "Camera configuration"
|
||||
default 32768
|
||||
help
|
||||
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
|
||||
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_CONVERTER_ENABLED
|
||||
bool "Enable camera RGB/YUV converter"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
default n
|
||||
help
|
||||
Enable this option if you want to use RGB565/YUV422/YUV420/YUV411 format conversion.
|
||||
|
||||
choice CAMERA_CONV_PROTOCOL
|
||||
bool "Camera converter protocol"
|
||||
depends on CAMERA_CONVERTER_ENABLED
|
||||
default LCD_CAM_CONV_BT601_ENABLED
|
||||
help
|
||||
Supports format conversion under both BT601 and BT709 standards.
|
||||
|
||||
config LCD_CAM_CONV_BT601_ENABLED
|
||||
bool "BT601"
|
||||
config LCD_CAM_CONV_BT709_ENABLED
|
||||
bool "BT709"
|
||||
endchoice
|
||||
|
||||
config LCD_CAM_CONV_FULL_RANGE_ENABLED
|
||||
bool "Camera converter full range mode"
|
||||
depends on CAMERA_CONVERTER_ENABLED
|
||||
default y
|
||||
help
|
||||
Supports format conversion under both full color range mode and limited color range mode.
|
||||
If full color range mode is selected, the color range of RGB or YUV is 0~255.
|
||||
If limited color range mode is selected, the color range of RGB is 16~240, and the color range of YUV is Y[16~240], UV[16~235].
|
||||
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.
|
||||
endmenu
|
||||
|
||||
@@ -25,6 +25,9 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi
|
||||
| GC0308 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 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 | 1/10" |
|
||||
| SC101IOT| 1280 x 720 | color | YUV/YCbCr422<br/>Raw RGB | 1/4.2" |
|
||||
| SC030IOT| 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer | 1/6.5" |
|
||||
|
||||
## Important to Remember
|
||||
|
||||
@@ -128,8 +131,8 @@ static camera_config_t camera_config = {
|
||||
.pin_pwdn = CAM_PIN_PWDN,
|
||||
.pin_reset = CAM_PIN_RESET,
|
||||
.pin_xclk = CAM_PIN_XCLK,
|
||||
.pin_sscb_sda = CAM_PIN_SIOD,
|
||||
.pin_sscb_scl = CAM_PIN_SIOC,
|
||||
.pin_sccb_sda = CAM_PIN_SIOD,
|
||||
.pin_sccb_scl = CAM_PIN_SIOC,
|
||||
|
||||
.pin_d7 = CAM_PIN_D7,
|
||||
.pin_d6 = CAM_PIN_D6,
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
#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"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
@@ -57,7 +61,7 @@ static const char * jd_errors[] = {
|
||||
"Not supported JPEG standard"
|
||||
};
|
||||
|
||||
static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
|
||||
static unsigned int _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
|
||||
{
|
||||
uint16_t x = rect->left;
|
||||
uint16_t y = rect->top;
|
||||
@@ -73,7 +77,7 @@ static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t _jpg_read(JDEC *decoder, uint8_t *buf, uint32_t len)
|
||||
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)) {
|
||||
|
||||
@@ -29,7 +29,12 @@ namespace jpge {
|
||||
if(b){
|
||||
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))
|
||||
return heap_caps_malloc(nSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
static inline void jpge_free(void *p) { free(p); }
|
||||
|
||||
|
||||
@@ -21,19 +21,6 @@
|
||||
#include "esp_jpg_decode.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/spiram.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "esp_spiram.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
@@ -72,7 +59,12 @@ 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))
|
||||
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
#endif
|
||||
// try allocating in internal memory
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
//output buffer and image width
|
||||
@@ -168,7 +160,7 @@ static bool _rgb565_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16
|
||||
}
|
||||
|
||||
//input buffer
|
||||
static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
|
||||
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) {
|
||||
@@ -310,7 +302,13 @@ bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixf
|
||||
*out_len = 0;
|
||||
|
||||
int pix_count = width*height;
|
||||
size_t out_size = (pix_count * 3) + BMP_HEADER_LEN;
|
||||
|
||||
// With BMP, 8-bit greyscale requires a palette.
|
||||
// For a 640x480 image though, that's a savings
|
||||
// over going RGB-24.
|
||||
int bpp = (format == PIXFORMAT_GRAYSCALE) ? 1 : 3;
|
||||
int palette_size = (format == PIXFORMAT_GRAYSCALE) ? 4 * 256 : 0;
|
||||
size_t out_size = (pix_count * bpp) + BMP_HEADER_LEN + palette_size;
|
||||
uint8_t * out_buf = (uint8_t *)_malloc(out_size);
|
||||
if(!out_buf) {
|
||||
ESP_LOGE(TAG, "_malloc failed! %u", out_size);
|
||||
@@ -322,45 +320,51 @@ bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixf
|
||||
bmp_header_t * bitmap = (bmp_header_t*)&out_buf[2];
|
||||
bitmap->reserved = 0;
|
||||
bitmap->filesize = out_size;
|
||||
bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN;
|
||||
bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN + palette_size;
|
||||
bitmap->dibheadersize = 40;
|
||||
bitmap->width = width;
|
||||
bitmap->height = -height;//set negative for top to bottom
|
||||
bitmap->planes = 1;
|
||||
bitmap->bitsperpixel = 24;
|
||||
bitmap->bitsperpixel = bpp * 8;
|
||||
bitmap->compression = 0;
|
||||
bitmap->imagesize = pix_count * 3;
|
||||
bitmap->imagesize = pix_count * bpp;
|
||||
bitmap->ypixelpermeter = 0x0B13 ; //2835 , 72 DPI
|
||||
bitmap->xpixelpermeter = 0x0B13 ; //2835 , 72 DPI
|
||||
bitmap->numcolorspallette = 0;
|
||||
bitmap->mostimpcolor = 0;
|
||||
|
||||
uint8_t * rgb_buf = out_buf + BMP_HEADER_LEN;
|
||||
uint8_t * palette_buf = out_buf + BMP_HEADER_LEN;
|
||||
uint8_t * pix_buf = palette_buf + palette_size;
|
||||
uint8_t * src_buf = src;
|
||||
|
||||
if (palette_size > 0) {
|
||||
// Grayscale palette
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
*palette_buf = i;
|
||||
palette_buf++;
|
||||
}
|
||||
// Reserved / alpha channel.
|
||||
*palette_buf = 0;
|
||||
palette_buf++;
|
||||
}
|
||||
}
|
||||
|
||||
//convert data to RGB888
|
||||
if(format == PIXFORMAT_RGB888) {
|
||||
memcpy(rgb_buf, src_buf, pix_count*3);
|
||||
memcpy(pix_buf, src_buf, pix_count*3);
|
||||
} else if(format == PIXFORMAT_RGB565) {
|
||||
int i;
|
||||
uint8_t hb, lb;
|
||||
for(i=0; i<pix_count; i++) {
|
||||
hb = *src_buf++;
|
||||
lb = *src_buf++;
|
||||
*rgb_buf++ = (lb & 0x1F) << 3;
|
||||
*rgb_buf++ = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
|
||||
*rgb_buf++ = hb & 0xF8;
|
||||
*pix_buf++ = (lb & 0x1F) << 3;
|
||||
*pix_buf++ = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
|
||||
*pix_buf++ = hb & 0xF8;
|
||||
}
|
||||
} else if(format == PIXFORMAT_GRAYSCALE) {
|
||||
int i;
|
||||
uint8_t b;
|
||||
for(i=0; i<pix_count; i++) {
|
||||
b = *src_buf++;
|
||||
*rgb_buf++ = b;
|
||||
*rgb_buf++ = b;
|
||||
*rgb_buf++ = b;
|
||||
}
|
||||
memcpy(pix_buf, src_buf, pix_count);
|
||||
} else if(format == PIXFORMAT_YUV422) {
|
||||
int i, maxi = pix_count / 2;
|
||||
uint8_t y0, y1, u, v;
|
||||
@@ -372,14 +376,14 @@ bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixf
|
||||
v = *src_buf++;
|
||||
|
||||
yuv2rgb(y0, u, v, &r, &g, &b);
|
||||
*rgb_buf++ = b;
|
||||
*rgb_buf++ = g;
|
||||
*rgb_buf++ = r;
|
||||
*pix_buf++ = b;
|
||||
*pix_buf++ = g;
|
||||
*pix_buf++ = r;
|
||||
|
||||
yuv2rgb(y1, u, v, &r, &g, &b);
|
||||
*rgb_buf++ = b;
|
||||
*rgb_buf++ = g;
|
||||
*rgb_buf++ = r;
|
||||
*pix_buf++ = b;
|
||||
*pix_buf++ = g;
|
||||
*pix_buf++ = r;
|
||||
}
|
||||
}
|
||||
*out = out_buf;
|
||||
|
||||
@@ -21,21 +21,6 @@
|
||||
#include "jpge.h"
|
||||
#include "yuv.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/spiram.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "esp_spiram.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define TAG ""
|
||||
@@ -50,7 +35,12 @@ static void *_malloc(size_t size)
|
||||
if(res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// check if SPIRAM is enabled and is allocatable
|
||||
#if (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;
|
||||
}
|
||||
|
||||
static IRAM_ATTR void convert_line_format(uint8_t * src, pixformat_t format, uint8_t * dst, size_t width, size_t in_channels, size_t line)
|
||||
|
||||
@@ -18,8 +18,27 @@
|
||||
#include "ll_cam.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
static const char *TAG = "cam_hal";
|
||||
#if (ESP_IDF_VERSION_MAJOR == 3) && (ESP_IDF_VERSION_MINOR == 3)
|
||||
#include "rom/ets_sys.h"
|
||||
#else
|
||||
#include "esp_timer.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include "esp32/rom/ets_sys.h" // will be removed in idf v5.0
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/ets_sys.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/ets_sys.h"
|
||||
#endif
|
||||
#endif // ESP_IDF_VERSION_MAJOR
|
||||
#define ESP_CAMERA_ETS_PRINTF ets_printf
|
||||
|
||||
#if CONFIG_CAM_TASK_STACK_SIZE
|
||||
#define CAM_TASK_STACK CONFIG_CAM_TASK_STACK_SIZE
|
||||
#else
|
||||
#define CAM_TASK_STACK (2*1024)
|
||||
#endif
|
||||
|
||||
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
|
||||
@@ -32,7 +51,7 @@ static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length)
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
sig = *((uint32_t *)(&inbuf[i])) & 0xFFFFFF;
|
||||
if (sig == JPEG_SOI_MARKER) {
|
||||
ESP_LOGW(TAG, "SOI: %d", i);
|
||||
ESP_LOGW(TAG, "SOI: %d", (int) i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -93,7 +112,7 @@ 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_EARLY_LOGE(TAG, "EV-%s-OVF", cam_event==CAM_IN_SUC_EOF_EVENT ? "EOF" : "VSYNC");
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +198,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, cam_obj->fb_size);
|
||||
ESP_LOGE(TAG, "FB-SIZE: %u != %u", frame_buffer_event->len, (unsigned) cam_obj->fb_size);
|
||||
}
|
||||
}
|
||||
//send frame
|
||||
@@ -246,7 +265,8 @@ static esp_err_t cam_dma_config(const camera_config_t *config)
|
||||
cam_obj->frame_copy_cnt = cam_obj->recv_size / cam_obj->dma_half_buffer_size; // Number of interrupted copies, ping-pong copy
|
||||
|
||||
ESP_LOGI(TAG, "buffer_size: %d, half_buffer_size: %d, node_buffer_size: %d, node_cnt: %d, total_cnt: %d",
|
||||
cam_obj->dma_buffer_size, cam_obj->dma_half_buffer_size, cam_obj->dma_node_buffer_size, cam_obj->dma_node_cnt, cam_obj->frame_copy_cnt);
|
||||
(int) cam_obj->dma_buffer_size, (int) cam_obj->dma_half_buffer_size, (int) cam_obj->dma_node_buffer_size,
|
||||
(int) cam_obj->dma_node_cnt, (int) cam_obj->frame_copy_cnt);
|
||||
|
||||
cam_obj->dma_buffer = NULL;
|
||||
cam_obj->dma = NULL;
|
||||
@@ -276,13 +296,19 @@ static esp_err_t cam_dma_config(const camera_config_t *config)
|
||||
cam_obj->frames[x].fb_offset = 0;
|
||||
cam_obj->frames[x].en = 0;
|
||||
ESP_LOGI(TAG, "Allocating %d Byte frame buffer in %s", alloc_size, _caps & MALLOC_CAP_SPIRAM ? "PSRAM" : "OnBoard RAM");
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
// In IDF v4.2 and earlier, memory returned by heap_caps_aligned_alloc must be freed using heap_caps_aligned_free.
|
||||
// And heap_caps_aligned_free is deprecated on v4.3.
|
||||
cam_obj->frames[x].fb.buf = (uint8_t *)heap_caps_aligned_alloc(16, alloc_size, _caps);
|
||||
#else
|
||||
cam_obj->frames[x].fb.buf = (uint8_t *)heap_caps_malloc(alloc_size, _caps);
|
||||
#endif
|
||||
CAM_CHECK(cam_obj->frames[x].fb.buf != NULL, "frame buffer malloc failed", ESP_FAIL);
|
||||
if (cam_obj->psram_mode) {
|
||||
//align PSRAM buffer. TODO: save the offset so proper address can be freed later
|
||||
cam_obj->frames[x].fb_offset = dma_align - ((uint32_t)cam_obj->frames[x].fb.buf & (dma_align - 1));
|
||||
cam_obj->frames[x].fb.buf += cam_obj->frames[x].fb_offset;
|
||||
ESP_LOGI(TAG, "Frame[%d]: Offset: %u, Addr: 0x%08X", x, cam_obj->frames[x].fb_offset, (uint32_t)cam_obj->frames[x].fb.buf);
|
||||
ESP_LOGI(TAG, "Frame[%d]: Offset: %u, Addr: 0x%08X", x, cam_obj->frames[x].fb_offset, (unsigned) cam_obj->frames[x].fb.buf);
|
||||
cam_obj->frames[x].dma = allocate_dma_descriptors(cam_obj->dma_node_cnt, cam_obj->dma_node_buffer_size, cam_obj->frames[x].fb.buf);
|
||||
CAM_CHECK(cam_obj->frames[x].dma != NULL, "frame dma malloc failed", ESP_FAIL);
|
||||
}
|
||||
@@ -293,7 +319,7 @@ static esp_err_t cam_dma_config(const camera_config_t *config)
|
||||
cam_obj->dma_buffer = (uint8_t *)heap_caps_malloc(cam_obj->dma_buffer_size * sizeof(uint8_t), MALLOC_CAP_DMA);
|
||||
if(NULL == cam_obj->dma_buffer) {
|
||||
ESP_LOGE(TAG,"%s(%d): DMA buffer %d Byte malloc failed, the current largest free block:%d Byte", __FUNCTION__, __LINE__,
|
||||
cam_obj->dma_buffer_size, heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
|
||||
(int) cam_obj->dma_buffer_size, (int) heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@@ -378,11 +404,11 @@ esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint
|
||||
|
||||
|
||||
#if CONFIG_CAMERA_CORE0
|
||||
xTaskCreatePinnedToCore(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 0);
|
||||
xTaskCreatePinnedToCore(cam_task, "cam_task", CAM_TASK_STACK, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 0);
|
||||
#elif CONFIG_CAMERA_CORE1
|
||||
xTaskCreatePinnedToCore(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 1);
|
||||
xTaskCreatePinnedToCore(cam_task, "cam_task", CAM_TASK_STACK, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 1);
|
||||
#else
|
||||
xTaskCreate(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle);
|
||||
xTaskCreate(cam_task, "cam_task", CAM_TASK_STACK, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle);
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "cam config ok");
|
||||
|
||||
@@ -57,6 +57,15 @@
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
#include "bf3005.h"
|
||||
#endif
|
||||
#if CONFIG_BF20A6_SUPPORT
|
||||
#include "bf20a6.h"
|
||||
#endif
|
||||
#if CONFIG_SC101IOT_SUPPORT
|
||||
#include "sc101iot.h"
|
||||
#endif
|
||||
#if CONFIG_SC030IOT_SUPPORT
|
||||
#include "sc030iot.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
@@ -119,10 +128,20 @@ static const sensor_func_t g_sensors[] = {
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
{bf3005_detect, bf3005_init},
|
||||
#endif
|
||||
#if CONFIG_BF20A6_SUPPORT
|
||||
{bf20a6_detect, bf20a6_init},
|
||||
#endif
|
||||
#if CONFIG_SC101IOT_SUPPORT
|
||||
{sc101iot_detect, sc101iot_init},
|
||||
#endif
|
||||
#if CONFIG_SC030IOT_SUPPORT
|
||||
{sc030iot_detect, sc030iot_init},
|
||||
#endif
|
||||
};
|
||||
|
||||
static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
*out_camera_model = CAMERA_NONE;
|
||||
if (s_state != NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
@@ -138,9 +157,17 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
|
||||
CAMERA_ENABLE_OUT_CLOCK(config);
|
||||
}
|
||||
|
||||
if (config->pin_sscb_sda != -1) {
|
||||
ESP_LOGD(TAG, "Initializing SSCB");
|
||||
SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl);
|
||||
if (config->pin_sccb_sda != -1) {
|
||||
ESP_LOGD(TAG, "Initializing SCCB");
|
||||
ret = SCCB_Init(config->pin_sccb_sda, config->pin_sccb_scl);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Using existing I2C port");
|
||||
ret = SCCB_Use_Port(config->sccb_i2c_port);
|
||||
}
|
||||
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "sccb init err");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (config->pin_pwdn >= 0) {
|
||||
@@ -170,15 +197,14 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
|
||||
ESP_LOGD(TAG, "Searching for camera address");
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
uint8_t slv_addr = SCCB_Probe();
|
||||
|
||||
if (slv_addr == 0) {
|
||||
CAMERA_DISABLE_OUT_CLOCK();
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
ret = ESP_ERR_NOT_FOUND;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr);
|
||||
@@ -203,9 +229,9 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
|
||||
}
|
||||
|
||||
if (CAMERA_NONE == *out_camera_model) { //If no supported sensors are detected
|
||||
CAMERA_DISABLE_OUT_CLOCK();
|
||||
ESP_LOGE(TAG, "Detected camera not supported.");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
ret = ESP_ERR_NOT_SUPPORTED;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x",
|
||||
@@ -213,11 +239,30 @@ 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);
|
||||
s_state->sensor.reset(&s_state->sensor);
|
||||
|
||||
return ESP_OK;
|
||||
return s_state->sensor.reset(&s_state->sensor);
|
||||
err :
|
||||
CAMERA_DISABLE_OUT_CLOCK();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
static pixformat_t get_output_data_format(camera_conv_mode_t conv_mode)
|
||||
{
|
||||
pixformat_t format = PIXFORMAT_RGB565;
|
||||
switch (conv_mode) {
|
||||
case YUV422_TO_YUV420:
|
||||
format = PIXFORMAT_YUV420;
|
||||
break;
|
||||
case YUV422_TO_RGB565: // default format is RGB565
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ESP_LOGD(TAG, "Convert to %d format enabled", format);
|
||||
return format;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_camera_init(const camera_config_t *config)
|
||||
{
|
||||
esp_err_t err;
|
||||
@@ -256,6 +301,7 @@ esp_err_t esp_camera_init(const camera_config_t *config)
|
||||
|
||||
s_state->sensor.status.framesize = frame_size;
|
||||
s_state->sensor.pixformat = pix_format;
|
||||
|
||||
ESP_LOGD(TAG, "Setting frame size to %dx%d", resolution[frame_size].width, resolution[frame_size].height);
|
||||
if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
|
||||
ESP_LOGE(TAG, "Failed to set frame size");
|
||||
@@ -263,6 +309,11 @@ esp_err_t esp_camera_init(const camera_config_t *config)
|
||||
goto fail;
|
||||
}
|
||||
s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
if(config->conv_mode) {
|
||||
s_state->sensor.pixformat = get_output_data_format(config->conv_mode); // If conversion enabled, change the out data format by conversion mode
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s_state->sensor.id.PID == OV2640_PID) {
|
||||
s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
.pin_pwdn = PIN_PWDN,
|
||||
.pin_reset = PIN_RESET,
|
||||
.pin_xclk = PIN_XCLK,
|
||||
.pin_sscb_sda = PIN_SIOD,
|
||||
.pin_sscb_scl = PIN_SIOC,
|
||||
.pin_sccb_sda = PIN_SIOD,
|
||||
.pin_sccb_scl = PIN_SIOC,
|
||||
.pin_d7 = PIN_D7,
|
||||
.pin_d6 = PIN_D6,
|
||||
.pin_d5 = PIN_D5,
|
||||
@@ -70,6 +70,7 @@
|
||||
#include "driver/ledc.h"
|
||||
#include "sensor.h"
|
||||
#include "sys/time.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -91,6 +92,19 @@ typedef enum {
|
||||
CAMERA_FB_IN_DRAM /*!< Frame buffer is placed in internal DRAM */
|
||||
} camera_fb_location_t;
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
/**
|
||||
* @brief Camera RGB\YUV conversion mode
|
||||
*/
|
||||
typedef enum {
|
||||
CONV_DISABLE,
|
||||
RGB565_TO_YUV422,
|
||||
|
||||
YUV422_TO_RGB565,
|
||||
YUV422_TO_YUV420
|
||||
} camera_conv_mode_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for camera initialization
|
||||
*/
|
||||
@@ -98,8 +112,14 @@ typedef struct {
|
||||
int pin_pwdn; /*!< GPIO pin for camera power down line */
|
||||
int pin_reset; /*!< GPIO pin for camera reset line */
|
||||
int pin_xclk; /*!< GPIO pin for camera XCLK line */
|
||||
int pin_sscb_sda; /*!< GPIO pin for camera SDA line */
|
||||
int pin_sscb_scl; /*!< GPIO pin for camera SCL line */
|
||||
union {
|
||||
int pin_sccb_sda; /*!< GPIO pin for camera SDA line */
|
||||
int pin_sscb_sda __attribute__((deprecated("please use pin_sccb_sda instead"))); /*!< GPIO pin for camera SDA line (legacy name) */
|
||||
};
|
||||
union {
|
||||
int pin_sccb_scl; /*!< GPIO pin for camera SCL line */
|
||||
int pin_sscb_scl __attribute__((deprecated("please use pin_sccb_scl instead"))); /*!< GPIO pin for camera SCL line (legacy name) */
|
||||
};
|
||||
int pin_d7; /*!< GPIO pin for camera D7 line */
|
||||
int pin_d6; /*!< GPIO pin for camera D6 line */
|
||||
int pin_d5; /*!< GPIO pin for camera D5 line */
|
||||
@@ -124,6 +144,11 @@ typedef struct {
|
||||
size_t fb_count; /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) */
|
||||
camera_fb_location_t fb_location; /*!< The location where the frame buffer will be allocated */
|
||||
camera_grab_mode_t grab_mode; /*!< When buffers should be filled */
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
camera_conv_mode_t conv_mode; /*!< RGB<->YUV Conversion mode */
|
||||
#endif
|
||||
|
||||
int sccb_i2c_port; /*!< If pin_sccb_sda is -1, use the already configured I2C bus by number */
|
||||
} camera_config_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,9 @@ typedef enum {
|
||||
GC032A_PID = 0x232a,
|
||||
GC0308_PID = 0x9b,
|
||||
BF3005_PID = 0x30,
|
||||
BF20A6_PID = 0x20a6,
|
||||
SC101IOT_PID = 0xda4a,
|
||||
SC030IOT_PID = 0x9a46,
|
||||
} camera_pid_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -40,6 +43,9 @@ typedef enum {
|
||||
CAMERA_GC032A,
|
||||
CAMERA_GC0308,
|
||||
CAMERA_BF3005,
|
||||
CAMERA_BF20A6,
|
||||
CAMERA_SC101IOT,
|
||||
CAMERA_SC030IOT,
|
||||
CAMERA_MODEL_MAX,
|
||||
CAMERA_NONE,
|
||||
} camera_model_t;
|
||||
@@ -55,11 +61,15 @@ typedef enum {
|
||||
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
BF3005_SCCB_ADDR = 0x6E,
|
||||
BF20A6_SCCB_ADDR = 0x6E,
|
||||
SC101IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
|
||||
SC030IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
|
||||
} camera_sccb_addr_t;
|
||||
|
||||
typedef enum {
|
||||
PIXFORMAT_RGB565, // 2BPP/RGB565
|
||||
PIXFORMAT_YUV422, // 2BPP/YUV422
|
||||
PIXFORMAT_YUV420, // 1.5BPP/YUV420
|
||||
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
|
||||
PIXFORMAT_JPEG, // JPEG/COMPRESSED
|
||||
PIXFORMAT_RGB888, // 3BPP/RGB888
|
||||
@@ -199,7 +209,7 @@ typedef struct _sensor {
|
||||
|
||||
// Sensor function pointers
|
||||
int (*init_status) (sensor_t *sensor);
|
||||
int (*reset) (sensor_t *sensor);
|
||||
int (*reset) (sensor_t *sensor); // Reset the configuration of the sensor, and return ESP_OK if reset is successful
|
||||
int (*set_pixformat) (sensor_t *sensor, pixformat_t pixformat);
|
||||
int (*set_framesize) (sensor_t *sensor, framesize_t framesize);
|
||||
int (*set_contrast) (sensor_t *sensor, int level);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#define __SCCB_H__
|
||||
#include <stdint.h>
|
||||
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_Read(uint8_t slv_addr, uint8_t reg);
|
||||
|
||||
@@ -25,6 +25,11 @@ static const char* TAG = "sccb";
|
||||
|
||||
#include "driver/i2c.h"
|
||||
|
||||
// support IDF 5.x
|
||||
#ifndef portTICK_RATE_MS
|
||||
#define portTICK_RATE_MS portTICK_PERIOD_MS
|
||||
#endif
|
||||
|
||||
#define SCCB_FREQ CONFIG_SCCB_CLK_FREQ /*!< I2C master frequency*/
|
||||
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||
@@ -33,16 +38,26 @@ static const char* TAG = "sccb";
|
||||
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||
#if CONFIG_SCCB_HARDWARE_I2C_PORT1
|
||||
const int SCCB_I2C_PORT = 1;
|
||||
const int SCCB_I2C_PORT_DEFAULT = 1;
|
||||
#else
|
||||
const int SCCB_I2C_PORT = 0;
|
||||
const int SCCB_I2C_PORT_DEFAULT = 0;
|
||||
#endif
|
||||
|
||||
static int sccb_i2c_port;
|
||||
static bool sccb_owns_i2c_port;
|
||||
|
||||
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;
|
||||
|
||||
memset(&conf, 0, sizeof(i2c_config_t));
|
||||
|
||||
sccb_i2c_port = SCCB_I2C_PORT_DEFAULT;
|
||||
sccb_owns_i2c_port = true;
|
||||
ESP_LOGI(TAG, "sccb_i2c_port=%d\n", sccb_i2c_port);
|
||||
|
||||
conf.mode = I2C_MODE_MASTER;
|
||||
conf.sda_io_num = pin_sda;
|
||||
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
@@ -50,30 +65,37 @@ int SCCB_Init(int pin_sda, int pin_scl)
|
||||
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.master.clk_speed = SCCB_FREQ;
|
||||
|
||||
i2c_param_config(SCCB_I2C_PORT, &conf);
|
||||
i2c_driver_install(SCCB_I2C_PORT, conf.mode, 0, 0, 0);
|
||||
return 0;
|
||||
if ((ret = i2c_param_config(sccb_i2c_port, &conf)) != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return i2c_driver_install(sccb_i2c_port, conf.mode, 0, 0, 0);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return i2c_driver_delete(SCCB_I2C_PORT);
|
||||
if (!sccb_owns_i2c_port) {
|
||||
return ESP_OK;
|
||||
}
|
||||
sccb_owns_i2c_port = false;
|
||||
return i2c_driver_delete(sccb_i2c_port);
|
||||
}
|
||||
|
||||
uint8_t SCCB_Probe(void)
|
||||
{
|
||||
uint8_t slave_addr = 0x0;
|
||||
// for (size_t i = 1; i < 0x80; i++) {
|
||||
// i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
// i2c_master_start(cmd);
|
||||
// i2c_master_write_byte(cmd, ( i << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||
// i2c_master_stop(cmd);
|
||||
// esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||
// i2c_cmd_link_delete(cmd);
|
||||
// if( ret == ESP_OK) {
|
||||
// ESP_LOGW(TAG, "Found I2C Device at 0x%02X", i);
|
||||
// }
|
||||
// }
|
||||
|
||||
for (size_t i = 0; i < CAMERA_MODEL_MAX; i++) {
|
||||
if (slave_addr == camera_sensor[i].sccb_addr) {
|
||||
continue;
|
||||
@@ -83,7 +105,7 @@ uint8_t SCCB_Probe(void)
|
||||
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);
|
||||
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;
|
||||
@@ -101,7 +123,7 @@ uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||
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();
|
||||
@@ -109,7 +131,7 @@ uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
|
||||
i2c_master_read_byte(cmd, &data, NACK_VAL);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
|
||||
@@ -126,7 +148,7 @@ uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data)
|
||||
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
|
||||
@@ -146,7 +168,7 @@ uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg)
|
||||
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);
|
||||
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();
|
||||
@@ -154,7 +176,7 @@ uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg)
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
|
||||
i2c_master_read_byte(cmd, &data, NACK_VAL);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data);
|
||||
@@ -175,7 +197,7 @@ uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data)
|
||||
i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "W [%04x]=%02x %d fail\n", reg, data, i++);
|
||||
|
||||
@@ -13,6 +13,9 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
|
||||
{CAMERA_GC032A, "GC032A", GC032A_SCCB_ADDR, GC032A_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_GC0308, "GC0308", GC0308_SCCB_ADDR, GC0308_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_BF3005, "BF3005", BF3005_SCCB_ADDR, BF3005_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_BF20A6, "BF20A6", BF20A6_SCCB_ADDR, BF20A6_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_SC101IOT, "SC101IOT", SC101IOT_SCCB_ADDR, SC101IOT_PID, FRAMESIZE_HD, false},
|
||||
{CAMERA_SC030IOT, "SC030IOT", SC030IOT_SCCB_ADDR, SC030IOT_PID, FRAMESIZE_VGA, false},
|
||||
};
|
||||
|
||||
const resolution_info_t resolution[FRAMESIZE_INVALID] = {
|
||||
|
||||
@@ -38,6 +38,11 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
// support IDF 5.x
|
||||
#ifndef portTICK_RATE_MS
|
||||
#define portTICK_RATE_MS portTICK_PERIOD_MS
|
||||
#endif
|
||||
|
||||
#include "esp_camera.h"
|
||||
|
||||
#define BOARD_WROVER_KIT 1
|
||||
@@ -94,8 +99,8 @@ static camera_config_t camera_config = {
|
||||
.pin_pwdn = CAM_PIN_PWDN,
|
||||
.pin_reset = CAM_PIN_RESET,
|
||||
.pin_xclk = CAM_PIN_XCLK,
|
||||
.pin_sscb_sda = CAM_PIN_SIOD,
|
||||
.pin_sscb_scl = CAM_PIN_SIOC,
|
||||
.pin_sccb_sda = CAM_PIN_SIOD,
|
||||
.pin_sccb_scl = CAM_PIN_SIOC,
|
||||
|
||||
.pin_d7 = CAM_PIN_D7,
|
||||
.pin_d6 = CAM_PIN_D6,
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
description: ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors.
|
||||
targets:
|
||||
- esp32
|
||||
- esp32s2
|
||||
- esp32s3
|
||||
url: https://github.com/espressif/esp32-camera
|
||||
|
||||
404
code/components/esp32-camera-master/sensors/bf20a6.c
Normal file
404
code/components/esp32-camera-master/sensors/bf20a6.c
Normal file
@@ -0,0 +1,404 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sccb.h"
|
||||
#include "bf20a6.h"
|
||||
#include "bf20a6_regs.h"
|
||||
#include "bf20a6_settings.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "bf20a6";
|
||||
#endif
|
||||
|
||||
#define H8(v) ((v)>>8)
|
||||
#define L8(v) ((v)&0xff)
|
||||
|
||||
//#define REG_DEBUG_ON
|
||||
|
||||
static int read_reg(uint8_t slv_addr, const uint16_t reg)
|
||||
{
|
||||
int ret = SCCB_Read(slv_addr, reg);
|
||||
// ESP_LOGI(TAG, "READ Register 0x%02x VALUE: 0x%02x", reg, ret);
|
||||
#ifdef REG_DEBUG_ON
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
|
||||
{
|
||||
int ret = SCCB_Write(slv_addr, reg, value);
|
||||
#ifdef REG_DEBUG_ON
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PRINT_REG
|
||||
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
|
||||
{
|
||||
return (read_reg(slv_addr, reg) & mask) == mask;
|
||||
}
|
||||
|
||||
static void print_regs(uint8_t slv_addr)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
ESP_LOGI(TAG, "REG list look ======================");
|
||||
for (size_t i = 0xf0; i <= 0xfe; i++) {
|
||||
ESP_LOGI(TAG, "reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
ESP_LOGI(TAG, "\npage 0 ===");
|
||||
write_reg(slv_addr, 0xfe, 0x00); // page 0
|
||||
for (size_t i = 0x03; i <= 0x24; i++) {
|
||||
ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
for (size_t i = 0x40; i <= 0x95; i++) {
|
||||
ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
ESP_LOGI(TAG, "\npage 3 ===");
|
||||
write_reg(slv_addr, 0xfe, 0x03); // page 3
|
||||
for (size_t i = 0x01; i <= 0x43; i++) {
|
||||
ESP_LOGI(TAG, "p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
}
|
||||
|
||||
static int read_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
|
||||
{
|
||||
int i = 0, ret = 0;
|
||||
while (regs[i][0] != REGLIST_TAIL) {
|
||||
if (regs[i][0] == REG_DLY) {
|
||||
vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
ret = read_reg(slv_addr, regs[i][0]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = SCCB_Read(sensor->slv_addr, reg);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
value = (ret & ~mask) | ((value << offset) & mask);
|
||||
ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
|
||||
{
|
||||
int i = 0, ret = 0;
|
||||
while (!ret && regs[i][0] != REGLIST_TAIL) {
|
||||
if (regs[i][0] == REG_DLY) {
|
||||
vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret;
|
||||
// Software Reset: clear all registers and reset them to their default values
|
||||
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x01);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Software Reset FAILED!");
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
ret = write_regs(sensor->slv_addr, bf20a6_default_init_regs);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Camera defaults loaded");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
// int test_value = read_regs(sensor->slv_addr, bf20a6_default_init_regs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret = 0;
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_YUV422:
|
||||
set_reg_bits(sensor, 0x12, 0, 1, 0);
|
||||
break;
|
||||
case PIXFORMAT_RAW:
|
||||
set_reg_bits(sensor, 0x12, 0, 1, 0x1);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "set_pix unsupport format");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
if (ret == 0) {
|
||||
sensor->pixformat = pixformat;
|
||||
ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
int ret = 0;
|
||||
if (framesize > FRAMESIZE_VGA) {
|
||||
return -1;
|
||||
}
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
|
||||
// Write MSBs
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, 0);
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, w >> 2);
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, 0);
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, h >> 2);
|
||||
|
||||
// Write LSBs
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1b, 0);
|
||||
|
||||
if ((w <= 320) && (h <= 240)) {
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 4));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 4));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 4));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 4));
|
||||
|
||||
} else if ((w <= 640) && (h <= 480)) {
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 8));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 8));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 8));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 8));
|
||||
}
|
||||
|
||||
// Delay
|
||||
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->status.hmirror = enable;
|
||||
//ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret |= set_reg_bits(sensor, 0x4a, 3, 0x01, enable);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set h-mirror to: %d", enable);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->status.vflip = enable;
|
||||
//ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret |= set_reg_bits(sensor, 0x4a, 2, 0x01, enable);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set v-flip to: %d", enable);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = write_reg(sensor->slv_addr, 0xb6, value);
|
||||
if (ret == 0) {
|
||||
sensor->status.colorbar = value;
|
||||
ESP_LOGD(TAG, "Set colorbar to: %d", value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_sharpness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Write(sensor->slv_addr, 0x70, level);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set sharpness to: %d", level);
|
||||
sensor->status.sharpness = level;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_reg(sensor_t *sensor, int reg, int mask)
|
||||
{
|
||||
int ret = 0;
|
||||
if (mask > 0xFF) {
|
||||
ESP_LOGE(TAG, "mask should not more than 0xff");
|
||||
} else {
|
||||
ret = read_reg(sensor->slv_addr, reg);
|
||||
}
|
||||
if (ret > 0) {
|
||||
ret &= mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
if (mask > 0xFF) {
|
||||
ESP_LOGE(TAG, "mask should not more than 0xff");
|
||||
} else {
|
||||
ret = read_reg(sensor->slv_addr, reg);
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
value = (ret & ~mask) | (value & mask);
|
||||
|
||||
if (mask > 0xFF) {
|
||||
|
||||
} else {
|
||||
ret = write_reg(sensor->slv_addr, reg, value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
// write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x6f);
|
||||
sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0xd6);
|
||||
sensor->status.saturation = 0;
|
||||
sensor->status.sharpness = SCCB_Read(sensor->slv_addr, 0x70);
|
||||
sensor->status.denoise = 0;
|
||||
sensor->status.ae_level = 0;
|
||||
sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x13);
|
||||
sensor->status.awb = 0;
|
||||
sensor->status.dcw = 0;
|
||||
sensor->status.agc = 0;
|
||||
sensor->status.aec = 0;
|
||||
sensor->status.hmirror = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x01);
|
||||
sensor->status.vflip = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x02);
|
||||
sensor->status.colorbar = 0;
|
||||
sensor->status.bpc = 0;
|
||||
sensor->status.wpc = 0;
|
||||
sensor->status.raw_gma = 0;
|
||||
sensor->status.lenc = 0;
|
||||
sensor->status.quality = 0;
|
||||
sensor->status.special_effect = 0;
|
||||
sensor->status.wb_mode = 0;
|
||||
sensor->status.awb_gain = 0;
|
||||
sensor->status.agc_gain = 0;
|
||||
sensor->status.aec_value = 0;
|
||||
sensor->status.aec2 = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val)
|
||||
{
|
||||
ESP_LOGW(TAG, "dummy Unsupported");
|
||||
return -1;
|
||||
}
|
||||
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
|
||||
{
|
||||
ESP_LOGW(TAG, "gainceiling Unsupported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bf20a6_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (BF20A6_SCCB_ADDR == slv_addr) {
|
||||
uint8_t MIDL = SCCB_Read(slv_addr, SENSOR_ID_LOW);
|
||||
uint8_t MIDH = SCCB_Read(slv_addr, SENSOR_ID_HIGH);
|
||||
uint16_t PID = MIDH << 8 | MIDL;
|
||||
if (BF20A6_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bf20a6_init(sensor_t *sensor)
|
||||
{
|
||||
sensor->init_status = init_status;
|
||||
sensor->reset = reset;
|
||||
sensor->set_pixformat = set_pixformat;
|
||||
sensor->set_framesize = set_framesize;
|
||||
sensor->set_contrast = set_dummy;
|
||||
sensor->set_brightness = set_dummy;
|
||||
sensor->set_saturation = set_dummy;
|
||||
sensor->set_sharpness = set_sharpness;
|
||||
sensor->set_denoise = set_dummy;
|
||||
sensor->set_gainceiling = set_gainceiling_dummy;
|
||||
sensor->set_quality = set_dummy;
|
||||
sensor->set_colorbar = set_colorbar;
|
||||
sensor->set_whitebal = set_dummy;
|
||||
sensor->set_gain_ctrl = set_dummy;
|
||||
sensor->set_exposure_ctrl = set_dummy;
|
||||
sensor->set_hmirror = set_hmirror; // set_hmirror;
|
||||
sensor->set_vflip = set_vflip; // set_vflip;
|
||||
|
||||
sensor->set_aec2 = set_dummy;
|
||||
sensor->set_awb_gain = set_dummy;
|
||||
sensor->set_agc_gain = set_dummy;
|
||||
sensor->set_aec_value = set_dummy;
|
||||
|
||||
sensor->set_special_effect = set_dummy;
|
||||
sensor->set_wb_mode = set_dummy;
|
||||
sensor->set_ae_level = set_dummy;
|
||||
|
||||
sensor->set_dcw = set_dummy;
|
||||
sensor->set_bpc = set_dummy;
|
||||
sensor->set_wpc = set_dummy;
|
||||
|
||||
sensor->set_raw_gma = set_dummy;
|
||||
sensor->set_lenc = set_dummy;
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = set_reg;
|
||||
sensor->set_res_raw = NULL;
|
||||
sensor->set_pll = NULL;
|
||||
sensor->set_xclk = NULL;
|
||||
|
||||
ESP_LOGD(TAG, "BF20A6 Attached");
|
||||
return 0;
|
||||
}
|
||||
@@ -88,10 +88,10 @@ static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
|
||||
static int write_regs(uint8_t slv_addr, const uint8_t (*regs)[2], size_t regs_size)
|
||||
{
|
||||
int i = 0, ret = 0;
|
||||
while (!ret && regs[i][0] != REGLIST_TAIL) {
|
||||
while (!ret && (i < regs_size)) {
|
||||
if (regs[i][0] == REG_DLY) {
|
||||
vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
@@ -132,11 +132,12 @@ static int reset(sensor_t *sensor)
|
||||
ESP_LOGE(TAG, "Software Reset FAILED!");
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs);
|
||||
|
||||
vTaskDelay(80 / portTICK_PERIOD_MS);
|
||||
ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs, sizeof(gc0308_sensor_default_regs)/(sizeof(uint8_t) * 2));
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Camera defaults loaded");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
vTaskDelay(80 / portTICK_PERIOD_MS);
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
set_reg_bits(sensor->slv_addr, 0x28, 4, 0x07, 1); //frequency division for esp32, ensure pclk <= 15MHz
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
#ifndef __BF20A6_H__
|
||||
#define __BF20A6_H__
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
/**
|
||||
* @brief Detect sensor pid
|
||||
*
|
||||
* @param slv_addr SCCB address
|
||||
* @param id Detection result
|
||||
* @return
|
||||
* 0: Can't detect this sensor
|
||||
* Nonzero: This sensor has been detected
|
||||
*/
|
||||
int bf20a6_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int bf20a6_init(sensor_t *sensor);
|
||||
|
||||
#endif // __BF20A6_H__
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* BF20A6 register definitions.
|
||||
*/
|
||||
#ifndef __BF20A6_REG_REGS_H__
|
||||
#define __BF20A6_REG_REGS_H__
|
||||
|
||||
#define SENSOR_ID_HIGH 0XFC
|
||||
#define SENSOR_ID_LOW 0XFD
|
||||
#define RESET_RELATED 0XF2
|
||||
|
||||
|
||||
#endif //__BF20A6_REG_REGS_H__
|
||||
@@ -0,0 +1,158 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define REG_DLY 0xffff
|
||||
#define REGLIST_TAIL 0xffff /* Array end token */
|
||||
|
||||
static const uint16_t bf20a6_default_init_regs[][2] = {
|
||||
{0xf2,0x01},
|
||||
{0x12,0x20},
|
||||
{0x3a,0x00},
|
||||
{0xe1,0x92},
|
||||
{0xe3,0x12},// PLL Control, important for framerate(choice: 0x02\0x12\0x22\0x32\0x82)
|
||||
{0xe0,0x00},
|
||||
{0x2a,0x98},
|
||||
{0xcd,0x17},
|
||||
{0xc0,0x10},
|
||||
{0xc6,0x1d},
|
||||
{0x10,0x35},
|
||||
{0xe2,0x09},
|
||||
{0xe4,0x72},
|
||||
{0xe5,0x22},
|
||||
{0xe6,0x24},
|
||||
{0xe7,0x64},
|
||||
{0xe8,0xa2}, // DVP:a2}, SPI:f2 VDDIO=1.8V,E8[2]=1},VDDIO=2.8V,E8[2]=0},
|
||||
{0x4a,0x00},
|
||||
{0x00,0x03},
|
||||
{0x1f,0x02},
|
||||
{0x22,0x02},
|
||||
{0x0c,0x31},
|
||||
|
||||
{0x00,0x00},
|
||||
{0x60,0x81},
|
||||
{0x61,0x81},
|
||||
|
||||
{0xa0,0x08},
|
||||
{0x01,0x1a},
|
||||
// {0x01,0x1a},
|
||||
// {0x01,0x1a},
|
||||
// {0x02,0x15},
|
||||
// {0x02,0x15},
|
||||
{0x02,0x15},
|
||||
{0x13,0x08},
|
||||
{0x8a,0x96},
|
||||
{0x8b,0x06},
|
||||
{0x87,0x18},
|
||||
|
||||
|
||||
{0x34,0x48}, // lens
|
||||
{0x35,0x40},
|
||||
{0x36,0x40},
|
||||
|
||||
{0x71,0x44},
|
||||
{0x72,0x48},
|
||||
{0x74,0xa2},
|
||||
{0x75,0xa9},
|
||||
{0x78,0x12},
|
||||
{0x79,0xa0},
|
||||
{0x7a,0x94},
|
||||
{0x7c,0x97},
|
||||
{0x40,0x30},
|
||||
{0x41,0x30},
|
||||
{0x42,0x28},
|
||||
{0x43,0x1f},
|
||||
{0x44,0x1c},
|
||||
{0x45,0x16},
|
||||
{0x46,0x13},
|
||||
{0x47,0x10},
|
||||
{0x48,0x0D},
|
||||
{0x49,0x0C},
|
||||
{0x4B,0x0A},
|
||||
{0x4C,0x0B},
|
||||
{0x4E,0x09},
|
||||
{0x4F,0x08},
|
||||
{0x50,0x08},
|
||||
|
||||
|
||||
{0x5f,0x29},
|
||||
{0x23,0x33},
|
||||
{0xa1,0x10}, // AWB
|
||||
{0xa2,0x0d},
|
||||
{0xa3,0x30},
|
||||
{0xa4,0x06},
|
||||
{0xa5,0x22},
|
||||
{0xa6,0x56},
|
||||
{0xa7,0x18},
|
||||
{0xa8,0x1a},
|
||||
{0xa9,0x12},
|
||||
{0xaa,0x12},
|
||||
{0xab,0x16},
|
||||
{0xac,0xb1},
|
||||
{0xba,0x12},
|
||||
{0xbb,0x12},
|
||||
{0xad,0x12},
|
||||
{0xae,0x56},
|
||||
{0xaf,0x0a},
|
||||
{0x3b,0x30},
|
||||
{0x3c,0x12},
|
||||
{0x3d,0x22},
|
||||
{0x3e,0x3f},
|
||||
{0x3f,0x28},
|
||||
{0xb8,0xc3},
|
||||
{0xb9,0xa3},
|
||||
{0x39,0x47}, // pure color threshold
|
||||
{0x26,0x13},
|
||||
{0x27,0x16},
|
||||
{0x28,0x14},
|
||||
{0x29,0x18},
|
||||
{0xee,0x0d},
|
||||
|
||||
|
||||
{0x13,0x05},
|
||||
{0x24,0x3C},
|
||||
{0x81,0x20},
|
||||
{0x82,0x40},
|
||||
{0x83,0x30},
|
||||
{0x84,0x58},
|
||||
{0x85,0x30},
|
||||
{0x92,0x08},
|
||||
{0x86,0x80},
|
||||
{0x8a,0x96},
|
||||
{0x91,0xff},
|
||||
{0x94,0x62},
|
||||
{0x9a,0x18}, // outdoor threshold
|
||||
{0xf0,0x45}, // integral time control, important for framerate(choice: 0x46\0x45\0x44..)
|
||||
{0x51,0x17}, // color normal
|
||||
{0x52,0x03},
|
||||
{0x53,0x5F},
|
||||
{0x54,0x47},
|
||||
{0x55,0x66},
|
||||
{0x56,0x0F},
|
||||
{0x7e,0x14},
|
||||
{0x57,0x36}, // color
|
||||
{0x58,0x2A},
|
||||
{0x59,0xAA},
|
||||
{0x5a,0xA8},
|
||||
{0x5b,0x43},
|
||||
{0x5c,0x10},
|
||||
{0x5d,0x00},
|
||||
{0x7d,0x36},
|
||||
{0x5e,0x10},
|
||||
|
||||
{0xd6,0x88}, // contrast
|
||||
{0xd5,0x20}, // bright
|
||||
{0xb0,0x84}, // low light ctrl in gray section
|
||||
{0xb5,0x08}, // the threshold of GLB_GAIN
|
||||
{0xb1,0xc8}, // saturation
|
||||
{0xb2,0xc0},
|
||||
{0xb3,0xd0},
|
||||
{0xb4,0xB0},
|
||||
|
||||
{0x32,0x10},
|
||||
// {0x8a,0x00},
|
||||
// {0x8b,0x10},
|
||||
{0xa0,0x09},
|
||||
{0x00,0x03},
|
||||
{0x0b,0x02},
|
||||
{REGLIST_TAIL, 0x00},
|
||||
};
|
||||
@@ -3,10 +3,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define REG_DLY 0xffff
|
||||
#define REGLIST_TAIL 0x0000 /* Array end token */
|
||||
#define REG_DLY 0xff
|
||||
|
||||
static const uint16_t gc0308_sensor_default_regs[][2] = {
|
||||
static const uint8_t gc0308_sensor_default_regs[][2] = {
|
||||
{0xfe, 0x00},
|
||||
{0xec, 0x20},
|
||||
{0x05, 0x00},
|
||||
@@ -239,7 +238,21 @@ static const uint16_t gc0308_sensor_default_regs[][2] = {
|
||||
{0x65, 0xd3},
|
||||
{0x66, 0x60},
|
||||
{0xfe, 0x00},
|
||||
{REGLIST_TAIL, 0x00},
|
||||
|
||||
{0x01, 0x32}, //frame setting
|
||||
{0x02, 0x0c},
|
||||
{0x0f, 0x01},
|
||||
{0xe2, 0x00},
|
||||
{0xe3, 0x78},
|
||||
{0xe4, 0x00},
|
||||
{0xe5, 0xfe},
|
||||
{0xe6, 0x01},
|
||||
{0xe7, 0xe0},
|
||||
{0xe8, 0x01},
|
||||
{0xe9, 0xe0},
|
||||
{0xea, 0x01},
|
||||
{0xeb, 0xe0},
|
||||
{0xfe, 0x00},
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,7 +42,8 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
|
||||
{ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
|
||||
|
||||
//sys reset
|
||||
{0x3000, 0x00},
|
||||
{0x3000, 0x20}, // reset MCU
|
||||
{REG_DLY, 10}, // delay 10ms
|
||||
{0x3002, 0x1c},
|
||||
|
||||
//clock enable
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
*
|
||||
* SC030IOT DVP driver.
|
||||
*
|
||||
*/
|
||||
#ifndef __SC030IOT_H__
|
||||
#define __SC030IOT_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 sc030iot_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int sc030iot_init(sensor_t *sensor);
|
||||
|
||||
#endif // __SC030IOT_H__
|
||||
@@ -0,0 +1,491 @@
|
||||
//version: V01P00_20220303
|
||||
//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
|
||||
//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
|
||||
//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
|
||||
//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
|
||||
//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
|
||||
//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
|
||||
//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
|
||||
//pin :BIT0 pwdn// BIT1:reset
|
||||
//avdd 0:3.3V// 1:2.5V// 2:1.8V
|
||||
//dovdd 0:2.8V// 1:2.5V// 2:1.8V
|
||||
//dvdd 0:1.8V// 1:1.5V// 2:1.2V
|
||||
|
||||
/*
|
||||
[DataBase]
|
||||
DBName=Dothinkey
|
||||
|
||||
[Vendor]
|
||||
VendorName=SmartSens
|
||||
|
||||
[Sensor]
|
||||
SensorName=SC031IOT
|
||||
width=640
|
||||
height=480
|
||||
port=1
|
||||
type=2
|
||||
pin=3
|
||||
SlaveID=0xd0
|
||||
mode=0
|
||||
FlagReg=0xf7
|
||||
FlagMask=0xff
|
||||
FlagData=0xfa
|
||||
FlagReg1=0xf8
|
||||
FlagMask1=0xff
|
||||
FlagData1=0x46
|
||||
outformat=0
|
||||
mclk=20
|
||||
avdd=2.80000
|
||||
dovdd=2.800000
|
||||
dvdd=1.5
|
||||
|
||||
Ext0=0
|
||||
Ext1=0
|
||||
Ext2=0
|
||||
AFVCC=0.0000
|
||||
VPP=0.000000
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t sc030iot_default_init_regs[][2] = {
|
||||
{0xf0, 0x30},
|
||||
{0x01, 0xff},
|
||||
{0x02, 0xff},
|
||||
{0x22, 0x07},
|
||||
{0x19, 0xff},
|
||||
{0x3f, 0x82},
|
||||
{0x30, 0x02},
|
||||
{0xf0, 0x01},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0x72, 0x20},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x75, 0x10},
|
||||
{0x76, 0x81},
|
||||
{0x77, 0x88},
|
||||
{0x78, 0xe1},
|
||||
{0x79, 0x01},
|
||||
{0xf5, 0x01},
|
||||
{0xf4, 0x0a},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x79},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x33},
|
||||
{0x02, 0x12},
|
||||
{0x7c, 0x02},
|
||||
{0x7d, 0x0e},
|
||||
{0xa2, 0x04},
|
||||
{0x5e, 0x06},
|
||||
{0x5f, 0x0a},
|
||||
{0x0b, 0x58},
|
||||
{0x06, 0x38},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x39},
|
||||
{0x02, 0x70},
|
||||
{0xf0, 0x45},
|
||||
{0x09, 0x1c},
|
||||
{0xf0, 0x37},
|
||||
{0x22, 0x0d},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x10},
|
||||
{0xb1, 0x80},
|
||||
{0x34, 0x40},
|
||||
{0x0b, 0x54},
|
||||
{0xb2, 0x78},
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x30},
|
||||
{0x38, 0x44},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x01, 0x10},
|
||||
{0x0b, 0x6c},
|
||||
{0x06, 0x24},
|
||||
{0xf0, 0x36},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x02},
|
||||
{0xa6, 0x40},
|
||||
{0xa7, 0x47},
|
||||
{0xe8, 0x5f},
|
||||
{0xa8, 0x51},
|
||||
{0xa9, 0x44},
|
||||
{0xe9, 0x36},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x64, 0x17},
|
||||
{0x90, 0x01},
|
||||
{0x91, 0x03},
|
||||
{0x92, 0x07},
|
||||
{0x01, 0x10},
|
||||
{0x93, 0x10},
|
||||
{0x94, 0x10},
|
||||
{0x95, 0x10},
|
||||
{0x96, 0x01},
|
||||
{0x97, 0x07},
|
||||
{0x98, 0x1f},
|
||||
{0x99, 0x10},
|
||||
{0x9a, 0x20},
|
||||
{0x9b, 0x28},
|
||||
{0x9c, 0x28},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0x54},
|
||||
{0xb6, 0x40},
|
||||
{0xb7, 0x41},
|
||||
{0xb8, 0x43},
|
||||
{0xb9, 0x47},
|
||||
{0xba, 0x4f},
|
||||
{0xb0, 0x8b},
|
||||
{0xb1, 0x8b},
|
||||
{0xb2, 0x8b},
|
||||
{0xb3, 0x9b},
|
||||
{0xb4, 0xb8},
|
||||
{0xb5, 0xf0},
|
||||
{0x7e, 0x41},
|
||||
{0x7f, 0x47},
|
||||
{0x77, 0x80},
|
||||
{0x78, 0x84},
|
||||
{0x79, 0x8a},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x43},
|
||||
{0x97, 0x44},
|
||||
{0x98, 0x54},
|
||||
{0xf0, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x74},
|
||||
{0xf0, 0x3f},
|
||||
{0x03, 0xa1},
|
||||
{0xf0, 0x36},//cvbs_off
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
{0xf0, 0x37},
|
||||
{0x24, 0x21},
|
||||
{0xf0, 0x36},
|
||||
{0x41, 0x00},
|
||||
{0xea, 0x09},
|
||||
{0xeb, 0x03},
|
||||
{0xec, 0x19},
|
||||
{0xed, 0x38},
|
||||
{0xe9, 0x30},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x00},
|
||||
{0x34, 0x00},
|
||||
{0xb1, 0x00},
|
||||
{0xf0, 0x00},
|
||||
{0xe0, 0x04},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x32, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x3e, 0xe0},
|
||||
{0x70, 0x56},
|
||||
{0x7c, 0x43},
|
||||
{0x7d, 0x47},
|
||||
{0x74, 0x00},
|
||||
{0x75, 0x00},
|
||||
{0x76, 0x00},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x22},
|
||||
{0x97, 0x22},
|
||||
{0x98, 0x22},
|
||||
{0xf0, 0x00},
|
||||
{0x72, 0x38},
|
||||
{0x7a, 0x80},
|
||||
{0x85, 0x18},
|
||||
{0x9b, 0x35},
|
||||
{0x9e, 0x20},
|
||||
{0xd0, 0x66},
|
||||
{0xd1, 0x34},
|
||||
{0Xd3, 0x44},
|
||||
{0xd6, 0x44},
|
||||
{0xb0, 0x41},
|
||||
{0xb2, 0x48},
|
||||
{0xb3, 0xf4},
|
||||
{0xb4, 0x0b},
|
||||
{0xb5, 0x78},
|
||||
{0xba, 0xff},
|
||||
{0xbb, 0xc0},
|
||||
{0xbc, 0x90},
|
||||
{0xbd, 0x3a},
|
||||
{0xc1, 0x67},
|
||||
{0xf0, 0x01},
|
||||
{0x20, 0x11},
|
||||
{0x23, 0x90},
|
||||
{0x24, 0x15},
|
||||
{0x25, 0x87},
|
||||
{0xbc, 0x9f},
|
||||
{0xbd, 0x3a},
|
||||
{0x48, 0xe6},
|
||||
{0x49, 0xc0},
|
||||
{0x4a, 0xd0},
|
||||
{0x4b, 0x48},
|
||||
|
||||
// [cvbs_on]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xf1},
|
||||
|
||||
// [cvbs_off]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
};
|
||||
|
||||
/*
|
||||
[Sensor]
|
||||
SensorName=SC031IOT
|
||||
width=640
|
||||
height=480
|
||||
port=1
|
||||
type=2
|
||||
pin=3
|
||||
SlaveID=0xd0
|
||||
mode=0
|
||||
FlagReg=0xf7
|
||||
FlagMask=0xff
|
||||
FlagData=0xfa
|
||||
FlagReg1=0xf8
|
||||
FlagMask1=0xff
|
||||
FlagData1=0x46
|
||||
outformat=0
|
||||
mclk=27
|
||||
avdd=2.80000
|
||||
dovdd=2.800000
|
||||
dvdd=1.5
|
||||
|
||||
Ext0=0
|
||||
Ext1=0
|
||||
Ext2=0
|
||||
AFVCC=0.0000
|
||||
VPP=0.000000
|
||||
*/
|
||||
/* 27M MCLK, 30fps
|
||||
static const uint8_t sc030iot_default_init_regs[][2] = {
|
||||
{0xf0, 0x30},
|
||||
{0x01, 0xff},
|
||||
{0x02, 0xff},
|
||||
{0x22, 0x07},
|
||||
{0x19, 0xff},
|
||||
{0x3f, 0x82},
|
||||
{0x30, 0x02},
|
||||
{0xf0, 0x01},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0x72, 0x20},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x75, 0x10},
|
||||
{0x76, 0x81},
|
||||
{0x77, 0x88},
|
||||
{0x78, 0xe1},
|
||||
{0x79, 0x01},
|
||||
{0xf5, 0x01},
|
||||
{0xf4, 0x0a},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x79},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x33},
|
||||
{0x02, 0x12},
|
||||
{0x7c, 0x02},
|
||||
{0x7d, 0x0e},
|
||||
{0xa2, 0x04},
|
||||
{0x5e, 0x06},
|
||||
{0x5f, 0x0a},
|
||||
{0x0b, 0x58},
|
||||
{0x06, 0x38},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x39},
|
||||
{0x02, 0x70},
|
||||
{0xf0, 0x45},
|
||||
{0x09, 0x1c},
|
||||
{0xf0, 0x37},
|
||||
{0x22, 0x0d},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x10},
|
||||
{0xb1, 0x80},
|
||||
{0x34, 0x40},
|
||||
{0x0b, 0x54},
|
||||
{0xb2, 0x78},
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x30},
|
||||
{0x38, 0x44},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x01, 0x10},
|
||||
{0x0b, 0x6c},
|
||||
{0x06, 0x24},
|
||||
{0xf0, 0x36},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x02},
|
||||
{0xa6, 0x40},
|
||||
{0xa7, 0x47},
|
||||
{0xe8, 0x5f},
|
||||
{0xa8, 0x51},
|
||||
{0xa9, 0x44},
|
||||
{0xe9, 0x36},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x64, 0x17},
|
||||
{0x90, 0x01},
|
||||
{0x91, 0x03},
|
||||
{0x92, 0x07},
|
||||
{0x01, 0x10},
|
||||
{0x93, 0x10},
|
||||
{0x94, 0x10},
|
||||
{0x95, 0x10},
|
||||
{0x96, 0x01},
|
||||
{0x97, 0x07},
|
||||
{0x98, 0x1f},
|
||||
{0x99, 0x10},
|
||||
{0x9a, 0x20},
|
||||
{0x9b, 0x28},
|
||||
{0x9c, 0x28},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0x54},
|
||||
{0xb6, 0x40},
|
||||
{0xb7, 0x41},
|
||||
{0xb8, 0x43},
|
||||
{0xb9, 0x47},
|
||||
{0xba, 0x4f},
|
||||
{0xb0, 0x8b},
|
||||
{0xb1, 0x8b},
|
||||
{0xb2, 0x8b},
|
||||
{0xb3, 0x9b},
|
||||
{0xb4, 0xb8},
|
||||
{0xb5, 0xf0},
|
||||
{0x7e, 0x41},
|
||||
{0x7f, 0x47},
|
||||
{0x77, 0x80},
|
||||
{0x78, 0x84},
|
||||
{0x79, 0x8a},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x43},
|
||||
{0x97, 0x44},
|
||||
{0x98, 0x54},
|
||||
{0xf0, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x74},
|
||||
{0xf0, 0x3f},
|
||||
{0x03, 0x93},
|
||||
{0xf0, 0x36},//cvbs_off
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
{0xf0, 0x37},
|
||||
{0x24, 0x21},
|
||||
{0xf0, 0x36},
|
||||
{0x41, 0x00},
|
||||
{0xe9, 0x2c},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x00},
|
||||
{0x34, 0x00},
|
||||
{0xb1, 0x00},
|
||||
{0xf0, 0x00},
|
||||
{0xe0, 0x04},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x32, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x3e, 0xe0},
|
||||
{0x70, 0x56},
|
||||
{0x7c, 0x43},
|
||||
{0x7d, 0x47},
|
||||
{0x74, 0x00},
|
||||
{0x75, 0x00},
|
||||
{0x76, 0x00},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x22},
|
||||
{0x97, 0x22},
|
||||
{0x98, 0x22},
|
||||
{0xf0, 0x00},
|
||||
{0x72, 0x38},
|
||||
{0x7a, 0x80},
|
||||
{0x85, 0x18},
|
||||
{0x9b, 0x35},
|
||||
{0x9e, 0x20},
|
||||
{0xd0, 0x66},
|
||||
{0xd1, 0x34},
|
||||
{0Xd3, 0x44},
|
||||
{0xd6, 0x44},
|
||||
{0xb0, 0x41},
|
||||
{0xb2, 0x48},
|
||||
{0xb3, 0xf4},
|
||||
{0xb4, 0x0b},
|
||||
{0xb5, 0x78},
|
||||
{0xba, 0xff},
|
||||
{0xbb, 0xc0},
|
||||
{0xbc, 0x90},
|
||||
{0xbd, 0x3a},
|
||||
{0xc1, 0x67},
|
||||
{0xf0, 0x01},
|
||||
{0x20, 0x11},
|
||||
{0x23, 0x90},
|
||||
{0x24, 0x15},
|
||||
{0x25, 0x87},
|
||||
{0xbc, 0x9f},
|
||||
{0xbd, 0x3a},
|
||||
{0x48, 0xe6},
|
||||
{0x49, 0xc0},
|
||||
{0x4a, 0xd0},
|
||||
{0x4b, 0x48},
|
||||
|
||||
// [cvbs_on]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xf1},
|
||||
|
||||
// [cvbs_off]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
};
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
*
|
||||
* SC101IOT DVP driver.
|
||||
*
|
||||
*/
|
||||
#ifndef __SC101IOT_H__
|
||||
#define __SC101IOT_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 sc101iot_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int sc101iot_init(sensor_t *sensor);
|
||||
|
||||
#endif // __SC101IOT_H__
|
||||
@@ -0,0 +1,257 @@
|
||||
//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
|
||||
//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
|
||||
//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
|
||||
//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
|
||||
//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
|
||||
//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
|
||||
//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
|
||||
//pin :BIT0 pwdn// BIT1:reset
|
||||
//avdd 0:2.8V// 1:2.5V// 2:1.8V
|
||||
//dovdd 0:2.8V// 1:2.5V// 2:1.8V
|
||||
//dvdd 0:1.8V// 1:1.5V// 2:1.2V
|
||||
/*
|
||||
[DataBase]
|
||||
DBName=DemoSens
|
||||
|
||||
[Vendor]
|
||||
VendorName=SmartSens
|
||||
I2C_CRC=0
|
||||
|
||||
[Sensor]
|
||||
SensorName=SC101AP_raw
|
||||
width=1280
|
||||
height=720
|
||||
port=1
|
||||
type=2
|
||||
pin=3
|
||||
SlaveID=0xd0
|
||||
mode=0
|
||||
FlagReg=0xf7
|
||||
FlagMask=0xff
|
||||
FlagData=0xda
|
||||
FlagReg1=0xf8
|
||||
FlagMask1=0xff
|
||||
FlagData1=0x4a
|
||||
outformat=0
|
||||
mclk=20
|
||||
avdd=2.800000
|
||||
dovdd=2.800000
|
||||
dvdd=1.200000
|
||||
|
||||
Ext0=0
|
||||
Ext1=0
|
||||
Ext2=0
|
||||
AFVCC=0.00
|
||||
VPP=0.000000
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t sc101iot_default_init_regs[][2] = {
|
||||
#if CONFIG_SC101IOT_720P_15FPS_ENABLED // 720P+YUV422+15FPS sensor default regs
|
||||
/* Here are some test results:
|
||||
# size xclk fps pic pclk
|
||||
# ------- ------- ------ --------- ------- --- --- --- --- ---
|
||||
# 720p 4 3 err
|
||||
# 720p 8 5 normal 15
|
||||
# 720p 10 7.8 normal 19
|
||||
# 720p 20 15 warning 37.5
|
||||
# VGA 8 6 normal
|
||||
# VGA 20 16 normal
|
||||
|
||||
*/
|
||||
{0xf0, 0x30},
|
||||
{0x01, 0xff},
|
||||
{0x02, 0xe0},
|
||||
{0x30, 0x10},
|
||||
{0x3f, 0x81},
|
||||
{0xf0, 0x00},
|
||||
{0x70, 0x6b},
|
||||
{0x72, 0x30},
|
||||
{0x84, 0xb4},
|
||||
{0x8b, 0x00},
|
||||
{0x8c, 0x20},
|
||||
{0x8d, 0x02},
|
||||
{0x8e, 0xec},
|
||||
{0x9e, 0x10},
|
||||
{0xb0, 0xc1},
|
||||
{0xc8, 0x10},
|
||||
{0xc9, 0x10},
|
||||
{0xc6, 0x00},
|
||||
{0xe0, 0x0f},
|
||||
{0xb5, 0xf0},
|
||||
{0xde, 0x80},
|
||||
{0xb5, 0xf0},
|
||||
{0xde, 0x80},
|
||||
{0xb2, 0x50},
|
||||
{0xb3, 0xfc},
|
||||
{0xb4, 0x40},
|
||||
{0xb5, 0xc0},
|
||||
{0xb6, 0x50},
|
||||
{0xb7, 0xfc},
|
||||
{0xb8, 0x40},
|
||||
{0xb9, 0xc0},
|
||||
{0xba, 0xff},
|
||||
{0xbb, 0xcc},
|
||||
{0xbc, 0xa9},
|
||||
{0xbd, 0x7d},
|
||||
{0xc1, 0x77},
|
||||
{0xf0, 0x01},
|
||||
{0x70, 0x02},
|
||||
{0x71, 0x02},
|
||||
{0x72, 0x50},
|
||||
{0x73, 0x02},
|
||||
{0x74, 0xd2},
|
||||
{0x75, 0x20},
|
||||
{0x76, 0x81},
|
||||
{0x77, 0x8c},
|
||||
{0x78, 0x81},
|
||||
{0xf4, 0x01},
|
||||
{0xf5, 0x00},
|
||||
{0xf6, 0x00},
|
||||
{0xf0, 0x36},
|
||||
{0x40, 0x03},
|
||||
{0x41, 0x01},
|
||||
{0xf0, 0x39},
|
||||
{0x02, 0x70},
|
||||
{0xf0, 0x32},
|
||||
{0x41, 0x00},
|
||||
{0x43, 0x01},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x45},
|
||||
{0x09, 0x20},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x10},
|
||||
{0xf0, 0x30},
|
||||
{0x38, 0x44},
|
||||
{0xf0, 0x39},
|
||||
{0x07, 0x00},
|
||||
{0x08, 0x19},
|
||||
{0x47, 0x00},
|
||||
{0x48, 0x00},
|
||||
{0xf0, 0x37},
|
||||
{0x24, 0x31},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x02},
|
||||
{0xa6, 0x51},
|
||||
{0xa7, 0x57},
|
||||
{0xe8, 0x5f},
|
||||
{0xa8, 0x50},
|
||||
{0xa9, 0x50},
|
||||
{0xe9, 0x50},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x58},
|
||||
{0xb2, 0x78},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x03},
|
||||
{0xa6, 0x51},
|
||||
{0xa7, 0x57},
|
||||
{0xaa, 0x01},
|
||||
{0xab, 0x28},
|
||||
{0xac, 0x01},
|
||||
{0xad, 0x38},
|
||||
{0xf0, 0x33},
|
||||
{0x0a, 0x01},
|
||||
{0x0b, 0x28},
|
||||
{0xf0, 0x33},
|
||||
{0x64, 0x0f},
|
||||
{0xec, 0x51},
|
||||
{0xed, 0x57},
|
||||
{0x06, 0x58},
|
||||
{0xe9, 0x58},
|
||||
{0xeb, 0x68},
|
||||
{0xf0, 0x33},
|
||||
{0x64, 0x0f},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0xdf},
|
||||
{0xb6, 0x40},
|
||||
{0xb7, 0x51},
|
||||
{0xb8, 0x53},
|
||||
{0xb9, 0x57},
|
||||
{0xba, 0x5f},
|
||||
{0xb0, 0x84},
|
||||
{0xb1, 0x82},
|
||||
{0xb2, 0x84},
|
||||
{0xb3, 0x88},
|
||||
{0xb4, 0x90},
|
||||
{0xb5, 0x90},
|
||||
{0xf0, 0x36},
|
||||
{0x7e, 0x50},
|
||||
{0x7f, 0x51},
|
||||
{0x77, 0x81},
|
||||
{0x78, 0x86},
|
||||
{0x79, 0x89},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0xdf},
|
||||
{0x9c, 0x51},
|
||||
{0x9d, 0x57},
|
||||
{0x90, 0x54},
|
||||
{0x91, 0x54},
|
||||
{0x92, 0x56},
|
||||
{0xf0, 0x36},
|
||||
{0xa0, 0x51},
|
||||
{0xa1, 0x57},
|
||||
{0x96, 0x33},
|
||||
{0x97, 0x43},
|
||||
{0x98, 0x43},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0xdf},
|
||||
{0x7c, 0x40},
|
||||
{0x7d, 0x53},
|
||||
{0x74, 0xd0},
|
||||
{0x75, 0xf0},
|
||||
{0x76, 0xf0},
|
||||
{0xf0, 0x37},
|
||||
{0x0f, 0xd5},
|
||||
{0x7a, 0x40},
|
||||
{0x7b, 0x57},
|
||||
{0x71, 0x09},
|
||||
{0x72, 0x09},
|
||||
{0x73, 0x05},
|
||||
{0xf0, 0x33},
|
||||
{0x01, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0xfb},
|
||||
{0xf0, 0x36},
|
||||
{0x3c, 0x0d},
|
||||
{0xf0, 0x33},
|
||||
{0x14, 0x95},
|
||||
{0xf0, 0x33},
|
||||
{0x8f, 0x80},
|
||||
{0xf0, 0x37},
|
||||
{0x27, 0x14},
|
||||
{0x28, 0x03},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0xf4},
|
||||
{0xf0, 0x33},
|
||||
{0x01, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x79, 0x89},
|
||||
{0xf0, 0x34},
|
||||
{0xac, 0x01},
|
||||
{0xad, 0x40},
|
||||
{0xf0, 0x33},
|
||||
{0xeb, 0x70},
|
||||
{0xf0, 0x34},
|
||||
{0xa8, 0x50},
|
||||
{0xa9, 0x50},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x58},
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x41, 0x51},
|
||||
{0xf0, 0x3f},
|
||||
{0x03, 0x09},
|
||||
{0xf0, 0x32},
|
||||
{0x0c, 0x06},
|
||||
{0x0d, 0x82},
|
||||
{0x0e, 0x02},
|
||||
{0x0f, 0xee},
|
||||
{0xf0, 0x36},
|
||||
{0xea, 0x09},
|
||||
{0xeb, 0xf5},
|
||||
{0xec, 0x11},
|
||||
{0xed, 0x27},
|
||||
{0xe9, 0x20},
|
||||
#endif
|
||||
};
|
||||
335
code/components/esp32-camera-master/sensors/sc030iot.c
Normal file
335
code/components/esp32-camera-master/sensors/sc030iot.c
Normal file
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* SC030IOT driver.
|
||||
*
|
||||
* Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "sc030iot.h"
|
||||
#include "sc030iot_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 = "sc030";
|
||||
#endif
|
||||
|
||||
#define SC030_SENSOR_ID_HIGH_REG 0XF7
|
||||
#define SC030_SENSOR_ID_LOW_REG 0XF8
|
||||
#define SC030_MAX_FRAME_WIDTH (640)
|
||||
#define SC030_MAX_FRAME_HIGH (480)
|
||||
|
||||
// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int get_reg(sensor_t *sensor, int reg, int reg_value_mask)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Read(sensor->slv_addr, reg_low);
|
||||
if(ret > 0){
|
||||
ret &= reg_value_mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Write(sensor->slv_addr, reg_low, value & 0xFF);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
|
||||
{
|
||||
int i=0, res = 0;
|
||||
while (i<regs_entry_len) {
|
||||
res = SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = get_reg(sensor, reg, 0xff);
|
||||
if(ret < 0){
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
value = (ret & ~mask) | ((value << offset) & mask);
|
||||
ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
|
||||
#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
|
||||
#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // mirror on
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // mirror off
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable test pattern mode
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_sharpness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
|
||||
WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
|
||||
WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_agc_gain(sensor_t *sensor, int gain)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
|
||||
WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
|
||||
WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
|
||||
WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
|
||||
WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_aec_value(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
|
||||
WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_awb_gain(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
|
||||
WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
|
||||
WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_saturation(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
|
||||
WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
|
||||
WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_contrast(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
|
||||
WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret = set_regs(sensor, sc030iot_default_init_regs, sizeof(sc030iot_default_init_regs)/(sizeof(uint8_t) * 2));
|
||||
|
||||
// Delay
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// ESP_LOGI(TAG, "set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
|
||||
// ESP_LOGI(TAG, "0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "reset fail");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
|
||||
{
|
||||
int ret = 0;
|
||||
//sc:H_start={0x0172[1:0],0x0170},H_end={0x0172[5:4],0x0171},
|
||||
WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x03) | (((offset_x+w)>>4)&0x30));
|
||||
|
||||
//sc:V_start={0x0175[1:0],0x0173},H_end={0x0175[5:4],0x0174},
|
||||
WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x03) | (((offset_y+h)>>4)&0x30));
|
||||
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
if(w>SC030_MAX_FRAME_WIDTH || h > SC030_MAX_FRAME_HIGH) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
uint16_t offset_x = (640-w) /2;
|
||||
uint16_t offset_y = (480-h) /2;
|
||||
|
||||
if(set_window(sensor, offset_x, offset_y, w, h)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
return 0;
|
||||
err:
|
||||
ESP_LOGE(TAG, "frame size err");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret=0;
|
||||
sensor->pixformat = pixformat;
|
||||
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_RGB565:
|
||||
case PIXFORMAT_RAW:
|
||||
case PIXFORMAT_GRAYSCALE:
|
||||
ESP_LOGE(TAG, "Not support");
|
||||
break;
|
||||
case PIXFORMAT_YUV422: // For now, sc030/sc031 sensor only support YUV422.
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val){ return -1; }
|
||||
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->xclk_freq_hz = xclk * 1000000U;
|
||||
ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int 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);
|
||||
uint8_t MIDH = SCCB_Read(slv_addr, SC030_SENSOR_ID_HIGH_REG);
|
||||
uint16_t PID = MIDH << 8 | MIDL;
|
||||
if (SC030IOT_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc030iot_init(sensor_t *sensor)
|
||||
{
|
||||
// Set function pointers
|
||||
sensor->reset = reset;
|
||||
sensor->init_status = init_status;
|
||||
sensor->set_pixformat = set_pixformat;
|
||||
sensor->set_framesize = set_framesize;
|
||||
|
||||
sensor->set_saturation= set_saturation;
|
||||
sensor->set_colorbar = set_colorbar;
|
||||
sensor->set_hmirror = set_hmirror;
|
||||
sensor->set_vflip = set_vflip;
|
||||
sensor->set_sharpness = set_sharpness;
|
||||
sensor->set_agc_gain = set_agc_gain;
|
||||
sensor->set_aec_value = set_aec_value;
|
||||
sensor->set_awb_gain = set_awb_gain;
|
||||
sensor->set_contrast = set_contrast;
|
||||
//not supported
|
||||
sensor->set_denoise = set_dummy;
|
||||
sensor->set_quality = set_dummy;
|
||||
sensor->set_special_effect = set_dummy;
|
||||
sensor->set_wb_mode = set_dummy;
|
||||
sensor->set_ae_level = set_dummy;
|
||||
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = set_reg;
|
||||
sensor->set_xclk = set_xclk;
|
||||
|
||||
ESP_LOGD(TAG, "sc030iot Attached");
|
||||
|
||||
return 0;
|
||||
}
|
||||
342
code/components/esp32-camera-master/sensors/sc101iot.c
Normal file
342
code/components/esp32-camera-master/sensors/sc101iot.c
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* SC101IOT driver.
|
||||
*
|
||||
* Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "sc101iot.h"
|
||||
#include "sc101iot_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 = "sc101";
|
||||
#endif
|
||||
|
||||
#define SC101_SENSOR_ID_HIGH_REG 0XF7
|
||||
#define SC101_SENSOR_ID_LOW_REG 0XF8
|
||||
#define SC101_MAX_FRAME_WIDTH (1280)
|
||||
#define SC101_MAX_FRAME_HIGH (720)
|
||||
|
||||
// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int get_reg(sensor_t *sensor, int reg, int mask)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Read(sensor->slv_addr, reg_low);
|
||||
if(ret > 0){
|
||||
ret &= mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Write(sensor->slv_addr, reg_low, value & 0xFF);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
|
||||
{
|
||||
int i=0, res = 0;
|
||||
while (i<regs_entry_len) {
|
||||
res = SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = get_reg(sensor, reg, 0xff);
|
||||
if(ret < 0){
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
value = (ret & ~mask) | ((value << offset) & mask);
|
||||
ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
|
||||
#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
|
||||
#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // enable mirror
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // disable mirror
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable colorbar mode
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_raw_gma(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 1, 1, enable & 0xff); // enable gamma compensation
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_sharpness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
|
||||
WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
|
||||
WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_agc_gain(sensor_t *sensor, int gain)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
|
||||
WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
|
||||
WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
|
||||
WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
|
||||
WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_aec_value(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
|
||||
WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_awb_gain(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
|
||||
WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
|
||||
WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_saturation(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
|
||||
WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
|
||||
WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_contrast(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
|
||||
WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret = set_regs(sensor, sc101iot_default_init_regs, sizeof(sc101iot_default_init_regs)/(sizeof(uint8_t) * 2));
|
||||
|
||||
// Delay
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// ESP_LOGI(TAG, "set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
|
||||
// ESP_LOGI(TAG, "0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "reset fail");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
|
||||
{
|
||||
int ret = 0;
|
||||
//sc:H_start={0x0172[3:0],0x0170},H_end={0x0172[7:4],0x0171},
|
||||
WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x0f) | (((offset_x+w)>>4)&0xf0));
|
||||
|
||||
//sc:V_start={0x0175[3:0],0x0173},H_end={0x0175[7:4],0x0174},
|
||||
WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x0f) | (((offset_y+h)>>4)&0xf0));
|
||||
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
if(w>SC101_MAX_FRAME_WIDTH || h > SC101_MAX_FRAME_HIGH) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
uint16_t offset_x = (SC101_MAX_FRAME_WIDTH-w) /2;
|
||||
uint16_t offset_y = (SC101_MAX_FRAME_HIGH-h) /2;
|
||||
|
||||
if(set_window(sensor, offset_x, offset_y, w, h)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
return 0;
|
||||
err:
|
||||
ESP_LOGE(TAG, "frame size err");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret=0;
|
||||
sensor->pixformat = pixformat;
|
||||
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_RGB565:
|
||||
case PIXFORMAT_RAW:
|
||||
case PIXFORMAT_GRAYSCALE:
|
||||
ESP_LOGE(TAG, "Not support");
|
||||
break;
|
||||
case PIXFORMAT_YUV422: // For now, sc101 sensor only support YUV422.
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val){ return -1; }
|
||||
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->xclk_freq_hz = xclk * 1000000U;
|
||||
ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int 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);
|
||||
uint8_t MIDH = SCCB_Read(slv_addr, SC101_SENSOR_ID_HIGH_REG);
|
||||
uint16_t PID = MIDH << 8 | MIDL;
|
||||
if (SC101IOT_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc101iot_init(sensor_t *sensor)
|
||||
{
|
||||
// Set function pointers
|
||||
sensor->reset = reset;
|
||||
sensor->init_status = init_status;
|
||||
sensor->set_pixformat = set_pixformat;
|
||||
sensor->set_framesize = set_framesize;
|
||||
sensor->set_hmirror = set_hmirror;
|
||||
sensor->set_vflip = set_vflip;
|
||||
sensor->set_colorbar = set_colorbar;
|
||||
sensor->set_raw_gma = set_raw_gma;
|
||||
sensor->set_sharpness = set_sharpness;
|
||||
sensor->set_agc_gain = set_agc_gain;
|
||||
sensor->set_aec_value = set_aec_value;
|
||||
sensor->set_awb_gain = set_awb_gain;
|
||||
sensor->set_saturation= set_saturation;
|
||||
sensor->set_contrast = set_contrast;
|
||||
|
||||
sensor->set_denoise = set_dummy;
|
||||
sensor->set_quality = set_dummy;
|
||||
sensor->set_special_effect = set_dummy;
|
||||
sensor->set_wb_mode = set_dummy;
|
||||
sensor->set_ae_level = set_dummy;
|
||||
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = set_reg;
|
||||
sensor->set_xclk = set_xclk;
|
||||
|
||||
ESP_LOGD(TAG, "sc101iot Attached");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -34,10 +34,14 @@ static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
|
||||
#include "xclk.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR >= 3)
|
||||
#include "esp_rom_gpio.h"
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
|
||||
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "esp32 ll_cam";
|
||||
@@ -233,7 +237,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
//DBG_PIN_SET(0);
|
||||
}
|
||||
|
||||
bool ll_cam_stop(cam_obj_t *cam)
|
||||
bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
|
||||
{
|
||||
I2S0.conf.rx_start = 0;
|
||||
I2S_ISR_DISABLE(in_suc_eof);
|
||||
@@ -443,8 +447,11 @@ static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
|
||||
// Calculate DMA size
|
||||
dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
|
||||
|
||||
ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u, dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u, image_size: %u",
|
||||
node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node, dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item, image_size);
|
||||
ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u, dma_half_buffer_min: %5u, dma_half_buffer: %5u,"
|
||||
"lines_per_half_buffer: %2u, dma_buffer_size: %5u, image_size: %u",
|
||||
(unsigned) (node_size * cam->dma_bytes_per_item), (unsigned) nodes_per_line, (unsigned) lines_per_node,
|
||||
(unsigned) (dma_half_buffer_min * cam->dma_bytes_per_item), (unsigned) (dma_half_buffer * cam->dma_bytes_per_item),
|
||||
(unsigned) (lines_per_half_buffer), (unsigned) (dma_buffer_size * cam->dma_bytes_per_item), (unsigned) image_size);
|
||||
|
||||
cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
|
||||
cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
|
||||
|
||||
@@ -21,10 +21,15 @@
|
||||
#include "xclk.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR >= 3)
|
||||
#include "esp_rom_gpio.h"
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
|
||||
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "s2 ll_cam";
|
||||
@@ -70,7 +75,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
bool ll_cam_stop(cam_obj_t *cam)
|
||||
bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
|
||||
{
|
||||
I2S0.conf.rx_start = 0;
|
||||
|
||||
@@ -300,7 +305,7 @@ static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u",
|
||||
node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node);
|
||||
(unsigned) (node_size * cam->dma_bytes_per_item), nodes_per_line, lines_per_node);
|
||||
|
||||
cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
|
||||
|
||||
@@ -334,7 +339,8 @@ static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
|
||||
dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
|
||||
|
||||
ESP_LOGI(TAG, "dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u",
|
||||
dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item);
|
||||
(unsigned) (dma_half_buffer_min * cam->dma_bytes_per_item), (unsigned) (dma_half_buffer * cam->dma_bytes_per_item),
|
||||
(unsigned) lines_per_half_buffer, (unsigned) (dma_buffer_size * cam->dma_bytes_per_item));
|
||||
|
||||
cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
|
||||
cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
|
||||
|
||||
@@ -22,10 +22,15 @@
|
||||
#include "soc/gdma_reg.h"
|
||||
#include "ll_cam.h"
|
||||
#include "cam_hal.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#define gpio_matrix_out(a,b,c,d) gpio_iomux_out(a,b,c)
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/gpio_periph.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
|
||||
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "s3 ll_cam";
|
||||
@@ -74,7 +79,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
bool ll_cam_stop(cam_obj_t *cam)
|
||||
bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
|
||||
{
|
||||
if (cam->jpeg_mode || !cam->psram_mode) {
|
||||
GDMA.channel[cam->dma_num].in.int_ena.in_suc_eof = 0;
|
||||
@@ -170,6 +175,7 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
|
||||
}
|
||||
|
||||
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
|
||||
@@ -178,8 +184,57 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
static esp_err_t ll_cam_converter_config(cam_obj_t *cam, const camera_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
switch (config->conv_mode) {
|
||||
case YUV422_TO_YUV420:
|
||||
if (config->pixel_format != PIXFORMAT_YUV422) {
|
||||
ret = ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "YUV422 to YUV420 mode");
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 1;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 1;
|
||||
}
|
||||
break;
|
||||
case YUV422_TO_RGB565:
|
||||
if (config->pixel_format != PIXFORMAT_YUV422) {
|
||||
ret = ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "YUV422 to RGB565 mode");
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#if CONFIG_LCD_CAM_CONV_BT709_ENABLED
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 1;
|
||||
#else
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 0;
|
||||
#endif
|
||||
#if CONFIG_LCD_CAM_CONV_FULL_RANGE_ENABLED
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_data_out_mode = 1;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_data_in_mode = 1;
|
||||
#else
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_data_out_mode = 0;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_data_in_mode = 0;
|
||||
#endif
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_mode_8bits_on = 1;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_bypass = 1;
|
||||
cam->conv_mode = config->conv_mode;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
@@ -215,15 +270,21 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
|
||||
|
||||
LCD_CAM.cam_rgb_yuv.val = 0;
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
if (config->conv_mode) {
|
||||
ret = ll_cam_converter_config(cam, config);
|
||||
if(ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LCD_CAM.cam_ctrl.cam_update = 1;
|
||||
LCD_CAM.cam_ctrl1.cam_start = 1;
|
||||
|
||||
esp_err_t err = ll_cam_dma_init(cam);
|
||||
if(err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
ret = ll_cam_dma_init(cam);
|
||||
|
||||
return ESP_OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
|
||||
@@ -262,11 +323,12 @@ esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config)
|
||||
gpio_set_pull_mode(data_pins[i], GPIO_FLOATING);
|
||||
gpio_matrix_in(data_pins[i], CAM_DATA_IN0_IDX + i, false);
|
||||
}
|
||||
|
||||
if (config->pin_xclk >= 0) {
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_xclk], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_xclk, GPIO_MODE_OUTPUT);
|
||||
gpio_set_pull_mode(config->pin_xclk, GPIO_FLOATING);
|
||||
gpio_matrix_out(config->pin_xclk, CAM_CLK_IDX, false, false);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -339,7 +401,7 @@ static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u",
|
||||
node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node);
|
||||
(unsigned) (node_size * cam->dma_bytes_per_item), (unsigned) nodes_per_line, (unsigned) lines_per_node);
|
||||
|
||||
cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
|
||||
|
||||
@@ -373,7 +435,8 @@ static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u",
|
||||
dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item);
|
||||
(unsigned) (dma_half_buffer_min * cam->dma_bytes_per_item), (unsigned) (dma_half_buffer * cam->dma_bytes_per_item),
|
||||
(unsigned) lines_per_half_buffer, (unsigned) (dma_buffer_size * cam->dma_bytes_per_item));
|
||||
|
||||
cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
|
||||
cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
|
||||
@@ -433,8 +496,22 @@ esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_
|
||||
}
|
||||
cam->fb_bytes_per_pixel = 1; // frame buffer stores Y8
|
||||
} else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
|
||||
cam->in_bytes_per_pixel = 2; // camera sends YU/YV
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
switch (cam->conv_mode) {
|
||||
case YUV422_TO_YUV420:
|
||||
cam->in_bytes_per_pixel = 1.5; // for DMA receive
|
||||
cam->fb_bytes_per_pixel = 1.5; // frame buffer stores YUV420
|
||||
break;
|
||||
case YUV422_TO_RGB565:
|
||||
default:
|
||||
cam->in_bytes_per_pixel = 2; // for DMA receive
|
||||
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
|
||||
break;
|
||||
}
|
||||
#else
|
||||
cam->in_bytes_per_pixel = 2; // for DMA receive
|
||||
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
|
||||
#endif
|
||||
} else if (pix_format == PIXFORMAT_JPEG) {
|
||||
cam->in_bytes_per_pixel = 1;
|
||||
cam->fb_bytes_per_pixel = 1;
|
||||
|
||||
@@ -116,8 +116,14 @@ typedef struct {
|
||||
//for RGB/YUV modes
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
float in_bytes_per_pixel;
|
||||
float fb_bytes_per_pixel;
|
||||
camera_conv_mode_t conv_mode;
|
||||
#else
|
||||
uint8_t in_bytes_per_pixel;
|
||||
uint8_t fb_bytes_per_pixel;
|
||||
#endif
|
||||
uint32_t fb_size;
|
||||
|
||||
cam_state_t state;
|
||||
@@ -134,7 +140,7 @@ esp_err_t ll_cam_init_isr(cam_obj_t *cam);
|
||||
void ll_cam_do_vsync(cam_obj_t *cam);
|
||||
uint8_t ll_cam_get_dma_align(cam_obj_t *cam);
|
||||
bool ll_cam_dma_sizes(cam_obj_t *cam);
|
||||
size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len);
|
||||
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);
|
||||
|
||||
// implemented in cam_hal
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "unity.h"
|
||||
#include <mbedtls/base64.h>
|
||||
#include "esp_log.h"
|
||||
#include "driver/i2c.h"
|
||||
|
||||
#include "esp_camera.h"
|
||||
|
||||
@@ -105,11 +106,16 @@
|
||||
|
||||
#endif
|
||||
|
||||
#define I2C_MASTER_SCL_IO 4 /*!< GPIO number used for I2C master clock */
|
||||
#define I2C_MASTER_SDA_IO 5 /*!< GPIO number used for I2C master data */
|
||||
#define I2C_MASTER_NUM 0 /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
|
||||
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
|
||||
|
||||
static const char *TAG = "test camera";
|
||||
|
||||
typedef void (*decode_func_t)(uint8_t *jpegbuffer, uint32_t size, uint8_t *outbuffer);
|
||||
|
||||
static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, framesize_t frame_size, uint8_t fb_count)
|
||||
static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, framesize_t frame_size, uint8_t fb_count, int sccb_sda_gpio_num, int sccb_port)
|
||||
{
|
||||
framesize_t size_bak = frame_size;
|
||||
if (PIXFORMAT_JPEG == pixel_format && FRAMESIZE_SVGA > frame_size) {
|
||||
@@ -119,8 +125,9 @@ static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, fr
|
||||
.pin_pwdn = PWDN_GPIO_NUM,
|
||||
.pin_reset = RESET_GPIO_NUM,
|
||||
.pin_xclk = XCLK_GPIO_NUM,
|
||||
.pin_sscb_sda = SIOD_GPIO_NUM,
|
||||
.pin_sscb_scl = SIOC_GPIO_NUM,
|
||||
.pin_sccb_sda = sccb_sda_gpio_num, // If pin_sccb_sda is -1, sccb will use the already initialized i2c port specified by `sccb_i2c_port`.
|
||||
.pin_sccb_scl = SIOC_GPIO_NUM,
|
||||
.sccb_i2c_port = sccb_port,
|
||||
|
||||
.pin_d7 = Y9_GPIO_NUM,
|
||||
.pin_d6 = Y8_GPIO_NUM,
|
||||
@@ -226,7 +233,7 @@ static void camera_performance_test(uint32_t xclk_freq, uint32_t pic_num)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
//detect sensor information
|
||||
TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
|
||||
TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2, SIOD_GPIO_NUM, -1));
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
camera_sensor_info_t *info = esp_camera_sensor_get_info(&s->id);
|
||||
TEST_ASSERT_NOT_NULL(info);
|
||||
@@ -249,7 +256,7 @@ static void camera_performance_test(uint32_t xclk_freq, uint32_t pic_num)
|
||||
for (; format_s <= format_e; format_s++) {
|
||||
for (size_t i = 0; i <= max_size; i++) {
|
||||
ESP_LOGI(TAG, "\n\n===> Testing format:%s resolution: %d x %d <===", get_cam_format_name(*format_s), resolution[i].width, resolution[i].height);
|
||||
ret = init_camera(xclk_freq, *format_s, i, 2);
|
||||
ret = init_camera(xclk_freq, *format_s, i, 2, SIOD_GPIO_NUM, -1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
if (ESP_OK != ret) {
|
||||
ESP_LOGW(TAG, "Testing init failed :-(, skip this item");
|
||||
@@ -276,7 +283,7 @@ static void camera_performance_test(uint32_t xclk_freq, uint32_t pic_num)
|
||||
TEST_CASE("Camera driver init, deinit test", "[camera]")
|
||||
{
|
||||
uint64_t t1 = esp_timer_get_time();
|
||||
TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
|
||||
TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2, SIOD_GPIO_NUM, -1));
|
||||
uint64_t t2 = esp_timer_get_time();
|
||||
ESP_LOGI(TAG, "Camera init time %llu ms", (t2 - t1) / 1000);
|
||||
|
||||
@@ -285,7 +292,7 @@ TEST_CASE("Camera driver init, deinit test", "[camera]")
|
||||
|
||||
TEST_CASE("Camera driver take RGB565 picture test", "[camera]")
|
||||
{
|
||||
TEST_ESP_OK(init_camera(10000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
|
||||
TEST_ESP_OK(init_camera(10000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2, SIOD_GPIO_NUM, -1));
|
||||
vTaskDelay(500 / portTICK_RATE_MS);
|
||||
ESP_LOGI(TAG, "Taking picture...");
|
||||
camera_fb_t *pic = esp_camera_fb_get();
|
||||
@@ -301,7 +308,7 @@ TEST_CASE("Camera driver take RGB565 picture test", "[camera]")
|
||||
|
||||
TEST_CASE("Camera driver take YUV422 picture test", "[camera]")
|
||||
{
|
||||
TEST_ESP_OK(init_camera(10000000, PIXFORMAT_YUV422, FRAMESIZE_QVGA, 2));
|
||||
TEST_ESP_OK(init_camera(10000000, PIXFORMAT_YUV422, FRAMESIZE_QVGA, 2, SIOD_GPIO_NUM, -1));
|
||||
vTaskDelay(500 / portTICK_RATE_MS);
|
||||
ESP_LOGI(TAG, "Taking picture...");
|
||||
camera_fb_t *pic = esp_camera_fb_get();
|
||||
@@ -317,7 +324,7 @@ TEST_CASE("Camera driver take YUV422 picture test", "[camera]")
|
||||
|
||||
TEST_CASE("Camera driver take JPEG picture test", "[camera]")
|
||||
{
|
||||
TEST_ESP_OK(init_camera(20000000, PIXFORMAT_JPEG, FRAMESIZE_QVGA, 2));
|
||||
TEST_ESP_OK(init_camera(20000000, PIXFORMAT_JPEG, FRAMESIZE_QVGA, 2, SIOD_GPIO_NUM, -1));
|
||||
vTaskDelay(500 / portTICK_RATE_MS);
|
||||
ESP_LOGI(TAG, "Taking picture...");
|
||||
camera_fb_t *pic = esp_camera_fb_get();
|
||||
@@ -484,6 +491,25 @@ static void img_jpeg_decode_test(uint16_t pic_index, uint16_t lib_index)
|
||||
jpg_decode_test(lib_index, DECODE_RGB565, imgs[pic_index].buf, imgs[pic_index].length, imgs[pic_index].w, imgs[pic_index].h, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief i2c master initialization
|
||||
*/
|
||||
static esp_err_t i2c_master_init(int i2c_port)
|
||||
{
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
|
||||
i2c_param_config(i2c_port, &conf);
|
||||
|
||||
return i2c_driver_install(i2c_port, conf.mode, 0, 0, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Conversions image 227x149 jpeg decode test", "[camera]")
|
||||
{
|
||||
img_jpeg_decode_test(0, 0);
|
||||
@@ -498,3 +524,12 @@ TEST_CASE("Conversions image 480x320 jpeg decode test", "[camera]")
|
||||
{
|
||||
img_jpeg_decode_test(2, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Camera driver uses an i2c port initialized by other devices test", "[camera]")
|
||||
{
|
||||
TEST_ESP_OK(i2c_master_init(I2C_MASTER_NUM));
|
||||
TEST_ESP_OK(init_camera(20000000, PIXFORMAT_JPEG, FRAMESIZE_QVGA, 2, -1, I2C_MASTER_NUM));
|
||||
vTaskDelay(500 / portTICK_RATE_MS);
|
||||
TEST_ESP_OK(esp_camera_deinit());
|
||||
TEST_ESP_OK(i2c_driver_delete(I2C_MASTER_NUM));
|
||||
}
|
||||
|
||||
BIN
code/components/esp32-camera-master_20220924.zip
Normal file
BIN
code/components/esp32-camera-master_20220924.zip
Normal file
Binary file not shown.
@@ -51,18 +51,28 @@ set(lib_srcs
|
||||
"${tflite_dir}/kernels/internal/quantization_util.cc"
|
||||
"${tflite_dir}/schema/schema_utils.cc")
|
||||
|
||||
set(priv_req esp-nn)
|
||||
|
||||
# include component requirements which were introduced after IDF version 4.1
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.1")
|
||||
list(APPEND priv_req esp_timer driver)
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
SRCS "${lib_srcs}"
|
||||
INCLUDE_DIRS "." "third_party/gemmlowp"
|
||||
"third_party/flatbuffers/include"
|
||||
"third_party/ruy"
|
||||
"third_party/kissfft"
|
||||
REQUIRES "esp-nn")
|
||||
REQUIRES ${pub_req}
|
||||
PRIV_REQUIRES ${priv_req})
|
||||
|
||||
# Reduce the level of paranoia to be able to compile TF sources
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE
|
||||
-Wno-maybe-uninitialized
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-error=sign-compare
|
||||
-Wno-error=double-promotion
|
||||
-DESP_NN # enables ESP-NN optimizations by Espressif
|
||||
-Wno-type-limits)
|
||||
|
||||
|
||||
@@ -185,6 +185,7 @@ typedef enum {
|
||||
kTfLiteBuiltinUnsortedSegmentSum = 155,
|
||||
kTfLiteBuiltinAtan2 = 156,
|
||||
kTfLiteBuiltinUnsortedSegmentMin = 157,
|
||||
kTfLiteBuiltinSign = 158,
|
||||
} TfLiteBuiltinOperator;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -283,4 +283,25 @@ const char* TfLiteTypeGetName(TfLiteType type) {
|
||||
|
||||
TfLiteDelegate TfLiteDelegateCreate() { return TfLiteDelegate{}; }
|
||||
|
||||
struct TfLiteOpaqueDelegateStruct* TfLiteOpaqueDelegateCreate(
|
||||
const TfLiteOpaqueDelegateBuilder* opaque_delegate_builder) {
|
||||
if (!opaque_delegate_builder) return nullptr;
|
||||
|
||||
TfLiteDelegate* result = new TfLiteDelegate{};
|
||||
result->opaque_delegate_builder = new TfLiteOpaqueDelegateBuilder{};
|
||||
*(result->opaque_delegate_builder) = *opaque_delegate_builder;
|
||||
|
||||
return reinterpret_cast<struct TfLiteOpaqueDelegateStruct*>(result);
|
||||
}
|
||||
|
||||
void TfLiteOpaqueDelegateDelete(
|
||||
const struct TfLiteOpaqueDelegateStruct* opaque_delegate) {
|
||||
if (!opaque_delegate) return;
|
||||
|
||||
const TfLiteDelegate* tflite_delegate =
|
||||
reinterpret_cast<const TfLiteDelegate*>(opaque_delegate);
|
||||
delete tflite_delegate->opaque_delegate_builder;
|
||||
delete tflite_delegate;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -63,6 +63,8 @@ typedef enum TfLiteExternalContextType {
|
||||
struct TfLiteContext;
|
||||
struct TfLiteDelegate;
|
||||
struct TfLiteRegistration;
|
||||
struct TfLiteOpaqueDelegateStruct;
|
||||
struct TfLiteOpaqueDelegateBuilder;
|
||||
|
||||
// An external context is a collection of information unrelated to the TF Lite
|
||||
// framework, but useful to a subset of the ops. TF Lite knows very little
|
||||
@@ -973,7 +975,7 @@ typedef enum TfLiteDelegateFlags {
|
||||
typedef struct TfLiteDelegate {
|
||||
// Data that delegate needs to identify itself. This data is owned by the
|
||||
// delegate. The delegate is owned in the user code, so the delegate is
|
||||
// responsible for doing this when it is destroyed.
|
||||
// responsible for deallocating this when it is destroyed.
|
||||
void* data_;
|
||||
|
||||
// Invoked by ModifyGraphWithDelegate. This prepare is called, giving the
|
||||
@@ -1010,12 +1012,83 @@ typedef struct TfLiteDelegate {
|
||||
|
||||
// Bitmask flags. See the comments in `TfLiteDelegateFlags`.
|
||||
int64_t flags;
|
||||
|
||||
// The opaque delegate builder associated with this object. If set then the
|
||||
// TF Lite runtime will give precedence to this field. E.g. instead of
|
||||
// invoking 'Prepare' via the function pointer inside the 'TfLiteDelegate'
|
||||
// object, the runtime will first check if the corresponding function
|
||||
// pointer inside 'opaque_delegate_builder' is set and if so invoke that.
|
||||
//
|
||||
// If this field is non-null, then the 'Prepare' field (of the
|
||||
// 'TfLiteDelegate') should be null.
|
||||
struct TfLiteOpaqueDelegateBuilder* opaque_delegate_builder;
|
||||
} TfLiteDelegate;
|
||||
|
||||
// Build a 'null' delegate, with all the fields properly set to their default
|
||||
// values.
|
||||
TfLiteDelegate TfLiteDelegateCreate(void);
|
||||
|
||||
// `TfLiteOpaqueDelegateBuilder` is used for constructing
|
||||
// `TfLiteOpaqueDelegateStruct`, see `TfLiteOpaqueDelegateCreate` below. Note:
|
||||
// This struct is not ABI stable.
|
||||
//
|
||||
// For forward source compatibility `TfLiteOpaqueDelegateBuilder` objects should
|
||||
// be brace-initialized, so that all fields (including any that might be added
|
||||
// in the future) get zero-initialized. The purpose of each field is exactly
|
||||
// the same as with `TfLiteDelegate`.
|
||||
//
|
||||
// WARNING: This is an experimental interface that is subject to change.
|
||||
typedef struct TfLiteOpaqueDelegateBuilder {
|
||||
// Data that delegate needs to identify itself. This data is owned by the
|
||||
// delegate. The delegate is owned in the user code, so the delegate is
|
||||
// responsible for deallocating this when it is destroyed.
|
||||
void* data;
|
||||
// Invoked by ModifyGraphWithDelegate. This prepare is called, giving the
|
||||
// delegate a view of the current graph through TfLiteContext*. It typically
|
||||
// will look at the nodes and call ReplaceNodeSubsetsWithDelegateKernels()
|
||||
// to ask the TensorFlow lite runtime to create macro-nodes to represent
|
||||
// delegated subgraphs of the original graph.
|
||||
TfLiteStatus (*Prepare)(TfLiteOpaqueContext* context, // NOLINT
|
||||
struct TfLiteOpaqueDelegateStruct* delegate,
|
||||
void* data);
|
||||
// Copies the data from delegate buffer handle into raw memory of the given
|
||||
// 'tensor'. Note that the delegate is allowed to allocate the raw bytes as
|
||||
// long as it follows the rules for kTfLiteDynamic tensors, in which case this
|
||||
// cannot be null.
|
||||
TfLiteStatus (*CopyFromBufferHandle)( // NOLINT
|
||||
TfLiteOpaqueContext* context, struct TfLiteOpaqueDelegateStruct* delegate,
|
||||
void* data, TfLiteBufferHandle buffer_handle, TfLiteOpaqueTensor* tensor);
|
||||
// Copies the data from raw memory of the given 'tensor' to delegate buffer
|
||||
// handle. This can be null if the delegate doesn't use its own buffer.
|
||||
TfLiteStatus (*CopyToBufferHandle)( // NOLINT
|
||||
TfLiteOpaqueContext* context, struct TfLiteOpaqueDelegateStruct* delegate,
|
||||
void* data, TfLiteBufferHandle buffer_handle, TfLiteOpaqueTensor* tensor);
|
||||
// Frees the Delegate Buffer Handle. Note: This only frees the handle, but
|
||||
// this doesn't release the underlying resource (e.g. textures). The
|
||||
// resources are either owned by application layer or the delegate.
|
||||
// This can be null if the delegate doesn't use its own buffer.
|
||||
void (*FreeBufferHandle)(TfLiteOpaqueContext* context, // NOLINT
|
||||
struct TfLiteOpaqueDelegateStruct* delegate,
|
||||
void* data, TfLiteBufferHandle* handle);
|
||||
// Bitmask flags. See the comments in `TfLiteDelegateFlags`.
|
||||
int64_t flags;
|
||||
} TfLiteOpaqueDelegateBuilder;
|
||||
|
||||
// Creates an opaque delegate and returns its address. The opaque delegate will
|
||||
// behave according to the provided 'opaque_delegate_builder'. The lifetime of
|
||||
// the fields within the 'opaque_delegate_builder' must outlive any interaction
|
||||
// between the runtime and the returned 'TfLiteOpaqueDelegateStruct'. The
|
||||
// returned address should be passed to 'TfLiteOpaqueDelegateDelete' for
|
||||
// deletion. If 'opaque_delegate_builder' is a null pointer, then a null
|
||||
// pointer will be returned.
|
||||
struct TfLiteOpaqueDelegateStruct* TfLiteOpaqueDelegateCreate(
|
||||
const TfLiteOpaqueDelegateBuilder* opaque_delegate_builder);
|
||||
|
||||
// Deletes the provided opaque 'delegate'. This function has no effect if the
|
||||
// 'delegate' is a null pointer.
|
||||
void TfLiteOpaqueDelegateDelete(
|
||||
const struct TfLiteOpaqueDelegateStruct* delegate);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
@@ -12,8 +12,9 @@ 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.
|
||||
==============================================================================*/
|
||||
// This provides a few C++ helpers that are useful for manipulating C structures
|
||||
// in C++.
|
||||
/// \file
|
||||
/// This provides a few C++ helpers that are useful for manipulating C
|
||||
/// structures in C++.
|
||||
#ifndef TENSORFLOW_LITE_CONTEXT_UTIL_H_
|
||||
#define TENSORFLOW_LITE_CONTEXT_UTIL_H_
|
||||
|
||||
@@ -23,13 +24,14 @@ limitations under the License.
|
||||
|
||||
namespace tflite {
|
||||
|
||||
// Provide a range iterable wrapper for TfLiteIntArray* (C lists that TfLite
|
||||
// C api uses. Can't use the google array_view, since we can't depend on even
|
||||
/// Provides a range iterable wrapper for TfLiteIntArray* (C lists) that TfLite
|
||||
/// C api uses.
|
||||
// Can't use the google array_view, since we can't depend on even
|
||||
// absl for embedded device reasons.
|
||||
class TfLiteIntArrayView {
|
||||
public:
|
||||
// Construct a view of a TfLiteIntArray*. Note, `int_array` should be non-null
|
||||
// and this view does not take ownership of it.
|
||||
/// Construct a view of a TfLiteIntArray*. Note, `int_array` should be
|
||||
/// non-null and this view does not take ownership of it.
|
||||
explicit TfLiteIntArrayView(const TfLiteIntArray* int_array)
|
||||
: int_array_(int_array) {}
|
||||
|
||||
|
||||
@@ -457,6 +457,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
return ParseRsqrt(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_SELECT_V2: {
|
||||
return ParseSelectV2(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_SHAPE: {
|
||||
return ParseShape(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
@@ -865,7 +869,6 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
case BuiltinOperator_RELU_0_TO_1:
|
||||
case BuiltinOperator_SCATTER_ND:
|
||||
case BuiltinOperator_SELECT:
|
||||
case BuiltinOperator_SELECT_V2:
|
||||
case BuiltinOperator_SLICE:
|
||||
case BuiltinOperator_TILE:
|
||||
case BuiltinOperator_TOPK_V2:
|
||||
@@ -881,6 +884,7 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
case BuiltinOperator_UNSORTED_SEGMENT_PROD:
|
||||
case BuiltinOperator_UNSORTED_SEGMENT_SUM:
|
||||
case BuiltinOperator_ATAN2:
|
||||
case BuiltinOperator_SIGN:
|
||||
case BuiltinOperator_WHERE:
|
||||
return kTfLiteOk;
|
||||
case BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES:
|
||||
@@ -1982,6 +1986,14 @@ TfLiteStatus ParseRsqrt(const Operator*, ErrorReporter*, BuiltinDataAllocator*,
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// We have this parse function instead of directly returning kTfLiteOk from the
|
||||
// switch-case in ParseOpData because this function is used as part of the
|
||||
// selective registration for the OpResolver implementation in micro.
|
||||
TfLiteStatus ParseSelectV2(const Operator*, ErrorReporter*,
|
||||
BuiltinDataAllocator*, void**) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ParseShape(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data) {
|
||||
SafeBuiltinDataAllocator safe_allocator(allocator);
|
||||
|
||||
@@ -319,6 +319,10 @@ TfLiteStatus ParseRound(const Operator* op, ErrorReporter* error_reporter,
|
||||
TfLiteStatus ParseRsqrt(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSelectV2(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseShape(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h"
|
||||
|
||||
#define FIXED_POINT 16
|
||||
|
||||
@@ -12,8 +12,8 @@ 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 TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ACTIVATIONS_H_
|
||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ACTIVATIONS_H_
|
||||
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_HARD_SWISH_H_
|
||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_HARD_SWISH_H_
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -165,4 +165,4 @@ inline void HardSwish(const HardSwishParams& params,
|
||||
} // namespace reference_ops
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CONV_H_
|
||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_HARD_SWISH_H_
|
||||
|
||||
@@ -16,6 +16,7 @@ limitations under the License.
|
||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_MUL_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <complex>
|
||||
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
|
||||
@@ -61,6 +62,20 @@ inline void Mul(const ArithmeticParams& params,
|
||||
}
|
||||
}
|
||||
|
||||
inline void Mul(const ArithmeticParams& params,
|
||||
const RuntimeShape& input1_shape,
|
||||
const std::complex<float>* input1_data,
|
||||
const RuntimeShape& input2_shape,
|
||||
const std::complex<float>* input2_data,
|
||||
const RuntimeShape& output_shape,
|
||||
std::complex<float>* output_data) {
|
||||
const int flat_size =
|
||||
MatchingExtendedShapeFlatSize(input1_shape, input2_shape, output_shape);
|
||||
for (int i = 0; i < flat_size; ++i) {
|
||||
output_data[i] = input1_data[i] * input2_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
inline void Mul(const ArithmeticParams& params,
|
||||
const RuntimeShape& input1_shape, const uint8_t* input1_data,
|
||||
const RuntimeShape& input2_shape, const uint8_t* input2_data,
|
||||
@@ -162,6 +177,37 @@ void BroadcastMul4DSlow(const ArithmeticParams& params,
|
||||
}
|
||||
}
|
||||
|
||||
inline void BroadcastMul4DSlow(const ArithmeticParams& params,
|
||||
const RuntimeShape& unextended_input1_shape,
|
||||
const std::complex<float>* input1_data,
|
||||
const RuntimeShape& unextended_input2_shape,
|
||||
const std::complex<float>* input2_data,
|
||||
const RuntimeShape& unextended_output_shape,
|
||||
std::complex<float>* output_data) {
|
||||
TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4);
|
||||
TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), 4);
|
||||
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
|
||||
const RuntimeShape output_shape =
|
||||
RuntimeShape::ExtendedShape(4, unextended_output_shape);
|
||||
|
||||
NdArrayDesc<4> desc1;
|
||||
NdArrayDesc<4> desc2;
|
||||
NdArrayDescsForElementwiseBroadcast(unextended_input1_shape,
|
||||
unextended_input2_shape, &desc1, &desc2);
|
||||
|
||||
for (int b = 0; b < output_shape.Dims(0); ++b) {
|
||||
for (int y = 0; y < output_shape.Dims(1); ++y) {
|
||||
for (int x = 0; x < output_shape.Dims(2); ++x) {
|
||||
for (int c = 0; c < output_shape.Dims(3); ++c) {
|
||||
output_data[Offset(output_shape, b, y, x, c)] =
|
||||
input1_data[SubscriptToIndex(desc1, b, y, x, c)] *
|
||||
input2_data[SubscriptToIndex(desc2, b, y, x, c)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace reference_ops
|
||||
} // namespace tflite
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_
|
||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "ruy/profiler/instrumentation.h" // from @ruy
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace reference_ops {
|
||||
|
||||
template <typename D, typename T>
|
||||
void Select(const RuntimeShape& input_condition_shape,
|
||||
const D* input_condition_data, const RuntimeShape& input_x_shape,
|
||||
const T* input_x_data, const RuntimeShape& input_y_shape,
|
||||
const T* input_y_data, const RuntimeShape& output_shape,
|
||||
T* output_data) {
|
||||
ruy::profiler::ScopeLabel label("Select");
|
||||
int64_t flatsize;
|
||||
// Allow select operator executions on mixed scalar tensors and one element
|
||||
// tensors.
|
||||
if (input_condition_shape.FlatSize() == 1 && input_x_shape.FlatSize() == 1 &&
|
||||
input_y_shape.FlatSize() == 1 && output_shape.FlatSize() == 1) {
|
||||
flatsize = 1;
|
||||
} else {
|
||||
flatsize = MatchingFlatSize(input_condition_shape, input_x_shape,
|
||||
input_y_shape, output_shape);
|
||||
}
|
||||
for (int64_t i = 0; i < flatsize; ++i) {
|
||||
output_data[i] =
|
||||
input_condition_data[i] ? input_x_data[i] : input_y_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename D, typename T>
|
||||
void RankOneSelect(const RuntimeShape& input_condition_shape,
|
||||
const D* input_condition_data,
|
||||
const RuntimeShape& input_x_shape, const T* input_x_data,
|
||||
const RuntimeShape& input_y_shape, const T* input_y_data,
|
||||
const RuntimeShape& output_shape, T* output_data) {
|
||||
ruy::profiler::ScopeLabel label("Select/RankOneSelect");
|
||||
const int64_t outer_size = input_condition_shape.FlatSize();
|
||||
int64_t inner_size;
|
||||
if (input_condition_shape.DimensionsCount() == 0) {
|
||||
inner_size = MatchingFlatSize(input_x_shape, input_y_shape, output_shape);
|
||||
} else {
|
||||
TFLITE_DCHECK_EQ(
|
||||
MatchingDim(input_x_shape, 0, input_y_shape, 0, output_shape, 0),
|
||||
outer_size);
|
||||
inner_size =
|
||||
MatchingFlatSizeSkipDim(input_x_shape, 0, input_y_shape, output_shape);
|
||||
}
|
||||
|
||||
int64_t offset = 0;
|
||||
for (int64_t i = 0; i < outer_size; i++) {
|
||||
const T* input_data = input_condition_data[i] ? input_x_data : input_y_data;
|
||||
memcpy(output_data + offset, input_data + offset, inner_size * sizeof(T));
|
||||
offset += inner_size;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename D, typename T>
|
||||
void BroadcastSelect5DSlow(const RuntimeShape& input_condition_shape,
|
||||
const D* input_condition_data,
|
||||
const RuntimeShape& input_x_shape,
|
||||
const T* input_x_data,
|
||||
const RuntimeShape& input_y_shape,
|
||||
const T* input_y_data,
|
||||
const RuntimeShape& output_shape, T* output_data) {
|
||||
ruy::profiler::ScopeLabel label("Select/BroadcastSelectSlow");
|
||||
TFLITE_DCHECK_LE(input_condition_shape.DimensionsCount(), 5);
|
||||
TFLITE_DCHECK_LE(input_x_shape.DimensionsCount(), 5);
|
||||
TFLITE_DCHECK_LE(input_y_shape.DimensionsCount(), 5);
|
||||
TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 5);
|
||||
|
||||
NdArrayDesc<5> desc_condition;
|
||||
NdArrayDesc<5> desc_x;
|
||||
NdArrayDesc<5> desc_y;
|
||||
NdArrayDesc<5> desc_output;
|
||||
const RuntimeShape extended_output_shape =
|
||||
RuntimeShape::ExtendedShape(5, output_shape);
|
||||
CopyDimsToDesc(extended_output_shape, &desc_output);
|
||||
NdArrayDescsForElementwiseBroadcast(input_condition_shape, input_x_shape,
|
||||
input_y_shape, &desc_condition, &desc_x,
|
||||
&desc_y);
|
||||
|
||||
// In Tensorflow, the dimensions are canonically named (batch_number, row,
|
||||
// col, channel), with extents (batches, height, width, depth), with the
|
||||
// trailing dimension changing most rapidly (channels has the smallest
|
||||
// stride, typically 1 element).
|
||||
//
|
||||
// In generated C code, we store arrays with the dimensions reversed. The
|
||||
// first dimension has smallest stride.
|
||||
//
|
||||
// We name our variables by their Tensorflow convention, but generate C code
|
||||
// nesting loops such that the innermost loop has the smallest stride for
|
||||
// the best cache behavior.
|
||||
for (int n = 0; n < desc_output.extents[0]; ++n) {
|
||||
int out_idx_n = desc_output.extents[1] * n;
|
||||
int cond_idx_n = desc_condition.strides[0] * n;
|
||||
int in_idx1_n = desc_x.strides[0] * n;
|
||||
int in_idx2_n = desc_y.strides[0] * n;
|
||||
for (int b = 0; b < desc_output.extents[1]; ++b) {
|
||||
int out_idx_b = (out_idx_n + b) * desc_output.extents[2];
|
||||
int cond_idx_b = cond_idx_n + desc_condition.strides[1] * b;
|
||||
int in_idx1_b = in_idx1_n + desc_x.strides[1] * b;
|
||||
int in_idx2_b = in_idx2_n + desc_y.strides[1] * b;
|
||||
for (int y = 0; y < desc_output.extents[2]; ++y) {
|
||||
int out_idx_y = (out_idx_b + y) * desc_output.extents[3];
|
||||
int cond_idx_y = cond_idx_b + desc_condition.strides[2] * y;
|
||||
int in_idx1_y = in_idx1_b + desc_x.strides[2] * y;
|
||||
int in_idx2_y = in_idx2_b + desc_y.strides[2] * y;
|
||||
for (int x = 0; x < desc_output.extents[3]; ++x) {
|
||||
int out_idx = (out_idx_y + x) * desc_output.extents[4];
|
||||
int cond_idx = cond_idx_y + desc_condition.strides[3] * x;
|
||||
int in_idx1 = in_idx1_y + desc_x.strides[3] * x;
|
||||
int in_idx2 = in_idx2_y + desc_y.strides[3] * x;
|
||||
for (int c = 0; c < desc_output.extents[4]; ++c) {
|
||||
output_data[out_idx] = input_condition_data[cond_idx]
|
||||
? input_x_data[in_idx1]
|
||||
: input_y_data[in_idx2];
|
||||
out_idx++;
|
||||
cond_idx += desc_condition.strides[4];
|
||||
in_idx1 += desc_x.strides[4];
|
||||
in_idx2 += desc_y.strides[4];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace reference_ops
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_
|
||||
@@ -92,6 +92,7 @@ AllOpsResolver::AllOpsResolver() {
|
||||
AddResizeNearestNeighbor();
|
||||
AddRound();
|
||||
AddRsqrt();
|
||||
AddSelectV2();
|
||||
AddShape();
|
||||
AddSin();
|
||||
AddSlice();
|
||||
@@ -102,6 +103,7 @@ AllOpsResolver::AllOpsResolver() {
|
||||
AddSplitV();
|
||||
AddSqrt();
|
||||
AddSquare();
|
||||
AddSquaredDifference();
|
||||
AddSqueeze();
|
||||
AddStridedSlice();
|
||||
AddSub();
|
||||
@@ -110,6 +112,7 @@ AllOpsResolver::AllOpsResolver() {
|
||||
AddTanh();
|
||||
AddTranspose();
|
||||
AddTransposeConv();
|
||||
AddUnidirectionalSequenceLSTM();
|
||||
AddUnpack();
|
||||
AddVarHandle();
|
||||
AddWhile();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
|
||||
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -59,6 +59,19 @@ TfLiteStatus CalculateOpDataAdd(TfLiteContext* context, TfLiteAddParams* params,
|
||||
|
||||
TfLiteStatus AddPrepare(TfLiteContext* context, TfLiteNode* node);
|
||||
|
||||
// Generic must define registration function.
|
||||
TfLiteRegistration Register_ADD();
|
||||
|
||||
#if defined(CMSIS_NN)
|
||||
TfLiteRegistration Register_ADD_INT8();
|
||||
|
||||
TfLiteRegistration Register_ADD_INT16();
|
||||
#else
|
||||
// Fallback registration
|
||||
inline TfLiteRegistration Register_ADD_INT8() { return Register_ADD(); }
|
||||
|
||||
inline TfLiteRegistration Register_ADD_INT16() { return Register_ADD(); }
|
||||
#endif
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_ADD_H_
|
||||
|
||||
@@ -121,7 +121,7 @@ TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node) {
|
||||
context, kTfLiteActNone, output, &data->output_activation_min,
|
||||
&data->output_activation_max));
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context, "ADD_N only supports FLOAT32 and INT8, got %s.",
|
||||
MicroPrintf("ADD_N only supports FLOAT32 and INT8, got %s.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -198,7 +198,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} else if (output->type == kTfLiteInt8) {
|
||||
EvalAddNQuantized<int8_t>(context, node, output);
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context, "ADD_N only supports FLOAT32 and INT8, got %s.",
|
||||
MicroPrintf("ADD_N only supports FLOAT32 and INT8, got %s.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -70,20 +70,19 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node, bool is_arg_max) {
|
||||
TF_LITE_ARG_MIN_MAX(int8_t, int32_t, int32_t);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
MicroPrintf(
|
||||
"Only float32, uint8_t and int8_t are "
|
||||
"supported currently, got %s.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"Only int32_t are supported currently, got %s.",
|
||||
MicroPrintf("Only int32_t are supported currently, got %s.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context, "Only int32_t are supported currently, got %s.",
|
||||
MicroPrintf("Only int32_t are supported currently, got %s.",
|
||||
TfLiteTypeGetName(axis->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
|
||||
@@ -90,7 +90,7 @@ TfLiteStatus CircularBufferEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
EvalInt8(tflite::micro::GetTensorData<int8_t>(input), num_slots, depth,
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ TfLiteStatus EqualEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -210,7 +210,7 @@ TfLiteStatus NotEqualEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -288,7 +288,7 @@ TfLiteStatus GreaterEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -366,7 +366,7 @@ TfLiteStatus GreaterEqualEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -444,7 +444,7 @@ TfLiteStatus LessEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -522,7 +522,7 @@ TfLiteStatus LessEqualEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE(context,
|
||||
input_type == kTfLiteFloat32 || input_type == kTfLiteInt8 ||
|
||||
input_type == kTfLiteInt16 || input_type == kTfLiteInt32 ||
|
||||
input_type == kTfLiteInt64);
|
||||
input_type == kTfLiteInt64 || input_type == kTfLiteBool);
|
||||
|
||||
// Output type must match input type
|
||||
TF_LITE_ENSURE_EQ(context, output_type, input_type);
|
||||
@@ -149,8 +149,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
int num_dimensions = NumDimensions(input);
|
||||
|
||||
if (num_dimensions > RuntimeShape::kMaxSmallSize) {
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context,
|
||||
MicroPrintf(
|
||||
"Op Concatenation does not currently support num dimensions > %d "
|
||||
"Tensor has %d dimensions.",
|
||||
RuntimeShape::kMaxSmallSize, num_dimensions);
|
||||
@@ -168,6 +167,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
switch (output_type) { // Already know in/outtypes are same.
|
||||
case kTfLiteBool:
|
||||
case kTfLiteFloat32:
|
||||
case kTfLiteInt16:
|
||||
case kTfLiteInt32:
|
||||
@@ -205,8 +205,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context, "Op Concatenation does not currently support Type '%s'.",
|
||||
MicroPrintf("Op Concatenation does not currently support Type '%s'.",
|
||||
TfLiteTypeGetName(output_type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -238,10 +237,12 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
case kTfLiteInt16:
|
||||
EvalUnquantized<int16_t>(context, node);
|
||||
break;
|
||||
case kTfLiteBool:
|
||||
EvalUnquantized<bool>(context, node);
|
||||
break;
|
||||
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context, "Op Concatenation does not currently support Type '%s'.",
|
||||
MicroPrintf("Op Concatenation does not currently support Type '%s'.",
|
||||
TfLiteTypeGetName(output_type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
if (axis < 0) axis += input_shape.DimensionsCount();
|
||||
|
||||
if (axis < 0 || axis >= input_shape.DimensionsCount()) {
|
||||
TF_LITE_KERNEL_LOG(context, "CUMSUM Invalid axis: %d", axis);
|
||||
MicroPrintf("CUMSUM Invalid axis: %d", axis);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -156,8 +156,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} break;
|
||||
|
||||
default: {
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"CUMSUM only supports FLOAT32 and INT8, got %s.",
|
||||
MicroPrintf("CUMSUM only supports FLOAT32 and INT8, got %s.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -124,8 +124,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context, "DEPTH_TO_SPACE only supports FLOAT32 and INT8, got %s.",
|
||||
MicroPrintf("DEPTH_TO_SPACE only supports FLOAT32 and INT8, got %s.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -82,8 +82,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
|
||||
@@ -162,8 +162,7 @@ TfLiteStatus EvalQuantized(TfLiteContext* context, TfLiteNode* node,
|
||||
}
|
||||
#undef TF_LITE_DIV
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context, "Unsupported combination of input and output types in DIV.");
|
||||
MicroPrintf("Unsupported combination of input and output types in DIV.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -189,7 +188,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE_OK(context, EvalQuantized(context, node, params, data,
|
||||
input1, input2, output));
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
MicroPrintf(
|
||||
"DIV only supports FLOAT32, quantized INT8 "
|
||||
"now, got type %s (%d).",
|
||||
TfLiteTypeGetName(output->type), output->type);
|
||||
|
||||
@@ -90,7 +90,7 @@ TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
if (!IsSupportedType(input->type)) {
|
||||
TF_LITE_KERNEL_LOG(context, "Input data type %s (%d) is not supported.",
|
||||
MicroPrintf("Input data type %s (%d) is not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ TfLiteStatus PrepareAbsRsqrt(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
if (!IsSupportedType(input->type)) {
|
||||
TF_LITE_KERNEL_LOG(context, "Input data type %s (%d) is not supported.",
|
||||
MicroPrintf("Input data type %s (%d) is not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -317,7 +317,7 @@ TfLiteStatus AbsEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
type);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Current data type %s is not supported.",
|
||||
MicroPrintf("Current data type %s is not supported.",
|
||||
TfLiteTypeGetName(type));
|
||||
return kTfLiteError;
|
||||
break;
|
||||
@@ -355,7 +355,7 @@ TfLiteStatus RsqrtEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
elementwise::validate_input_func, type);
|
||||
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Current data type %s is not supported.",
|
||||
MicroPrintf("Current data type %s is not supported.",
|
||||
TfLiteTypeGetName(type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ limitations under the License.
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
@@ -136,8 +135,7 @@ TfLiteStatus EluEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context, "ELU only supports float32 and int8 currently, got %s.",
|
||||
MicroPrintf("ELU only supports float32 and int8 currently, got %s.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ limitations under the License.
|
||||
#include "tensorflow/lite/kernels/padding.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include <esp_timer.h>
|
||||
|
||||
#if ESP_NN
|
||||
|
||||
@@ -27,7 +27,6 @@ limitations under the License.
|
||||
#include "tensorflow/lite/kernels/padding.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include <esp_timer.h>
|
||||
|
||||
#if ESP_NN
|
||||
|
||||
@@ -25,7 +25,6 @@ limitations under the License.
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include <esp_timer.h>
|
||||
|
||||
#if ESP_NN
|
||||
|
||||
@@ -19,6 +19,7 @@ limitations under the License.
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
@@ -63,7 +64,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
static_cast<size_t>(flat_size),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) currently not supported by Exp.",
|
||||
MicroPrintf("Type %s (%d) currently not supported by Exp.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -31,8 +31,7 @@ TfLiteStatus GetAxisValueFromTensor(TfLiteContext* context,
|
||||
int32_t* axis_value) {
|
||||
const int axis_dims = (tflite::GetTensorShape(axis)).DimensionsCount();
|
||||
if (axis_dims > 1) {
|
||||
TF_LITE_KERNEL_LOG(context, "Axis has only one element for Expand_Dims.",
|
||||
axis_dims);
|
||||
MicroPrintf("Axis has only one element for Expand_Dims.", axis_dims);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -41,8 +40,7 @@ TfLiteStatus GetAxisValueFromTensor(TfLiteContext* context,
|
||||
*axis_value = axis_ptr[0];
|
||||
return kTfLiteOk;
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"Axis type %s (%d) not supported by Expand_Dims.",
|
||||
MicroPrintf("Axis type %s (%d) not supported by Expand_Dims.",
|
||||
TfLiteTypeGetName(axis->type), axis->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -99,8 +97,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
output->type = input->type;
|
||||
if (IsDynamicTensor(axis)) {
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"DynamicTensor is not yet supported by Expand_Dims.");
|
||||
MicroPrintf("DynamicTensor is not yet supported by Expand_Dims.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
TF_LITE_ENSURE_OK(context, VerifyTensorDim(context, input, axis, output));
|
||||
@@ -135,8 +132,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetTensorData<int8_t>(input), flat_size);
|
||||
} break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context,
|
||||
MicroPrintf(
|
||||
"Expand_Dims only currently supports int8 and float32, got %d.",
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
|
||||
@@ -53,8 +53,7 @@ TfLiteStatus EnsureEq(TfLiteContext* context, const TfLiteIntArray* array,
|
||||
case kTfLiteInt64:
|
||||
return EnsureEqImpl<int64_t>(context, array, tensor);
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"cannot compare int array to tensor of type %d.",
|
||||
MicroPrintf("cannot compare int array to tensor of type %d.",
|
||||
tensor->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -123,8 +122,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
FillImpl<int8_t>(value, output);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context, "Fill only currently supports float32 for input 1, got %d.",
|
||||
MicroPrintf("Fill only currently supports float32 for input 1, got %d.",
|
||||
TfLiteTypeGetName(value->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ TfLiteStatus EvalFloorDiv(TfLiteContext* context,
|
||||
// Validate the denominator.
|
||||
for (int i = 0; i < tflite::ElementCount(*input2->dims); ++i) {
|
||||
if (std::equal_to<T>()(denominator_data[i], 0)) {
|
||||
TF_LITE_KERNEL_LOG(context, "Division by 0");
|
||||
MicroPrintf("Division by 0");
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return EvalFloorDiv<float>(context, input1, input2, output);
|
||||
}
|
||||
default: {
|
||||
TF_LITE_KERNEL_LOG(context, "Type '%s' is not supported by FLOOR_DIV.",
|
||||
MicroPrintf("Type '%s' is not supported by FLOOR_DIV.",
|
||||
TfLiteTypeGetName(input1->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
output);
|
||||
}
|
||||
default: {
|
||||
TF_LITE_KERNEL_LOG(context, "Type '%s' is not supported by FLOOR_MOD.",
|
||||
MicroPrintf("Type '%s' is not supported by FLOOR_MOD.",
|
||||
TfLiteTypeGetName(input1->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -141,8 +141,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
}
|
||||
|
||||
default: {
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,8 +118,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
case kTfLiteInt32:
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"Positions of type '%s' are not supported by gather.",
|
||||
MicroPrintf("Positions of type '%s' are not supported by gather.",
|
||||
TfLiteTypeGetName(coords->type));
|
||||
return kTfLiteError;
|
||||
break;
|
||||
@@ -134,7 +133,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
case kTfLiteInt8:
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type '%s' is not supported by gather.",
|
||||
MicroPrintf("Type '%s' is not supported by gather.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
break;
|
||||
@@ -207,7 +206,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return Gather<int8_t, int32_t>(params, input, coords, output);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type '%s' is not supported by gather.",
|
||||
MicroPrintf("Type '%s' is not supported by gather.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
break;
|
||||
|
||||
@@ -47,8 +47,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
case kTfLiteInt8:
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"Params of type '%s' are not supported by gather_nd.",
|
||||
MicroPrintf("Params of type '%s' are not supported by gather_nd.",
|
||||
TfLiteTypeGetName(params->type));
|
||||
return kTfLiteError;
|
||||
break;
|
||||
@@ -57,8 +56,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
case kTfLiteInt32:
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"Indices of type '%s' are not supported by gather_nd.",
|
||||
MicroPrintf("Indices of type '%s' are not supported by gather_nd.",
|
||||
TfLiteTypeGetName(indices->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -67,21 +65,19 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
const int indices_rank = NumDimensions(indices);
|
||||
const int indices_nd = SizeOfDimension(indices, indices_rank - 1);
|
||||
if (params_rank < 1) {
|
||||
TF_LITE_KERNEL_LOG(context, "Params must be at least a vector.");
|
||||
MicroPrintf("Params must be at least a vector.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
if (indices_rank < 1) {
|
||||
TF_LITE_KERNEL_LOG(context, "Indices must be at least a vector.");
|
||||
MicroPrintf("Indices must be at least a vector.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
if (indices_nd > params_rank) {
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context, "Index innermost dimension length must be <= params rank.");
|
||||
MicroPrintf("Index innermost dimension length must be <= params rank.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
if (indices_nd > MAX_INDICES_ND) {
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"Index innermost dimension length must not exceed %d.",
|
||||
MicroPrintf("Index innermost dimension length must not exceed %d.",
|
||||
MAX_INDICES_ND);
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -171,13 +167,12 @@ TfLiteStatus EvalGatherNd(TfLiteContext* context,
|
||||
status = GatherNd<int8_t, IndicesT>(params, indices, output);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"Params type '%s' are not supported by gather_nd.",
|
||||
MicroPrintf("Params type '%s' are not supported by gather_nd.",
|
||||
TfLiteTypeGetName(params->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
if (status != kTfLiteOk) {
|
||||
TF_LITE_KERNEL_LOG(context, "gather_nd index out of bounds");
|
||||
MicroPrintf("gather_nd index out of bounds");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -195,8 +190,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return EvalGatherNd<int32_t>(context, params, indices, output);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"Indices of type '%s' are not supported by gather_nd.",
|
||||
MicroPrintf("Indices of type '%s' are not supported by gather_nd.",
|
||||
TfLiteTypeGetName(indices->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -106,5 +106,17 @@ TfLiteStatus KernelRunner::Invoke() {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus KernelRunner::Free() {
|
||||
tflite::micro::ClearBufferApi(&context_);
|
||||
context_.GetScratchBuffer = MicroContextGetScratchBuffer;
|
||||
|
||||
if (registration_.free == nullptr) {
|
||||
MicroPrintf("TfLiteRegistration missing free function pointer!");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
registration_.free(&context_, node_.user_data);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
} // namespace micro
|
||||
} // namespace tflite
|
||||
@@ -48,6 +48,11 @@ class KernelRunner {
|
||||
// passed into the constructor of this class.
|
||||
TfLiteStatus Invoke();
|
||||
|
||||
// Calls Free on a given TfLiteRegistration pointer(if it's implemented).
|
||||
// After successful Free, kTfLiteOk status will be returned. If Free is not
|
||||
// implemented for a given kernel kTfLiteError will be returned.
|
||||
TfLiteStatus Free();
|
||||
|
||||
// Returns a pointer to the internal MockMicroGraph which KernelRunner uses
|
||||
// to stub out MicroGraph methods and track invocations on each subgraph.
|
||||
MockMicroGraph* GetMockGraph() { return &mock_micro_graph_; }
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/micro/memory_helpers.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace micro {
|
||||
@@ -39,9 +40,10 @@ int ValidateTensorIndexing(const TfLiteContext* context, int index,
|
||||
TfLiteRegistration RegisterOp(
|
||||
void* (*init)(TfLiteContext* context, const char* buffer, size_t length),
|
||||
TfLiteStatus (*prepare)(TfLiteContext* context, TfLiteNode* node),
|
||||
TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node)) {
|
||||
TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node),
|
||||
void (*free)(TfLiteContext* context, void* buffer)) {
|
||||
return {/*init=*/init,
|
||||
/*free=*/nullptr,
|
||||
/*free=*/free,
|
||||
/*prepare=*/prepare,
|
||||
/*invoke=*/invoke,
|
||||
/*profiling_string=*/nullptr,
|
||||
@@ -160,6 +162,46 @@ TfLiteStatus CopyOpInputsToOpOutputs(TfLiteContext* context, TfLiteNode* node) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Args:
|
||||
// 1. int8_t tensor_data - int8_t buffer of unknown size who's data you'd
|
||||
// like
|
||||
// to print
|
||||
// 2. int n_btyes - a small int representing number of bytes you want to
|
||||
// print
|
||||
// to debug output. It should always be <= tensor_data's size.
|
||||
// 3. prefix - optional message you'd like to print before printing bytes
|
||||
//
|
||||
// Purpose:
|
||||
// Function takes in paramaters above and prints n_bytes bytes from the
|
||||
// tensor_data buffer. This can be use to debug the output of a model and it's
|
||||
// op.
|
||||
|
||||
void PrintNBytes(const int8_t* tensor_data, int n_bytes, const char* prefix) {
|
||||
if (prefix != nullptr) {
|
||||
MicroPrintf("%s", prefix);
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_bytes; ++i) {
|
||||
MicroPrintf(" %x", tensor_data[i]);
|
||||
}
|
||||
MicroPrintf("\n");
|
||||
}
|
||||
|
||||
// same as the PrintNBytes above but the buffer needs to be extracted out of the
|
||||
// TfLiteEvalTensor*
|
||||
void PrintNBytes(const TfLiteEvalTensor* tensor, int n_bytes,
|
||||
const char* prefix) {
|
||||
const int8_t* tensor_data = tflite::micro::GetTensorData<int8_t>(tensor);
|
||||
PrintNBytes(tensor_data, n_bytes, prefix);
|
||||
}
|
||||
|
||||
// same as the PrintNBytes above but the buffer needs to be extracted out of the
|
||||
// TfLiteEvalTensor*
|
||||
void PrintNBytes(const TfLiteTensor* tensor, int n_bytes, const char* prefix) {
|
||||
const int8_t* tensor_data = tflite::GetTensorData<int8_t>(tensor);
|
||||
PrintNBytes(tensor_data, n_bytes, prefix);
|
||||
}
|
||||
|
||||
TfLiteStatus CopyOpInputsToSubgraphInputs(TfLiteContext* context,
|
||||
TfLiteNode* node,
|
||||
MicroGraph* graph_info,
|
||||
|
||||
@@ -21,8 +21,10 @@ limitations under the License.
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/micro/micro_context.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace micro {
|
||||
@@ -30,7 +32,20 @@ namespace micro {
|
||||
TfLiteRegistration RegisterOp(
|
||||
void* (*init)(TfLiteContext* context, const char* buffer, size_t length),
|
||||
TfLiteStatus (*prepare)(TfLiteContext* context, TfLiteNode* node),
|
||||
TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node));
|
||||
TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node),
|
||||
void (*free)(TfLiteContext* context, void* buffer) = nullptr);
|
||||
|
||||
// Prints out n bytes in a int8_t buffer as hex
|
||||
void PrintNBytes(const int8_t* tensor_data, int n_bytes,
|
||||
const char* prefix = nullptr);
|
||||
|
||||
// Prints out the the n bytes in a TfLiteEvalTensor as hex
|
||||
void PrintNBytes(const TfLiteEvalTensor* tensor, int n_bytes,
|
||||
const char* prefix = nullptr);
|
||||
|
||||
// Prints out the the n bytes in a TfLiteTensor as hex
|
||||
void PrintNBytes(const TfLiteTensor* tensor, int n_bytes,
|
||||
const char* prefix = nullptr);
|
||||
|
||||
// Returns a mutable tensor for a given input index. is_variable must be checked
|
||||
// during prepare when the full TfLiteTensor is available.
|
||||
|
||||
@@ -125,8 +125,7 @@ TfLiteStatus L2Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
L2EvalFloat(*params, *input, &op_params, output);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"L2_POOL_2D only supports float32 currently, got %s.",
|
||||
MicroPrintf("L2_POOL_2D only supports float32 currently, got %s.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context, "Output type is %s, requires float.",
|
||||
MicroPrintf("Output type is %s, requires float.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -132,8 +132,7 @@ TfLiteStatus LogSoftmaxEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"LOG_SOFTMAX only supports float32, int8, got %s.",
|
||||
MicroPrintf("LOG_SOFTMAX only supports float32, int8, got %s.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ limitations under the License.
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/integer_ops/logistic.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/integer_ops/tanh.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
@@ -530,11 +532,20 @@ void CalculateLstmGateInteger8x8_16(
|
||||
// Apply activation
|
||||
switch (activation) {
|
||||
case kTfLiteActSigmoid:
|
||||
micro_tensor_utils::ApplySigmoid(gate, n_batch, n_cell, gate);
|
||||
break;
|
||||
case kTfLiteActTanh:
|
||||
micro_tensor_utils::ApplyTanh(3, gate, n_batch, n_cell, gate);
|
||||
|
||||
reference_integer_ops::Logistic(
|
||||
0 /*data->input_multiplier*/, 0 /*data->input_left_shift */,
|
||||
n_batch * n_cell /*NumElements(input->dims)*/,
|
||||
gate /* tflite::micro::GetTensorData<int16_t>(input) */,
|
||||
gate /*tflite::micro::GetTensorData<int16_t>(output) */);
|
||||
|
||||
break;
|
||||
case kTfLiteActTanh: {
|
||||
int32_t dims_data = n_batch * n_cell;
|
||||
RuntimeShape tanh_inp_shape = RuntimeShape(1, &dims_data);
|
||||
reference_integer_ops::Tanh(0, 0, tanh_inp_shape, gate, tanh_inp_shape,
|
||||
gate);
|
||||
} break;
|
||||
default:
|
||||
// Only Sigmoid or Tanh is used.
|
||||
TFLITE_ASSERT_FALSE;
|
||||
@@ -599,7 +610,7 @@ void UpdateLstmCellInteger(int n_batch, int n_cell, int16_t* cell_state,
|
||||
// - scratch1: scratch area of size n_batch*n_cell
|
||||
// - scratch2: scratch area used by MatrixBatchVectorMultiplyAccumulate
|
||||
void CalculateLstmOutputInteger8x8_16(
|
||||
int n_batch, int n_cell, int n_output, const int16_t* cell_state,
|
||||
int n_batch, int n_cell, int n_output, int16_t* cell_state,
|
||||
int32_t cell_state_scale, const int16_t* output_gate,
|
||||
int32_t hidden_scale_a, int32_t hidden_scale_b, int32_t hidden_zp,
|
||||
const int8_t* projection_weights, int32_t proj_scale_a,
|
||||
@@ -607,8 +618,23 @@ void CalculateLstmOutputInteger8x8_16(
|
||||
int32_t output_state_zp, int8_t quantized_proj_clip, int8_t* output_state,
|
||||
int16_t* scratch0, int8_t* scratch1, int32_t* scratch2) {
|
||||
// Note: unlike float/hybrid, the activation is always Tanh.
|
||||
micro_tensor_utils::ApplyTanh(15 + cell_state_scale, cell_state, n_batch,
|
||||
n_cell, scratch0);
|
||||
|
||||
{
|
||||
int32_t tanh_input_left_shift = (15 + cell_state_scale) - 3;
|
||||
int32_t dims_data = n_batch * n_cell;
|
||||
if (tanh_input_left_shift < 0) /* handling negative shift value */
|
||||
{
|
||||
int32_t i;
|
||||
tanh_input_left_shift = -tanh_input_left_shift;
|
||||
for (i = 0; i < dims_data; i++) {
|
||||
cell_state[i] = cell_state[i] >> tanh_input_left_shift;
|
||||
}
|
||||
tanh_input_left_shift = 0;
|
||||
}
|
||||
RuntimeShape tanh_inp_shape = RuntimeShape(1, &dims_data);
|
||||
reference_integer_ops::Tanh(0, tanh_input_left_shift, tanh_inp_shape,
|
||||
cell_state, tanh_inp_shape, scratch0);
|
||||
}
|
||||
micro_tensor_utils::CwiseMul(output_gate, scratch0, hidden_scale_a,
|
||||
hidden_scale_b, n_batch, n_cell, hidden_zp,
|
||||
scratch1);
|
||||
|
||||
@@ -98,15 +98,13 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLiteOperation<int64_t, OpType>(context, node, op_context);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"Type %s (%d) is not supported by Maximum/Minimum.",
|
||||
MicroPrintf("Type %s (%d) is not supported by Maximum/Minimum.",
|
||||
TfLiteTypeGetName(op_context.output->type),
|
||||
op_context.output->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context,
|
||||
"Kernel type not supported by Maximum/Minimum.");
|
||||
MicroPrintf("Kernel type not supported by Maximum/Minimum.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
|
||||
@@ -72,6 +72,7 @@ TfLiteRegistration Register_READ_VARIABLE();
|
||||
TfLiteRegistration Register_RELU();
|
||||
TfLiteRegistration Register_RELU6();
|
||||
TfLiteRegistration Register_RESIZE_BILINEAR();
|
||||
TfLiteRegistration Register_SELECT_V2();
|
||||
TfLiteRegistration Register_SHAPE();
|
||||
TfLiteRegistration Register_SLICE();
|
||||
TfLiteRegistration Register_SPACE_TO_BATCH_ND();
|
||||
@@ -79,6 +80,7 @@ TfLiteRegistration Register_SPACE_TO_DEPTH();
|
||||
TfLiteRegistration Register_SQUARED_DIFFERENCE();
|
||||
TfLiteRegistration Register_SQUEEZE();
|
||||
TfLiteRegistration Register_SUB();
|
||||
TfLiteRegistration Register_SUM();
|
||||
TfLiteRegistration Register_SVDF();
|
||||
TfLiteRegistration Register_TRANSPOSE();
|
||||
TfLiteRegistration Register_TRANSPOSE_CONV();
|
||||
|
||||
@@ -663,7 +663,7 @@ void PortableCwiseMul(const int16_t* input_1, const int16_t* input_2,
|
||||
const int16_t b = input_2[index];
|
||||
int32_t value = static_cast<int32_t>(a) * static_cast<int32_t>(b);
|
||||
value = MultiplyByQuantizedMultiplier(value, multiplier, shift);
|
||||
value -= output_zp;
|
||||
value += output_zp;
|
||||
value = std::min(std::max(static_cast<int32_t>(-128), value),
|
||||
static_cast<int32_t>(127));
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
|
||||
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -60,6 +60,15 @@ void EvalMulFloatReference(TfLiteContext* context, TfLiteNode* node,
|
||||
const TfLiteEvalTensor* input2,
|
||||
TfLiteEvalTensor* output);
|
||||
|
||||
// Generic must define registration function.
|
||||
TfLiteRegistration Register_MUL();
|
||||
|
||||
#if defined(CMSIS_NN)
|
||||
TfLiteRegistration Register_MUL_INT8();
|
||||
#else
|
||||
// Fallback registration
|
||||
inline TfLiteRegistration Register_MUL_INT8() { return Register_MUL(); }
|
||||
#endif
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_MUL_H_
|
||||
|
||||
@@ -41,8 +41,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
|
||||
@@ -95,7 +95,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
data->axis);
|
||||
}
|
||||
default: {
|
||||
TF_LITE_KERNEL_LOG(context, "Type '%s' is not supported by pack.",
|
||||
MicroPrintf("Type '%s' is not supported by pack.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} break;
|
||||
default:
|
||||
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s not currently supported by Pad.",
|
||||
MicroPrintf("Type %s not currently supported by Pad.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ TfLiteStatus AverageEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
AveragePoolingEvalQuantized(context, node, params, data, input, output);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Input type %s is not currently supported",
|
||||
MicroPrintf("Input type %s is not currently supported",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
@@ -73,7 +73,7 @@ TfLiteStatus MaxEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
MaxPoolingEvalQuantized(context, node, params, data, input, output);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s not currently supported.",
|
||||
MicroPrintf("Type %s not currently supported.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
|
||||
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ limitations under the License.
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/micro/kernels/micro_ops.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
@@ -66,6 +67,19 @@ void MaxPoolingEvalQuantized(TfLiteContext* context, TfLiteNode* node,
|
||||
const TfLiteEvalTensor* input,
|
||||
TfLiteEvalTensor* output);
|
||||
|
||||
#if defined(CMSIS_NN)
|
||||
TfLiteRegistration Register_AVERAGE_POOL_2D_INT8();
|
||||
|
||||
TfLiteRegistration Register_MAX_POOL_2D_INT8();
|
||||
#else
|
||||
inline TfLiteRegistration Register_AVERAGE_POOL_2D_INT8() {
|
||||
return tflite::Register_AVERAGE_POOL_2D();
|
||||
}
|
||||
|
||||
inline TfLiteRegistration Register_MAX_POOL_2D_INT8() {
|
||||
return tflite::Register_MAX_POOL_2D();
|
||||
}
|
||||
#endif
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_POOLING_H_
|
||||
|
||||
@@ -61,8 +61,7 @@ TfLiteStatus PreluEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return kTfLiteOk;
|
||||
} break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context, "Only float32 and uint8_t are supported currently, got %d.",
|
||||
MicroPrintf("Only float32 and uint8_t are supported currently, got %d.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ limitations under the License.
|
||||
|
||||
namespace tflite {
|
||||
|
||||
const int kMaxNumberOfAxis = 4;
|
||||
const int kMaxNumberOfAxis = 5;
|
||||
const int kMaxNumberOfReducedAxis = 2;
|
||||
|
||||
TfLiteStatus PrepareSimple(TfLiteContext* context, TfLiteNode* node,
|
||||
|
||||
@@ -55,8 +55,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
auto* params =
|
||||
reinterpret_cast<TfLiteResizeBilinearParams*>(node->builtin_data);
|
||||
if (params->half_pixel_centers && params->align_corners) {
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context, "If half_pixel_centers is True, align_corners must be False.");
|
||||
MicroPrintf("If half_pixel_centers is True, align_corners must be False.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -100,8 +99,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context, "Output type is %d, requires float or int8.",
|
||||
output->type);
|
||||
MicroPrintf("Output type is %d, requires float or int8.", output->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ limitations under the License.
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
@@ -55,7 +54,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
output->type = input->type;
|
||||
|
||||
if (!IsConstantTensor(size)) {
|
||||
TF_LITE_KERNEL_LOG(context, "Dynamic tensors are unsupported in tfmicro.");
|
||||
MicroPrintf("Dynamic tensors are unsupported in tfmicro.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/kernels/internal/reference/select.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
constexpr int kInputTensorCondition = 0;
|
||||
constexpr int kInputTensorX = 1;
|
||||
constexpr int kInputTensorY = 2;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
struct OpData {
|
||||
bool requires_broadcast;
|
||||
// True if input condition is scalar or input condition has rank one and
|
||||
// matches the first dimension of other inputs.
|
||||
bool has_low_rank_input_condition;
|
||||
};
|
||||
|
||||
void* SelectInit(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
auto* data = static_cast<OpData*>(
|
||||
context->AllocatePersistentBuffer(context, sizeof(OpData)));
|
||||
data->requires_broadcast = false;
|
||||
data->has_low_rank_input_condition = false;
|
||||
return data;
|
||||
}
|
||||
|
||||
TfLiteStatus CheckBroadcastShape(TfLiteContext* context,
|
||||
const TfLiteTensor* input1,
|
||||
const TfLiteTensor* input2,
|
||||
const TfLiteTensor* input3,
|
||||
const TfLiteIntArray* output_shape) {
|
||||
const int dims1 = NumDimensions(input1);
|
||||
const int dims2 = NumDimensions(input2);
|
||||
const int dims3 = NumDimensions(input3);
|
||||
const int out_dims = std::max(std::max(dims1, dims2), dims3);
|
||||
TF_LITE_ENSURE_EQ(context, out_dims, output_shape->size);
|
||||
|
||||
for (int i = 0; i < out_dims; ++i) {
|
||||
const int d1 = i >= dims1 ? 1 : SizeOfDimension(input1, dims1 - i - 1);
|
||||
const int d2 = i >= dims2 ? 1 : SizeOfDimension(input2, dims2 - i - 1);
|
||||
const int d3 = i >= dims3 ? 1 : SizeOfDimension(input3, dims3 - i - 1);
|
||||
const int min_value = std::min(std::min(d1, d2), d3);
|
||||
int max_value = std::max(std::max(d1, d2), d3);
|
||||
// If one dimention is 0, others must be 0 or 1.
|
||||
if (min_value == 0) max_value = 0;
|
||||
if (!(d1 == 1 || d1 == max_value) || !(d2 == 1 || d2 == max_value) ||
|
||||
!(d3 == 1 || d3 == max_value)) {
|
||||
MicroPrintf("Given shapes are not broadcastable.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
TF_LITE_ENSURE_EQ(context, output_shape->data[out_dims - i - 1], max_value);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus SelectPrepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
OpData* data = reinterpret_cast<OpData*>(node->user_data);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 3);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TfLiteTensor* input_condition =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorCondition);
|
||||
|
||||
TfLiteTensor* input_x =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorX);
|
||||
|
||||
TfLiteTensor* input_y =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorY);
|
||||
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
|
||||
// Input must be bool.
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input_condition->type, kTfLiteBool);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input_x->type, input_y->type);
|
||||
output->type = input_x->type;
|
||||
|
||||
// Respect the original output shape when there are mixed shapes to represent
|
||||
// a scalar data.
|
||||
if (GetTensorShape(input_condition).FlatSize() == 1 &&
|
||||
GetTensorShape(input_x).FlatSize() == 1 &&
|
||||
GetTensorShape(input_y).FlatSize() == 1 &&
|
||||
GetTensorShape(output).FlatSize() == 1) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
bool same_shape = HaveSameShapes(input_condition, input_x) &&
|
||||
HaveSameShapes(input_x, input_y);
|
||||
if (!same_shape) {
|
||||
TF_LITE_ENSURE_OK(
|
||||
context, CheckBroadcastShape(context, input_condition, input_x, input_y,
|
||||
output->dims));
|
||||
data->requires_broadcast = true;
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input_condition);
|
||||
micro_context->DeallocateTempTfLiteTensor(input_x);
|
||||
micro_context->DeallocateTempTfLiteTensor(input_y);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus SelectEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input_condition =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorCondition);
|
||||
|
||||
TfLiteTensor* input_x =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorX);
|
||||
|
||||
TfLiteTensor* input_y =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorY);
|
||||
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
|
||||
#define TF_LITE_SELECT(type, op) \
|
||||
reference_ops::op(GetTensorShape(input_condition), \
|
||||
GetTensorData<bool>(input_condition), \
|
||||
GetTensorShape(input_x), GetTensorData<type>(input_x), \
|
||||
GetTensorShape(input_y), GetTensorData<type>(input_y), \
|
||||
GetTensorShape(output), GetTensorData<type>(output));
|
||||
|
||||
#define TF_LITE_SWITCH(type, op) \
|
||||
switch (type) { \
|
||||
case kTfLiteFloat32: \
|
||||
TF_LITE_SELECT(float, op); \
|
||||
break; \
|
||||
case kTfLiteInt8: \
|
||||
TF_LITE_SELECT(int8_t, op); \
|
||||
break; \
|
||||
case kTfLiteInt16: \
|
||||
TF_LITE_SELECT(int16_t, op); \
|
||||
break; \
|
||||
default: \
|
||||
MicroPrintf("Does not support type other than %s, but got %s", \
|
||||
"int8|int16|float32", TfLiteTypeGetName(type)); \
|
||||
return kTfLiteError; \
|
||||
}
|
||||
|
||||
if (data->has_low_rank_input_condition) {
|
||||
MicroPrintf("Not yet implemented.");
|
||||
return kTfLiteError;
|
||||
} else if (data->requires_broadcast) {
|
||||
TF_LITE_SWITCH(input_x->type, BroadcastSelect5DSlow);
|
||||
} else {
|
||||
TF_LITE_SWITCH(input_x->type, Select);
|
||||
}
|
||||
|
||||
#undef TF_LITE_SELECT
|
||||
#undef TF_LITE_SWITCH
|
||||
micro_context->DeallocateTempTfLiteTensor(input_condition);
|
||||
micro_context->DeallocateTempTfLiteTensor(input_x);
|
||||
micro_context->DeallocateTempTfLiteTensor(input_y);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// SelectV2 op selects values of 'x' if the corresponding value of 'condition'
|
||||
// is true or the value of 'y' if false. There are valid condition input sizes:
|
||||
//
|
||||
// 1. Either the same shape (in which case the select is elementwise), or
|
||||
// 2. Broadcastable shapes between 'condition', 'x' and 'y'.
|
||||
TfLiteRegistration Register_SELECT_V2() {
|
||||
return tflite::micro::RegisterOp(tflite::SelectInit, tflite::SelectPrepare,
|
||||
tflite::SelectEval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -47,7 +47,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
if (output->type != kTfLiteInt32) {
|
||||
TF_LITE_KERNEL_LOG(context, "Output type %s (%d) not supported.",
|
||||
MicroPrintf("Output type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(output->type), output->type);
|
||||
return kTfLiteError;
|
||||
} else {
|
||||
|
||||
@@ -106,7 +106,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
GetBeginAndSizeVectors<int64_t>(input->dims->size, begin, size,
|
||||
op_params.begin, op_params.size);
|
||||
} else {
|
||||
TF_LITE_KERNEL_LOG(context, "Begin tensor type %s (%d) not supported.",
|
||||
MicroPrintf("Begin tensor type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
@@ -75,8 +75,8 @@ TfLiteStatus SoftmaxEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user