diff --git a/components/esp_driver_cam/dvp/include/esp_private/esp_cam_dvp.h b/components/esp_driver_cam/dvp/include/esp_private/esp_cam_dvp.h index 032c816179..79a6ff5ecc 100644 --- a/components/esp_driver_cam/dvp/include/esp_private/esp_cam_dvp.h +++ b/components/esp_driver_cam/dvp/include/esp_private/esp_cam_dvp.h @@ -26,7 +26,7 @@ extern "C" { esp_err_t esp_cam_ctlr_dvp_init(int ctlr_id, cam_clock_source_t clk_src, const esp_cam_ctlr_dvp_pin_config_t *pin); /** - * @brief ESP CAM DVP output hardware clock + * @brief ESP CAM DVP output hardware clock, the function "esp_cam_ctlr_dvp_init" should be called first * * @param ctlr_id CAM DVP controller ID * @param clk_src CAM DVP clock source @@ -38,6 +38,24 @@ esp_err_t esp_cam_ctlr_dvp_init(int ctlr_id, cam_clock_source_t clk_src, const e */ esp_err_t esp_cam_ctlr_dvp_output_clock(int ctlr_id, cam_clock_source_t clk_src, uint32_t xclk_freq); +/** + * @brief Initialize ESP CAM DVP clock pin (other DVP GPIO pins excluded the clock pins will not be initialized) + * and hardware clock, then output clock signal with given frequency + * + * @note The function is implemented based on "esp_cam_ctlr_dvp_init" and "esp_cam_ctlr_dvp_output_clock", + * so calling "esp_cam_ctlr_dvp_init" and "esp_cam_ctlr_dvp_output_clock" is not required + * + * @param ctlr_id DVP controller ID + * @param pin DVP clock pin + * @param clk_src DVP clock source + * @param xclk_freq DVP clock frequency + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_start_clock(int ctlr_id, gpio_num_t pin, cam_clock_source_t clk_src, uint32_t xclk_freq); + /** * @brief ESP CAM DVP de-initialzie. * diff --git a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c index c1b0df3156..1bac8fadf4 100644 --- a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c +++ b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c @@ -45,14 +45,16 @@ #define ALIGN_UP_BY(num, align) ((align) == 0 ? (num) : (((num) + ((align) - 1)) & ~((align) - 1))) -#define DVP_CAM_CONFIG_INPUT_PIN(pin, sig, inv) \ -{ \ - ret = esp_cam_ctlr_dvp_config_input_gpio(pin, sig, inv); \ - if (ret != ESP_OK) { \ - ESP_LOGE(TAG, "failed to configure pin=%d sig=%d", \ - pin, sig); \ - return ret; \ - } \ +#define DVP_CAM_CONFIG_INPUT_PIN(pin, sig, inv) \ +{ \ + if (pin != GPIO_NUM_NC) { \ + ret = esp_cam_ctlr_dvp_config_input_gpio(pin, sig, inv); \ + if (ret != ESP_OK) { \ + ESP_LOGE(TAG, "failed to configure pin=%d sig=%d", \ + pin, sig); \ + return ret; \ + } \ + } \ } typedef struct dvp_platform { @@ -360,7 +362,7 @@ esp_err_t esp_cam_ctlr_dvp_init(int ctlr_id, cam_clock_source_t clk_src, const e } /** - * @brief ESP CAM DVP output hardware clock + * @brief ESP CAM DVP output hardware clock, the function "esp_cam_ctlr_dvp_init" should be called first * * @param ctlr_id CAM DVP controller ID * @param clk_src CAM DVP clock source @@ -383,15 +385,54 @@ esp_err_t esp_cam_ctlr_dvp_output_clock(int ctlr_id, cam_clock_source_t clk_src, if ((src_clk_hz % xclk_freq) == 0) { DVP_CAM_CLK_ATOMIC() { + // The camera sensors require precision without frequency and duty cycle jitter, + // so the fractional divisor can't be used. cam_ll_set_group_clock_coeff(ctlr_id, src_clk_hz / xclk_freq, 0, 0); }; ret = ESP_OK; + } else { + ESP_LOGE(TAG, "calculated frequency divider is not integer so clock isn't changed"); } return ret; } +/** + * @brief Initialize ESP CAM DVP clock pin (other DVP GPIO pins excluded the clock pins will not be initialized) + * and hardware clock, then output clock signal with given frequency + * + * @note The function is implemented based on "esp_cam_ctlr_dvp_init" and "esp_cam_ctlr_dvp_output_clock", + * so calling "esp_cam_ctlr_dvp_init" and "esp_cam_ctlr_dvp_output_clock" is not required + * + * @param ctlr_id DVP controller ID + * @param pin DVP clock pin + * @param clk_src DVP clock source + * @param xclk_freq DVP clock frequency + * + * @return + * - ESP_OK on success + * - Others if failed + */ +esp_err_t esp_cam_ctlr_dvp_start_clock(int ctlr_id, gpio_num_t pin, cam_clock_source_t clk_src, uint32_t xclk_freq) +{ + esp_cam_ctlr_dvp_pin_config_t pin_config = {0}; + + pin_config.data_width = CAM_CTLR_DATA_WIDTH_8; + pin_config.vsync_io = GPIO_NUM_NC; + pin_config.de_io = GPIO_NUM_NC; + pin_config.pclk_io = GPIO_NUM_NC; + for (int i = 0; i < CAM_DVP_DATA_SIG_NUM; i++) { + pin_config.data_io[i] = GPIO_NUM_NC; + } + pin_config.xclk_io = pin; + + ESP_RETURN_ON_ERROR(esp_cam_ctlr_dvp_init(ctlr_id, clk_src, &pin_config), TAG, "failed to initialize DVP controller"); + ESP_RETURN_ON_ERROR(esp_cam_ctlr_dvp_output_clock(ctlr_id, clk_src, xclk_freq), TAG, "failed to output clock"); + + return ESP_OK; +} + /** * @brief ESP CAM DVP de-initialzie. * diff --git a/components/esp_driver_cam/test_apps/dvp/main/test_dvp_driver.c b/components/esp_driver_cam/test_apps/dvp/main/test_dvp_driver.c index f138f1ee9b..b7e7e5ac2f 100644 --- a/components/esp_driver_cam/test_apps/dvp/main/test_dvp_driver.c +++ b/components/esp_driver_cam/test_apps/dvp/main/test_dvp_driver.c @@ -7,6 +7,7 @@ #include "sdkconfig.h" #include "unity.h" #include "esp_cam_ctlr_dvp.h" +#include "esp_private/esp_cam_dvp.h" #include "esp_cam_ctlr.h" TEST_CASE("TEST DVP driver allocation", "[DVP]") @@ -171,3 +172,14 @@ TEST_CASE("TEST DVP driver intern/extern generate xclk", "[DVP]") TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle)); TEST_ESP_OK(esp_cam_ctlr_del(handle)); } + +TEST_CASE("TEST DVP driver only output xclk signal", "[DVP]") +{ + TEST_ESP_OK(esp_cam_ctlr_dvp_start_clock(0, 20, CAM_CLK_SRC_DEFAULT, 20000000)); + TEST_ESP_OK(esp_cam_ctlr_dvp_deinit(0)); + +#if CONFIG_IDF_TARGET_ESP32S3 + TEST_ESP_OK(esp_cam_ctlr_dvp_start_clock(0, 20, CAM_CLK_SRC_PLL240M, 24000000)); + TEST_ESP_OK(esp_cam_ctlr_dvp_deinit(0)); +#endif +}