diff --git a/components/wave_4b/Kconfig b/components/bsp_common/Kconfig similarity index 91% rename from components/wave_4b/Kconfig rename to components/bsp_common/Kconfig index 615cfdd..0279d39 100644 --- a/components/wave_4b/Kconfig +++ b/components/bsp_common/Kconfig @@ -1,5 +1,4 @@ -menu "Board Support Package (Waveshare ESP32-P4 4B)" - depends on KERN_BOARD_WAVE_4B +menu "Board Support Package" config BSP_ERROR_CHECK bool "Enable error check in BSP" @@ -28,13 +27,6 @@ menu "Board Support Package (Waveshare ESP32-P4 4B)" endmenu menu "Display" - config BSP_LCD_DPI_BUFFER_NUMS - int "Set number of frame buffers" - default 1 - range 1 3 - help - Let DPI LCD driver create a specified number of frame-size buffers. - config BSP_DISPLAY_BRIGHTNESS_LEDC_CH int "LEDC channel index" default 1 @@ -42,18 +34,26 @@ menu "Board Support Package (Waveshare ESP32-P4 4B)" help LEDC channel is used to generate PWM signal that controls display brightness. + config BSP_LCD_DPI_BUFFER_NUMS + int "Set number of frame buffers" + default 1 + range 1 3 + help + Let DPI LCD driver create a specified number of frame-size buffers. + Only used by MIPI DSI boards (wave_4b, wave_5). + choice BSP_LCD_COLOR_FORMAT prompt "Select LCD color format" default BSP_LCD_COLOR_FORMAT_RGB565 help Select the LCD color format RGB565/RGB888. + Only used by MIPI DSI boards (wave_4b, wave_5). config BSP_LCD_COLOR_FORMAT_RGB565 bool "RGB565" config BSP_LCD_COLOR_FORMAT_RGB888 bool "RGB888" endchoice - endmenu endmenu diff --git a/components/video/CMakeLists.txt b/components/video/CMakeLists.txt index 9ba852e..7d97e33 100644 --- a/components/video/CMakeLists.txt +++ b/components/video/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRCS "video.c" INCLUDE_DIRS "." - REQUIRES esp_cam_sensor esp_video wave_4b wave_35 wave_5 + REQUIRES esp_cam_sensor esp_video esp_sccb_intf wave_4b wave_35 wave_5 ) \ No newline at end of file diff --git a/components/video/video.c b/components/video/video.c index 43ce29e..7fc7e71 100644 --- a/components/video/video.c +++ b/components/video/video.c @@ -5,14 +5,49 @@ #include #include +#include "driver/i2c_master.h" #include "esp_err.h" #include "esp_log.h" +#include "esp_sccb_i2c.h" +#include "esp_sccb_intf.h" #include "esp_video_init.h" -#include "esp_video_isp_ioctl.h" #include "video.h" static const char *TAG = "video"; +// OV5647 SCCB handle used to widen on-sensor AE hysteresis past the ±8% +// window the managed sensor driver writes. The sensor's hunting response to +// that narrow band made luminance visibly pulse under high-contrast scenes. +#define OV5647_SCCB_ADDR 0x36 +#define OV5647_SCCB_FREQ_HZ 100000 +#define OV5647_AE_HYST_NUM_LOW 7 // BPT = target * 7/10 +#define OV5647_AE_HYST_NUM_HIGH 13 // WPT = target * 13/10 +#define OV5647_AE_HYST_DEN 10 +// Gain ceiling (register pair 0x3a18/0x3a19). Sensor default is 0x03FF (~64x) +// which enables a digital-gain multiplier stage; that stage produces discrete +// ~2x jumps as AE steps in/out of it, appearing as square-wave luminance. +// 0x01FF (~32x) stays entirely in the analog-gain range. +#define OV5647_MAX_GAIN_HI 0x01 +#define OV5647_MAX_GAIN_LO 0xFF + +static i2c_master_bus_handle_t s_i2c_bus = NULL; +static esp_sccb_io_handle_t s_sensor_sccb = NULL; + +static esp_err_t ensure_sensor_sccb(void) { + if (s_sensor_sccb) + return ESP_OK; + if (!s_i2c_bus) + return ESP_ERR_INVALID_STATE; + sccb_i2c_config_t cfg = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = OV5647_SCCB_ADDR, + .scl_speed_hz = OV5647_SCCB_FREQ_HZ, + .addr_bits_width = 16, + .val_bits_width = 8, + }; + return sccb_new_i2c_io(s_i2c_bus, &cfg, &s_sensor_sccb); +} + #define MAX_BUFFER_COUNT 6 #define MIN_BUFFER_COUNT 2 // 8KB needed: motor driver init (DW9714) deepens SCCB/I2C call stack @@ -82,6 +117,8 @@ esp_err_t app_video_main(i2c_master_bus_handle_t i2c_bus_handle) { esp_err_t ret; + s_i2c_bus = i2c_bus_handle; + if (i2c_bus_handle) { static esp_video_init_csi_config_t csi_config; static esp_video_init_config_t cam_config; @@ -436,14 +473,48 @@ esp_err_t app_video_close(int fd) { } esp_err_t app_video_set_ae_target(int fd, uint32_t level) { - struct v4l2_ext_controls controls = {.ctrl_class = V4L2_CTRL_CLASS_USER, - .count = 1}; - struct v4l2_ext_control control = {.id = V4L2_CID_EXPOSURE, .value = level}; - controls.controls = &control; - if (ioctl(fd, VIDIOC_S_EXT_CTRLS, &controls)) { - ESP_LOGW(TAG, "Set AE target level failed"); + (void)fd; + // Bypass V4L2_CID_EXPOSURE: the OV5647 driver's ov5647_set_AE_target() writes + // a ±8% stable band, which causes AE to hunt on small luma shifts under + // high-contrast scenes. Write the same target registers here with ±20%. + if (ensure_sensor_sccb() != ESP_OK) { + ESP_LOGW(TAG, "Set AE target: SCCB handle unavailable"); return ESP_FAIL; } + if (level == 0) + level = 1; + if (level > 255) + level = 255; + uint32_t low = (level * OV5647_AE_HYST_NUM_LOW) / OV5647_AE_HYST_DEN; + uint32_t high = (level * OV5647_AE_HYST_NUM_HIGH) / OV5647_AE_HYST_DEN; + if (high > 255) + high = 255; + uint32_t fast_high = high * 2; + if (fast_high > 255) + fast_high = 255; + uint32_t fast_low = low / 2; + + const struct { + uint16_t reg; + uint8_t val; + } writes[] = { + {0x3a0f, (uint8_t)high}, // WPT (stable window high) + {0x3a10, (uint8_t)low}, // BPT (stable window low) + {0x3a1b, (uint8_t)high}, // WPT2 + {0x3a1e, (uint8_t)low}, // BPT2 + {0x3a11, (uint8_t)fast_high}, // VPT (fast-step high) + {0x3a1f, (uint8_t)fast_low}, // fast-step low + {0x3a18, OV5647_MAX_GAIN_HI}, // AE gain ceiling high + {0x3a19, OV5647_MAX_GAIN_LO}, // AE gain ceiling low + }; + for (size_t i = 0; i < sizeof(writes) / sizeof(writes[0]); i++) { + if (esp_sccb_transmit_reg_a16v8(s_sensor_sccb, writes[i].reg, + writes[i].val) != ESP_OK) { + ESP_LOGW(TAG, "Set AE target: SCCB write to 0x%04x failed", + writes[i].reg); + return ESP_FAIL; + } + } return ESP_OK; } @@ -460,36 +531,6 @@ esp_err_t app_video_set_focus(int fd, uint32_t position) { return ESP_OK; } -esp_err_t app_video_disable_af(void) { - // AF control must go to the ISP device, not the camera sensor device - int isp_fd = open(ESP_VIDEO_ISP1_DEVICE_NAME, O_RDWR); - if (isp_fd < 0) { - ESP_LOGW(TAG, "Failed to open ISP device for AF disable"); - return ESP_FAIL; - } - - esp_video_isp_af_t af_cfg = {.enable = false}; - struct v4l2_ext_control control = { - .id = V4L2_CID_USER_ESP_ISP_AF, - .size = sizeof(af_cfg), - .p_u8 = (uint8_t *)&af_cfg, - }; - struct v4l2_ext_controls controls = { - .ctrl_class = V4L2_CTRL_CLASS_USER, - .count = 1, - .controls = &control, - }; - esp_err_t ret = ESP_OK; - if (ioctl(isp_fd, VIDIOC_S_EXT_CTRLS, &controls)) { - ESP_LOGW(TAG, "Disable AF failed"); - ret = ESP_FAIL; - } else { - ESP_LOGI(TAG, "ISP auto-focus disabled"); - } - close(isp_fd); - return ret; -} - bool app_video_has_focus_motor(int fd) { struct v4l2_query_ext_ctrl qctrl = {.id = V4L2_CID_FOCUS_ABSOLUTE}; return (ioctl(fd, VIDIOC_QUERY_EXT_CTRL, &qctrl) == 0); @@ -501,6 +542,12 @@ esp_err_t app_video_deinit(void) { return ESP_OK; } + if (s_sensor_sccb) { + esp_sccb_del_i2c_io(s_sensor_sccb); + s_sensor_sccb = NULL; + } + s_i2c_bus = NULL; + esp_err_t ret = esp_video_deinit(); if (ret != ESP_OK) { ESP_LOGE(TAG, "Deinit failed: %s", esp_err_to_name(ret)); diff --git a/components/video/video.h b/components/video/video.h index a465b97..860b3cf 100644 --- a/components/video/video.h +++ b/components/video/video.h @@ -213,16 +213,6 @@ esp_err_t app_video_set_focus(int video_fd, uint32_t position); */ bool app_video_has_focus_motor(int video_fd); -/** - * @brief Disable the ISP auto-focus algorithm. - * - * Opens the ISP device internally. Must be called after streaming - * starts to prevent the IPA pipeline from overriding the focus position. - * - * @return ESP_OK on success, or ESP_FAIL on failure. - */ -esp_err_t app_video_disable_af(void); - /** * @brief Deinitialize the video system. * diff --git a/components/wave_35/Kconfig b/components/wave_35/Kconfig deleted file mode 100644 index b92dde3..0000000 --- a/components/wave_35/Kconfig +++ /dev/null @@ -1,39 +0,0 @@ -menu "Board Support Package (Waveshare ESP32-P4 3.5)" - depends on KERN_BOARD_WAVE_35 - - config BSP_ERROR_CHECK - bool "Enable error check in BSP" - default y - help - Error check assert the application before returning the error code. - - menu "I2C" - config BSP_I2C_NUM - int "I2C peripheral index" - default 1 - range 0 1 - help - ESP32P4 has two I2C peripherals, pick the one you want to use. - - config BSP_I2C_FAST_MODE - bool "Enable I2C fast mode" - default y - help - I2C has two speed modes: normal (100kHz) and fast (400kHz). - - config BSP_I2C_CLK_SPEED_HZ - int - default 400000 if BSP_I2C_FAST_MODE - default 100000 - endmenu - - menu "Display" - config BSP_DISPLAY_BRIGHTNESS_LEDC_CH - int "LEDC channel index" - default 1 - range 0 7 - help - LEDC channel is used to generate PWM signal that controls display brightness. - endmenu - -endmenu diff --git a/components/wave_5/Kconfig b/components/wave_5/Kconfig deleted file mode 100644 index 1e31832..0000000 --- a/components/wave_5/Kconfig +++ /dev/null @@ -1,59 +0,0 @@ -menu "Board Support Package (Waveshare ESP32-P4 5-inch)" - depends on KERN_BOARD_WAVE_5 - - config BSP_ERROR_CHECK - bool "Enable error check in BSP" - default y - help - Error check assert the application before returning the error code. - - menu "I2C" - config BSP_I2C_NUM - int "I2C peripheral index" - default 1 - range 0 1 - help - ESP32P4 has two I2C peripherals, pick the one you want to use. - - config BSP_I2C_FAST_MODE - bool "Enable I2C fast mode" - default y - help - I2C has two speed modes: normal (100kHz) and fast (400kHz). - - config BSP_I2C_CLK_SPEED_HZ - int - default 400000 if BSP_I2C_FAST_MODE - default 100000 - endmenu - - menu "Display" - config BSP_LCD_DPI_BUFFER_NUMS - int "Set number of frame buffers" - default 1 - range 1 3 - help - Let DPI LCD driver create a specified number of frame-size buffers. - - config BSP_DISPLAY_BRIGHTNESS_LEDC_CH - int "LEDC channel index" - default 1 - range 0 7 - help - LEDC channel is used to generate PWM signal that controls display brightness. - - choice BSP_LCD_COLOR_FORMAT - prompt "Select LCD color format" - default BSP_LCD_COLOR_FORMAT_RGB565 - help - Select the LCD color format RGB565/RGB888. - - config BSP_LCD_COLOR_FORMAT_RGB565 - bool "RGB565" - config BSP_LCD_COLOR_FORMAT_RGB888 - bool "RGB888" - endchoice - - endmenu - -endmenu diff --git a/main/pages/capture_entropy.c b/main/pages/capture_entropy.c index dc4b98a..4ecdd0b 100644 --- a/main/pages/capture_entropy.c +++ b/main/pages/capture_entropy.c @@ -281,6 +281,11 @@ static bool camera_init(void) { if (app_video_stream_task_start(camera_handle, 0) != ESP_OK) return false; + // Apply the wider AE hysteresis + gain cap — without this, the sensor keeps + // its init-time ±8% window and uncapped gain ceiling, which causes + // square-wave luminance pulsing under low-light, high-contrast scenes. + app_video_set_ae_target(camera_handle, 80); + ppa_client_config_t ppa_cfg = {.oper_type = PPA_OPERATION_SRM}; if (ppa_register_client(&ppa_cfg, &cam_ppa_client) != ESP_OK) { ESP_LOGE(TAG, "Failed to register PPA client"); diff --git a/main/qr/scanner.c b/main/qr/scanner.c index 07c7717..849fae1 100644 --- a/main/qr/scanner.c +++ b/main/qr/scanner.c @@ -421,8 +421,7 @@ static void create_settings_overlay(void) { settings_overlay = lv_obj_create(qr_scanner_screen); lv_obj_remove_style_all(settings_overlay); lv_obj_set_size(settings_overlay, LV_PCT(100), LV_PCT(100)); - lv_obj_set_style_bg_color(settings_overlay, lv_color_black(), 0); - lv_obj_set_style_bg_opa(settings_overlay, LV_OPA_10, 0); + lv_obj_set_style_bg_opa(settings_overlay, LV_OPA_TRANSP, 0); lv_obj_add_flag(settings_overlay, LV_OBJ_FLAG_CLICKABLE); lv_obj_clear_flag(settings_overlay, LV_OBJ_FLAG_SCROLLABLE); @@ -431,7 +430,7 @@ static void create_settings_overlay(void) { lv_obj_set_size(panel, LV_PCT(85), LV_SIZE_CONTENT); lv_obj_align(panel, LV_ALIGN_BOTTOM_MID, 0, -12); theme_apply_frame(panel); - lv_obj_set_style_bg_opa(panel, LV_OPA_70, 0); + lv_obj_set_style_bg_opa(panel, LV_OPA_COVER, 0); lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(panel, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); @@ -475,6 +474,7 @@ static void create_settings_overlay(void) { lv_obj_t *close_btn = theme_create_button(panel, "Close", true); lv_obj_set_width(close_btn, LV_PCT(60)); lv_obj_set_style_bg_opa(close_btn, LV_OPA_COVER, 0); + lv_obj_set_style_margin_top(close_btn, 16, 0); lv_obj_add_event_cb(close_btn, settings_close_cb, LV_EVENT_CLICKED, NULL); } @@ -939,8 +939,11 @@ static void camera_init(void) { return; } - // Detect focus motor before streaming starts +#if BSP_CAM_HAS_MOTOR has_focus_motor = app_video_has_focus_motor(camera_ctlr_handle); +#else + has_focus_motor = false; +#endif ESP_ERROR_CHECK( app_video_register_frame_operation_cb(camera_video_frame_operation)); @@ -970,11 +973,10 @@ static void camera_init(void) { return; } - // Apply camera settings after stream starts (ISP pipeline must be active). - // Disable AF so manual focus isn't overridden by the IPA pipeline. + // Apply camera settings after stream starts (V4L2 controls register with the + // sensor device only once streaming). app_video_set_ae_target(camera_ctlr_handle, settings_get_ae_target()); if (has_focus_motor) { - app_video_disable_af(); app_video_set_focus(camera_ctlr_handle, settings_get_focus_position()); } diff --git a/sdkconfig.defaults b/sdkconfig.defaults index b53db58..e0deeb6 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -22,8 +22,7 @@ CONFIG_CAMERA_OV5647=y CONFIG_CAMERA_OV5647_MIPI_RAW8_800X800_50FPS=n CONFIG_CAMERA_OV5647_MIPI_RAW10_1280X960_BINNING_45FPS=y CONFIG_CAMERA_OV5647_MIPI_DEFAULT_FMT_RAW10_1280X960_BINNING_45FPS=y -CONFIG_LVGL_PORT_ENABLE_PPA=y -CONFIG_ESP_VIDEO_ENABLE_ISP_PIPELINE_CONTROLLER=y +CONFIG_ESP_VIDEO_ENABLE_ISP_PIPELINE_CONTROLLER=n CONFIG_LV_USE_CLIB_MALLOC=y CONFIG_LV_USE_CLIB_STRING=y CONFIG_LV_USE_CLIB_SPRINTF=y