From c8a486cbda6bdee5dd1132d1e5325896cba4d4c7 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 2 Jul 2025 13:05:38 +0200 Subject: [PATCH 1/8] common: Add container_of macro. Signed-off-by: iabdalkader --- common/omv_common.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/omv_common.h b/common/omv_common.h index 5df1865d1..77c95556e 100644 --- a/common/omv_common.h +++ b/common/omv_common.h @@ -74,4 +74,10 @@ #define OMV_PROFILE_PRINT(F) #endif +// Returns a pointer to the containing structure +// ptr: Pointer to the member within the structure +// type: Type of the containing structure +// member: Name of the member within the structure +#define OMV_CONTAINER_OF(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) #endif //__OMV_COMMON_H__ From 9f98ac73a0ac981f68dfe07e55563dbb6a32052f Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 2 Jul 2025 13:05:16 +0200 Subject: [PATCH 2/8] lib/stm32: Pass DCMI handle to DCMI_DMAConvCpltUser. Signed-off-by: iabdalkader --- lib/stm32/f4/src/stm32f4xx_hal_dcmi.c | 24 ++++++++++-------------- lib/stm32/f7/src/stm32f7xx_hal_dcmi.c | 21 +++++++++++---------- lib/stm32/h7/src/stm32h7xx_hal_dcmi.c | 10 +++------- 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/lib/stm32/f4/src/stm32f4xx_hal_dcmi.c b/lib/stm32/f4/src/stm32f4xx_hal_dcmi.c index c2097060d..ec03fcbca 100644 --- a/lib/stm32/f4/src/stm32f4xx_hal_dcmi.c +++ b/lib/stm32/f4/src/stm32f4xx_hal_dcmi.c @@ -1114,12 +1114,6 @@ HAL_StatusTypeDef HAL_DCMI_UnRegisterCallback(DCMI_HandleTypeDef *hdcmi, HAL_DCM /** @defgroup DCMI_Private_Functions DCMI Private Functions * @{ */ - -__weak void DCMI_DMAConvCpltUser(uint32_t addr) -{ - -} - /** * @brief DMA conversion complete callback. * @param hdma pointer to a DMA_HandleTypeDef structure that contains @@ -1129,28 +1123,30 @@ __weak void DCMI_DMAConvCpltUser(uint32_t addr) static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma) { DCMI_HandleTypeDef* hdcmi; + DMA_Stream_TypeDef *stream; + extern void DCMI_DMAConvCpltUser(DCMI_HandleTypeDef* hdcmi, uint32_t addr); - hdcmi = ( DCMI_HandleTypeDef* )((DMA_HandleTypeDef* )hdma)->Parent; - hdcmi->State= HAL_DCMI_STATE_READY; + hdcmi = (DCMI_HandleTypeDef*) ((DMA_HandleTypeDef*)hdma)->Parent; + stream = (DMA_Stream_TypeDef*) (hdcmi->DMA_Handle->Instance); // Note: we don't need to adjust memory addresses because they stay the same. - if(hdcmi->XferCount != 0) { + if (hdcmi->XferCount != 0) { hdcmi->XferCount--; } - if((hdcmi->DMA_Handle->Instance->CR & DMA_SxCR_CT) == 0) { + if ((stream->CR & DMA_SxCR_CT) == 0) { // Current traget is M0 call user callback with M1 - DCMI_DMAConvCpltUser(hdcmi->DMA_Handle->Instance->M1AR); + DCMI_DMAConvCpltUser(hdcmi, stream->M1AR); } else { // Current traget is M1 call user callback with M0 - DCMI_DMAConvCpltUser(hdcmi->DMA_Handle->Instance->M0AR); + DCMI_DMAConvCpltUser(hdcmi, stream->M0AR); } /* Check if the frame is transferred */ - if(hdcmi->XferCount == 0) { + if (hdcmi->XferCount == 0) { /* Reload XferCount */ hdcmi->XferCount = hdcmi->XferTransferNumber; - /* Enable the Frame interrupt */ + /* Re-enable frame interrupt */ __HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME); /* When snapshot mode, set dcmi state to ready */ diff --git a/lib/stm32/f7/src/stm32f7xx_hal_dcmi.c b/lib/stm32/f7/src/stm32f7xx_hal_dcmi.c index fd22b6ad5..96cd018e9 100755 --- a/lib/stm32/f7/src/stm32f7xx_hal_dcmi.c +++ b/lib/stm32/f7/src/stm32f7xx_hal_dcmi.c @@ -800,10 +800,6 @@ __weak void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) */ } -__weak void DCMI_DMAConvCpltUser(uint32_t addr) -{ - -} /** * @} */ @@ -1168,26 +1164,30 @@ HAL_StatusTypeDef HAL_DCMI_UnRegisterCallback(DCMI_HandleTypeDef *hdcmi, HAL_DCM static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma) { DCMI_HandleTypeDef* hdcmi; - hdcmi = (DCMI_HandleTypeDef*) ((DMA_HandleTypeDef*)hdma)->Parent; + DMA_Stream_TypeDef *stream; + extern void DCMI_DMAConvCpltUser(DCMI_HandleTypeDef* hdcmi, uint32_t addr); + + hdcmi = (DCMI_HandleTypeDef*) ((DMA_HandleTypeDef*)hdma)->Parent; + stream = (DMA_Stream_TypeDef*) (hdcmi->DMA_Handle->Instance); // Note: we don't need to adjust memory addresses because they stay the same. if (hdcmi->XferCount != 0) { hdcmi->XferCount--; } - if ((hdcmi->DMA_Handle->Instance->CR & DMA_SxCR_CT) == 0) { + if ((stream->CR & DMA_SxCR_CT) == 0) { // Current traget is M0 call user callback with M1 - DCMI_DMAConvCpltUser(hdcmi->DMA_Handle->Instance->M1AR); + DCMI_DMAConvCpltUser(hdcmi, stream->M1AR); } else { // Current traget is M1 call user callback with M0 - DCMI_DMAConvCpltUser(hdcmi->DMA_Handle->Instance->M0AR); + DCMI_DMAConvCpltUser(hdcmi, stream->M0AR); } /* Check if the frame is transferred */ - if(hdcmi->XferCount == 0) { + if (hdcmi->XferCount == 0) { /* Reload XferCount */ hdcmi->XferCount = hdcmi->XferTransferNumber; - /* Enable the Frame interrupt */ + /* Re-enable frame interrupt */ __HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME); /* When snapshot mode, set dcmi state to ready */ @@ -1196,6 +1196,7 @@ static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma) } } } + /** * @brief DMA error callback * @param hdma pointer to a DMA_HandleTypeDef structure that contains diff --git a/lib/stm32/h7/src/stm32h7xx_hal_dcmi.c b/lib/stm32/h7/src/stm32h7xx_hal_dcmi.c index 0d1d0cd3e..98b6ce14e 100644 --- a/lib/stm32/h7/src/stm32h7xx_hal_dcmi.c +++ b/lib/stm32/h7/src/stm32h7xx_hal_dcmi.c @@ -809,11 +809,6 @@ __weak void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) */ } -__weak void DCMI_DMAConvCpltUser(uint32_t addr) -{ - -} - /** * @} */ @@ -1194,6 +1189,7 @@ static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma) { DCMI_HandleTypeDef* hdcmi; DMA_Stream_TypeDef *stream; + extern void DCMI_DMAConvCpltUser(DCMI_HandleTypeDef* hdcmi, uint32_t addr); hdcmi = (DCMI_HandleTypeDef*) ((DMA_HandleTypeDef*)hdma)->Parent; stream = (DMA_Stream_TypeDef*) (hdcmi->DMA_Handle->Instance); @@ -1205,10 +1201,10 @@ static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma) if ((stream->CR & DMA_SxCR_CT) == 0) { // Current traget is M0 call user callback with M1 - DCMI_DMAConvCpltUser(stream->M1AR); + DCMI_DMAConvCpltUser(hdcmi, stream->M1AR); } else { // Current traget is M1 call user callback with M0 - DCMI_DMAConvCpltUser(stream->M0AR); + DCMI_DMAConvCpltUser(hdcmi, stream->M0AR); } /* Check if the frame is transferred */ From a48e545bc0d11a6e7be602cd17286b61c7252a81 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 7 Jul 2025 11:43:56 +0200 Subject: [PATCH 3/8] ports/stm32: Disable DCMI pins init for DCMIPP. DCMIPP is used exclusively for CSI sensors now. Signed-off-by: iabdalkader --- ports/stm32/stm_hal.c | 50 ------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/ports/stm32/stm_hal.c b/ports/stm32/stm_hal.c index 77fe8c7dc..e1d7dd1c8 100644 --- a/ports/stm32/stm_hal.c +++ b/ports/stm32/stm_hal.c @@ -514,22 +514,6 @@ void HAL_DCMI_MspDeInit(DCMI_HandleTypeDef *hdcmi) { #if defined(DCMIPP) void HAL_DCMIPP_MspInit(DCMIPP_HandleTypeDef *hdcmipp) { - const omv_gpio_t dcmi_pins[] = { - #ifdef OMV_CSI_D0_PIN - OMV_CSI_D0_PIN, - OMV_CSI_D1_PIN, - OMV_CSI_D2_PIN, - OMV_CSI_D3_PIN, - OMV_CSI_D4_PIN, - OMV_CSI_D5_PIN, - OMV_CSI_D6_PIN, - OMV_CSI_D7_PIN, - OMV_CSI_HSYNC_PIN, - OMV_CSI_VSYNC_PIN, - OMV_CSI_PXCLK_PIN, - #endif - }; - // Enable DCMIPP clock. __HAL_RCC_DCMIPP_CLK_ENABLE(); __HAL_RCC_DCMIPP_CLK_SLEEP_ENABLE(); @@ -541,38 +525,9 @@ void HAL_DCMIPP_MspInit(DCMIPP_HandleTypeDef *hdcmipp) { __HAL_RCC_CSI_CLK_SLEEP_ENABLE(); __HAL_RCC_CSI_FORCE_RESET(); __HAL_RCC_CSI_RELEASE_RESET(); - - #ifdef OMV_CSI_VSYNC_PIN - // Configure VSYNC EXTI. - #if DCMI_VSYNC_EXTI_SHARED - if (exti_gpio == 0) - #endif // DCMI_VSYNC_EXTI_SHARED - omv_gpio_config(OMV_CSI_VSYNC_PIN, OMV_GPIO_MODE_IT_BOTH, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_MAX, -1); - #endif // OMV_CSI_VSYNC_PIN - - // Configure DCMI pins. - for (int i = 0; i < OMV_ARRAY_SIZE(dcmi_pins); i++) { - omv_gpio_config(dcmi_pins[i], OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_MAX, -1); - } } void HAL_DCMIPP_MspDeInit(DCMIPP_HandleTypeDef *hdcmipp) { - const omv_gpio_t dcmi_pins[] = { - #ifdef OMV_CSI_D0_PIN - OMV_CSI_D0_PIN, - OMV_CSI_D1_PIN, - OMV_CSI_D2_PIN, - OMV_CSI_D3_PIN, - OMV_CSI_D4_PIN, - OMV_CSI_D5_PIN, - OMV_CSI_D6_PIN, - OMV_CSI_D7_PIN, - OMV_CSI_HSYNC_PIN, - OMV_CSI_VSYNC_PIN, - OMV_CSI_PXCLK_PIN, - #endif - }; - // Disable DCMI clock. __HAL_RCC_DCMIPP_FORCE_RESET(); __HAL_RCC_DCMIPP_RELEASE_RESET(); @@ -583,11 +538,6 @@ void HAL_DCMIPP_MspDeInit(DCMIPP_HandleTypeDef *hdcmipp) { __HAL_RCC_CSI_RELEASE_RESET(); __HAL_RCC_CSI_CLK_DISABLE(); __HAL_RCC_CSI_CLK_SLEEP_DISABLE(); - - // Deinit pins. - for (int i = 0; i < OMV_ARRAY_SIZE(dcmi_pins); i++) { - omv_gpio_deinit(dcmi_pins[i]); - } } #endif From 77d79ba58a89b81c7e37851ed3b8e559cd8ccd3f Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 2 Jul 2025 13:06:08 +0200 Subject: [PATCH 4/8] ports/stm32: Decouple CSI instance used by main driver. This patch removes the assumption that the main driver always uses the default CSI instance. Instead, the CSI instance used by each interface is now explicitly set. This prepares the driver for supporting multiple CSIs using the main driver. Signed-off-by: iabdalkader --- ports/stm32/omv_csi.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 52def82ac..e2e9b85da 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -72,28 +72,36 @@ extern uint8_t _line_buf; extern uint32_t hal_get_exti_gpio(uint32_t line); +// Stores the CSI handle associated with DCMI/DCMIPP. +typedef enum { + CSI_HANDLE_DCMI = 0, + CSI_HANDLE_DCMIPP = 1, +} csi_handle_t; + +static omv_csi_t *stm_csi_all[2] = { 0 }; + #if USE_DCMI void DCMI_IRQHandler(void) { - omv_csi_t *csi = omv_csi_get(-1); + omv_csi_t *csi = stm_csi_all[CSI_HANDLE_DCMI]; HAL_DCMI_IRQHandler(&csi->dcmi); } #endif #if USE_DCMIPP void CSI_IRQHandler(void) { - omv_csi_t *csi = omv_csi_get(-1); + omv_csi_t *csi = stm_csi_all[CSI_HANDLE_DCMIPP]; HAL_DCMIPP_CSI_IRQHandler(&csi->dcmi); } void DCMIPP_IRQHandler(void) { - omv_csi_t *csi = omv_csi_get(-1); + omv_csi_t *csi = stm_csi_all[CSI_HANDLE_DCMIPP]; HAL_DCMIPP_IRQHandler(&csi->dcmi); } #endif #if USE_MDMA void omv_csi_mdma_irq_handler(void) { - omv_csi_t *csi = omv_csi_get(-1); + omv_csi_t *csi = stm_csi_all[CSI_HANDLE_DCMI]; if (MDMA->GISR0 & (1 << OMV_MDMA_CHANNEL_DCMI_0)) { HAL_MDMA_IRQHandler(&csi->mdma0); @@ -137,6 +145,9 @@ static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) { return -1; } + // Store CSI handle used for DCMIPP + stm_csi_all[CSI_HANDLE_DCMIPP] = csi; + // Configure and enable DCMI IRQ Channel NVIC_SetPriority(DCMIPP_IRQn, IRQ_PRI_DCMI); HAL_NVIC_EnableIRQ(DCMIPP_IRQn); @@ -163,6 +174,9 @@ static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) { return -1; } + // Store CSI handle used for DCMI + stm_csi_all[CSI_HANDLE_DCMI] = csi; + // Configure and enable DCMI IRQ Channel NVIC_SetPriority(DCMI_IRQn, IRQ_PRI_DCMI); HAL_NVIC_EnableIRQ(DCMI_IRQn); @@ -408,9 +422,9 @@ static uint32_t get_dcmi_hw_crop(omv_csi_t *csi, uint32_t bytes_per_pixel) { #if USE_DCMI void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) { #else -void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t pipe) { +void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *hdcmi, uint32_t pipe) { #endif - omv_csi_t *csi = omv_csi_get(-1); + omv_csi_t *csi = OMV_CONTAINER_OF(hdcmi, omv_csi_t, dcmi); framebuffer_t *fb = csi->fb; #if USE_MDMA @@ -444,7 +458,7 @@ void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t p if (buffer == NULL) { omv_csi_abort(csi, false, false); } else { - HAL_DCMIPP_PIPE_SetMemoryAddress(dcmipp, pipe, DCMIPP_MEMORY_ADDRESS_0, (uint32_t) buffer->data); + HAL_DCMIPP_PIPE_SetMemoryAddress(hdcmi, pipe, DCMIPP_MEMORY_ADDRESS_0, (uint32_t) buffer->data); } #endif } @@ -454,8 +468,8 @@ void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t p // buffer that was used. At this point, the DMA transfers the next line to the next buffer. // Using line buffers allows performing post-processing before writing the frame to the // framebuffer, and help hide external RAM latency. -void DCMI_DMAConvCpltUser(uint32_t addr) { - omv_csi_t *csi = omv_csi_get(-1); +void DCMI_DMAConvCpltUser(DCMI_HandleTypeDef *hdcmi, uint32_t addr) { + omv_csi_t *csi = OMV_CONTAINER_OF(hdcmi, omv_csi_t, dcmi); framebuffer_t *fb = csi->fb; // Throttle frames to match the current frame rate. From 07d3e3f559484377ab0699030379ed37b2dabbd1 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 6 Jul 2025 13:42:08 +0200 Subject: [PATCH 5/8] ports/stm32: Support using DCMI and DCMIPP. Signed-off-by: iabdalkader --- ports/stm32/omv_csi.c | 636 +++++++++++++++++------------------ ports/stm32/omv_portconfig.h | 13 +- 2 files changed, 322 insertions(+), 327 deletions(-) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index e2e9b85da..65da2eec5 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -44,58 +44,63 @@ #include "stm_dma.h" #include "stm_isp.h" -#if defined(DMA2) -#define USE_DMA (1) -#define DMA_MAX_TRANSFER (0xFFFFU * 4U) +#if defined(DCMIPP) +#define USE_DCMIPP (1) +// NOTE using PIPE1. +#define DCMIPP_PIPE (DCMIPP_PIPE1) +#endif + +#if defined(PSSI) +#define DCMI_IRQn DCMI_PSSI_IRQn +#define DCMI_IRQHandler DCMI_PSSI_IRQHandler +#define DMA_PRIORITY_HIGH DMA_HIGH_PRIORITY #endif #if defined(OMV_MDMA_CHANNEL_DCMI_0) #define USE_MDMA (1) #endif -#if !defined(DCMIPP) -#define USE_DCMI (1) -#define DCMI_IS_ACTIVE() (DCMI->CR & DCMI_CR_ENABLE) -#else -#define USE_DCMIPP (1) -// NOTE using PIPE1. -#define DCMI_IS_ACTIVE() (DCMIPP->P1FCTCR & DCMIPP_P1FCTCR_CPTREQ) -#define DCMIPP_PIPE (DCMIPP_PIPE1) -#endif - #ifndef OMV_CSI_DMA_XFER_PORTS -#define OMV_CSI_DMA_XFER_PORTS (0) +#define OMV_CSI_DMA_XFER_PORTS (0) #endif -#define LINE_WIDTH_ALIGNMENT (16) +#ifndef OMV_CSI_DMA_MAX_SIZE +#define OMV_CSI_DMA_MAX_SIZE (0xFFFFU * 4U) +#endif -extern uint8_t _line_buf; -extern uint32_t hal_get_exti_gpio(uint32_t line); +#ifndef OMV_CSI_LINE_ALIGNMENT +#define OMV_CSI_LINE_ALIGNMENT (16) +#endif -// Stores the CSI handle associated with DCMI/DCMIPP. typedef enum { CSI_HANDLE_DCMI = 0, CSI_HANDLE_DCMIPP = 1, } csi_handle_t; +extern uint8_t _line_buf; +// Stores the CSI handle associated with DCMI/DCMIPP. static omv_csi_t *stm_csi_all[2] = { 0 }; -#if USE_DCMI +#if defined(STM32N6) +static DMA_QListTypeDef dma_queue; +// Nodes can't be places in CSI state because the need to be uncacheable. +static DMA_NodeTypeDef OMV_ATTR_SECTION(dma_nodes[2], ".dma_buffer"); +#endif + void DCMI_IRQHandler(void) { omv_csi_t *csi = stm_csi_all[CSI_HANDLE_DCMI]; HAL_DCMI_IRQHandler(&csi->dcmi); } -#endif #if USE_DCMIPP void CSI_IRQHandler(void) { omv_csi_t *csi = stm_csi_all[CSI_HANDLE_DCMIPP]; - HAL_DCMIPP_CSI_IRQHandler(&csi->dcmi); + HAL_DCMIPP_CSI_IRQHandler(&csi->dcmipp); } void DCMIPP_IRQHandler(void) { omv_csi_t *csi = stm_csi_all[CSI_HANDLE_DCMIPP]; - HAL_DCMIPP_IRQHandler(&csi->dcmi); + HAL_DCMIPP_IRQHandler(&csi->dcmipp); } #endif @@ -112,199 +117,189 @@ void omv_csi_mdma_irq_handler(void) { } #endif +static bool stm_csi_is_active(omv_csi_t *csi) { + #if USE_DCMIPP + if (csi->mipi_if) { + return (DCMIPP->P1FCTCR & DCMIPP_P1FCTCR_CPTREQ); + } + #endif + return (DCMI->CR & DCMI_CR_ENABLE); +} + static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) { if (config == OMV_CSI_CONFIG_INIT) { - #if USE_DMA + if (!csi->mipi_if) { + // Configure and initialize DMA. + if (stm_dma_init(&csi->dma, OMV_CSI_DMA_CHANNEL, OMV_CSI_DMA_REQUEST, + DMA_PERIPH_TO_MEMORY, 4, 4, OMV_CSI_DMA_XFER_PORTS, + &stm_dma_csi_init, true)) { + return OMV_CSI_ERROR_DMA_INIT_FAILED; + } - // Configure and initialize DMA. - if (stm_dma_init(&csi->dma, OMV_CSI_DMA_CHANNEL, OMV_CSI_DMA_REQUEST, - DMA_PERIPH_TO_MEMORY, 4, 4, OMV_CSI_DMA_XFER_PORTS, - &stm_dma_csi_init, true)) { - return OMV_CSI_ERROR_DMA_INIT_FAILED; - } + #if defined(STM32N6) + // Initialize DMA in circular mode. + if (stm_dma_ll_init(&csi->dma, &dma_queue, dma_nodes, + OMV_ARRAY_SIZE(dma_nodes), OMV_CSI_DMA_LIST_PORTS)) { + return OMV_CSI_ERROR_CSI_INIT_FAILED; + } + #endif - // Set DMA IRQ handle - stm_dma_set_irq_descr(OMV_CSI_DMA_CHANNEL, &csi->dma); - - // Configure the DMA IRQ Channel - csi->dma_irqn = stm_dma_channel_to_irqn(OMV_CSI_DMA_CHANNEL); - NVIC_SetPriority(csi->dma_irqn, IRQ_PRI_DMA21); + // Set DMA IRQ handle + stm_dma_set_irq_descr(OMV_CSI_DMA_CHANNEL, &csi->dma); - #if USE_MDMA - csi->mdma0.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_0); - csi->mdma1.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_1); - #endif + // Configure the DMA IRQ Channel + csi->dma_irqn = stm_dma_channel_to_irqn(OMV_CSI_DMA_CHANNEL); + NVIC_SetPriority(csi->dma_irqn, IRQ_PRI_DMA21); - #endif // USE_DMA + #if USE_MDMA + csi->mdma0.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_0); + csi->mdma1.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_1); + #endif - // Configure DCMI/PP. - #if USE_DCMIPP - // Initialize the DCMIPP - csi->dcmi.Instance = DCMIPP; - if (HAL_DCMIPP_Init(&csi->dcmi) != HAL_OK) { - return -1; - } + csi->dcmi.Instance = DCMI; + csi->dcmi.Init.VSPolarity = csi->vsync_pol ? DCMI_VSPOLARITY_HIGH : DCMI_VSPOLARITY_LOW; + csi->dcmi.Init.HSPolarity = csi->hsync_pol ? DCMI_HSPOLARITY_HIGH : DCMI_HSPOLARITY_LOW; + csi->dcmi.Init.PCKPolarity = csi->pixck_pol ? DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING; + csi->dcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE; + csi->dcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME; + csi->dcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B; + csi->dcmi.Init.JPEGMode = DCMI_JPEG_DISABLE; - // Store CSI handle used for DCMIPP - stm_csi_all[CSI_HANDLE_DCMIPP] = csi; + // Link the DMA handle to the DCMI handle + __HAL_LINKDMA(&csi->dcmi, DMA_Handle, csi->dma); - // Configure and enable DCMI IRQ Channel - NVIC_SetPriority(DCMIPP_IRQn, IRQ_PRI_DCMI); - HAL_NVIC_EnableIRQ(DCMIPP_IRQn); + // Initialize the DCMI + HAL_DCMI_DeInit(&csi->dcmi); + if (HAL_DCMI_Init(&csi->dcmi) != HAL_OK) { + return OMV_CSI_ERROR_CSI_INIT_FAILED; + } - // Configure and enable CSI IRQ Channel - NVIC_SetPriority(CSI_IRQn, IRQ_PRI_DCMI); - HAL_NVIC_EnableIRQ(CSI_IRQn); - #else - csi->dcmi.Instance = DCMI; - csi->dcmi.Init.VSPolarity = csi->vsync_pol ? DCMI_VSPOLARITY_HIGH : DCMI_VSPOLARITY_LOW; - csi->dcmi.Init.HSPolarity = csi->hsync_pol ? DCMI_HSPOLARITY_HIGH : DCMI_HSPOLARITY_LOW; - csi->dcmi.Init.PCKPolarity = csi->pixck_pol ? DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING; - csi->dcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE; - csi->dcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME; - csi->dcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B; - csi->dcmi.Init.JPEGMode = DCMI_JPEG_DISABLE; + // Store CSI handle used for DCMI + stm_csi_all[CSI_HANDLE_DCMI] = csi; - // Link the DMA handle to the DCMI handle - __HAL_LINKDMA(&csi->dcmi, DMA_Handle, csi->dma); + // Configure and enable DCMI IRQ Channel + NVIC_SetPriority(DCMI_IRQn, IRQ_PRI_DCMI); + HAL_NVIC_EnableIRQ(DCMI_IRQn); + } else { + #if USE_DCMIPP + // Initialize the DCMIPP + csi->dcmipp.Instance = DCMIPP; - // Initialize the DCMI - HAL_DCMI_DeInit(&csi->dcmi); - if (HAL_DCMI_Init(&csi->dcmi) != HAL_OK) { - return -1; - } + HAL_DCMIPP_DeInit(&csi->dcmipp); + if (HAL_DCMIPP_Init(&csi->dcmipp) != HAL_OK) { + return OMV_CSI_ERROR_CSI_INIT_FAILED; + } - // Store CSI handle used for DCMI - stm_csi_all[CSI_HANDLE_DCMI] = csi; - - // Configure and enable DCMI IRQ Channel - NVIC_SetPriority(DCMI_IRQn, IRQ_PRI_DCMI); - HAL_NVIC_EnableIRQ(DCMI_IRQn); - #endif - } else if (config == OMV_CSI_CONFIG_DEINIT) { - #if USE_DCMI - HAL_NVIC_DisableIRQ(DCMI_IRQn); - HAL_DCMI_DeInit(&csi->dcmi); - #else - HAL_NVIC_DisableIRQ(DCMIPP_IRQn); - HAL_DCMIPP_DeInit(&csi->dcmi); - #endif - } else if (config == OMV_CSI_CONFIG_PIXFORMAT) { - #if USE_DCMI - DCMI->CR &= ~(DCMI_CR_JPEG_Msk << DCMI_CR_JPEG_Pos); - DCMI->CR |= (csi->pixformat == PIXFORMAT_JPEG) ? DCMI_JPEG_ENABLE : DCMI_JPEG_DISABLE; - #else - - // Reset DCMI and pipes states to allow reconfiguring them. Note - // that abort() doesn't reset the state unless the pipe is active. - csi->dcmi.State = HAL_DCMIPP_STATE_INIT; - for (size_t i=0; idcmi.PipeState[i] = HAL_DCMIPP_PIPE_STATE_RESET; - } - - // Select and configure the DCMIPP source. - if (csi->mipi_if) { + // Select and configure the DCMIPP source. DCMIPP_CSI_ConfTypeDef scfg = { .NumberOfLanes = DCMIPP_CSI_TWO_DATA_LANES, .DataLaneMapping = DCMIPP_CSI_PHYSICAL_DATA_LANES, .PHYBitrate = (csi->mipi_brate == 850) ? DCMIPP_CSI_PHY_BT_850 : DCMIPP_CSI_PHY_BT_1200, }; - if (HAL_DCMIPP_CSI_SetConfig(&csi->dcmi, &scfg) != HAL_OK) { + + if (HAL_DCMIPP_CSI_SetConfig(&csi->dcmipp, &scfg) != HAL_OK) { return OMV_CSI_ERROR_CSI_INIT_FAILED; } + // Configure CSI virtual channel and pipe. - DCMIPP_CSI_PIPE_ConfTypeDef pcfg = { + DCMIPP_CSI_PIPE_ConfTypeDef csi_pcfg = { .DataTypeMode = DCMIPP_DTMODE_DTIDA, .DataTypeIDA = DCMIPP_DT_RAW10, .DataTypeIDB = DCMIPP_DT_RAW10, }; - if (HAL_DCMIPP_CSI_SetVCConfig(&csi->dcmi, DCMIPP_VIRTUAL_CHANNEL0, - DCMIPP_CSI_DT_BPP10) != HAL_OK) { + if (HAL_DCMIPP_CSI_SetVCConfig(&csi->dcmipp, DCMIPP_VIRTUAL_CHANNEL0, + DCMIPP_CSI_DT_BPP10) != HAL_OK) { return OMV_CSI_ERROR_CSI_INIT_FAILED; } - if (HAL_DCMIPP_CSI_PIPE_SetConfig(&csi->dcmi, DCMIPP_PIPE, &pcfg) != HAL_OK) { - return OMV_CSI_ERROR_CSI_INIT_FAILED; - } - } else { - DCMIPP_ParallelConfTypeDef scfg = { - .SynchroMode = DCMIPP_SYNCHRO_HARDWARE, - .ExtendedDataMode = DCMIPP_INTERFACE_8BITS, - .VSPolarity = csi->vsync_pol ? DCMIPP_VSPOLARITY_HIGH : DCMIPP_VSPOLARITY_LOW, - .HSPolarity = csi->hsync_pol ? DCMIPP_HSPOLARITY_HIGH : DCMIPP_HSPOLARITY_LOW, - .PCKPolarity = csi->pixck_pol ? DCMIPP_PCKPOLARITY_RISING : DCMIPP_PCKPOLARITY_FALLING, - }; - if (csi->raw_output) { - scfg.Format = DCMIPP_FORMAT_RAW8; - } else if (csi->pixformat == PIXFORMAT_RGB565) { - scfg.Format = DCMIPP_FORMAT_RGB565; - scfg.SwapCycles = (csi->rgb_swap == 1) ? DCMIPP_SWAPCYCLES_DISABLE : DCMIPP_SWAPCYCLES_ENABLE; - } else if (csi->pixformat == PIXFORMAT_GRAYSCALE) { - scfg.Format = (csi->mono_bpp == 1) ? DCMIPP_FORMAT_MONOCHROME_8B : DCMIPP_FORMAT_YUV422; - } else if (csi->pixformat == PIXFORMAT_YUV422) { - scfg.Format = DCMIPP_FORMAT_YUV422; - scfg.SwapCycles = (csi->yuv_swap == 1) ? DCMIPP_SWAPCYCLES_ENABLE : DCMIPP_SWAPCYCLES_DISABLE; - } else if (csi->pixformat == PIXFORMAT_BAYER) { - scfg.Format = DCMIPP_FORMAT_RAW8; - } else { - return OMV_CSI_ERROR_PIXFORMAT_UNSUPPORTED; - } - if (HAL_DCMIPP_PARALLEL_SetConfig(&csi->dcmi, &scfg) != HAL_OK) { - return OMV_CSI_ERROR_CSI_INIT_FAILED; - } - } - // Configure the pixel processing pipeline. - if (stm_isp_config_pipeline(&csi->dcmi, DCMIPP_PIPE, csi->pixformat, csi->raw_output)) { - return OMV_CSI_ERROR_CSI_INIT_FAILED; + if (HAL_DCMIPP_CSI_PIPE_SetConfig(&csi->dcmipp, DCMIPP_PIPE, &csi_pcfg) != HAL_OK) { + return OMV_CSI_ERROR_CSI_INIT_FAILED; + } + + // Store CSI handle used for DCMIPP + stm_csi_all[CSI_HANDLE_DCMIPP] = csi; + + // Configure and enable DCMI IRQ Channel + NVIC_SetPriority(DCMIPP_IRQn, IRQ_PRI_DCMI); + HAL_NVIC_EnableIRQ(DCMIPP_IRQn); + + // Configure and enable CSI IRQ Channel + NVIC_SetPriority(CSI_IRQn, IRQ_PRI_DCMI); + HAL_NVIC_EnableIRQ(CSI_IRQn); + #endif + } + } else if (config == OMV_CSI_CONFIG_DEINIT) { + if (!csi->mipi_if) { + HAL_NVIC_DisableIRQ(DCMI_IRQn); + HAL_DCMI_DeInit(&csi->dcmi); + } else { + #if USE_DCMIPP + HAL_NVIC_DisableIRQ(DCMIPP_IRQn); + HAL_DCMIPP_DeInit(&csi->dcmipp); + #endif + } + } else if (config == OMV_CSI_CONFIG_PIXFORMAT) { + if (!csi->mipi_if) { + DCMI->CR &= ~(DCMI_CR_JPEG_Msk << DCMI_CR_JPEG_Pos); + DCMI->CR |= (csi->pixformat == PIXFORMAT_JPEG) ? DCMI_JPEG_ENABLE : DCMI_JPEG_DISABLE; + } else { + #if USE_DCMIPP + csi->dcmipp.State = HAL_DCMIPP_STATE_READY; + // Reset pipes states to allow reconfiguring them. + for (size_t i=0; idcmipp.PipeState[i] = HAL_DCMIPP_PIPE_STATE_RESET; + } + // Configure the pixel processing pipeline. + if (stm_isp_config_pipeline(&csi->dcmipp, DCMIPP_PIPE, csi->pixformat, csi->raw_output)) { + return OMV_CSI_ERROR_CSI_INIT_FAILED; + } + #endif } - #endif } + return 0; } // Stop the DCMI from generating more DMA requests, and disable the DMA. static int stm_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) { - if (!DCMI_IS_ACTIVE()) { + csi->dma_size = 0; + + if (!stm_csi_is_active(csi)) { return 0; } - #if USE_DCMI - DCMI->CR &= ~DCMI_CR_ENABLE; - #endif - - #if USE_DMA - if (in_irq) { - HAL_DMA_Abort_IT(&csi->dma); - } else { - HAL_DMA_Abort(&csi->dma); - } - HAL_NVIC_DisableIRQ(csi->dma_irqn); - #endif - - #if USE_MDMA - if (!in_irq) { - HAL_MDMA_Abort(&csi->mdma0); - HAL_MDMA_Abort(&csi->mdma1); - } - HAL_MDMA_DeInit(&csi->mdma0); - HAL_MDMA_DeInit(&csi->mdma1); - #endif - - #if USE_DCMI - __HAL_DCMI_DISABLE_IT(&csi->dcmi, DCMI_IT_FRAME); - __HAL_DCMI_CLEAR_FLAG(&csi->dcmi, DCMI_FLAG_FRAMERI); - #else if (!csi->mipi_if) { - HAL_DCMIPP_PIPE_Stop(&csi->dcmi, DCMIPP_PIPE); - } else { - HAL_DCMIPP_CSI_PIPE_Stop(&csi->dcmi, DCMIPP_PIPE, DCMIPP_VIRTUAL_CHANNEL0); - } - for (size_t i=0; idcmi.PipeState[i] = HAL_DCMIPP_PIPE_STATE_RESET; - } - #endif + DCMI->CR &= ~DCMI_CR_ENABLE; + while (DCMI->CR & DCMI_CR_ENABLE); + #if defined(STM32N6) + HAL_DMA_Abort(&csi->dma); + #else + if (in_irq) { + HAL_DMA_Abort_IT(&csi->dma); + } else { + HAL_DMA_Abort(&csi->dma); + } + #endif + HAL_NVIC_DisableIRQ(csi->dma_irqn); + + #if USE_MDMA + if (!in_irq) { + HAL_MDMA_Abort(&csi->mdma0); + HAL_MDMA_Abort(&csi->mdma1); + } + HAL_MDMA_DeInit(&csi->mdma0); + HAL_MDMA_DeInit(&csi->mdma1); + #endif + + __HAL_DCMI_DISABLE_IT(&csi->dcmi, DCMI_IT_FRAME); + __HAL_DCMI_CLEAR_FLAG(&csi->dcmi, DCMI_FLAG_FRAMERI); + } else { + #if USE_DCMIPP + HAL_DCMIPP_CSI_PIPE_Stop(&csi->dcmipp, DCMIPP_PIPE, DCMIPP_VIRTUAL_CHANNEL0); + #endif // USE_DCMIPP + } return 0; } @@ -373,7 +368,7 @@ static int stm_clk_set_frequency(omv_clk_t *clk, uint32_t frequency) { if ((HAL_TIM_PWM_Init(&clk->tim) != HAL_OK) || (HAL_TIM_PWM_ConfigChannel(&clk->tim, &TIMOCHandle, OMV_CSI_TIM_CHANNEL) != HAL_OK) || (HAL_TIM_PWM_Start(&clk->tim, OMV_CSI_TIM_CHANNEL) != HAL_OK)) { - return -1; + return OMV_CSI_ERROR_TIM_INIT_FAILED; } #elif (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_MCO) // Pass through the MCO1 clock with source input set to HSE (12MHz). @@ -402,29 +397,22 @@ int omv_csi_set_vsync_callback(omv_csi_t *csi, omv_csi_cb_t cb) { return 0; } -#if USE_DCMI // If the image is cropped by more than 1 word in width, align the line start to a word // address to improve copy performance. Do not crop by more than 1 word as this will // result in less time between DMA transfers complete interrupts on 16-byte boundaries. static uint32_t get_dcmi_hw_crop(omv_csi_t *csi, uint32_t bytes_per_pixel) { framebuffer_t *fb = csi->fb; - uint32_t byte_x_offset = (fb->x * bytes_per_pixel) % sizeof(uint32_t); + uint32_t byte_x_offset = (fb->x * bytes_per_pixel) % 4; uint32_t width_remainder = (resolution[csi->framesize][0] - (fb->x + fb->u)) * bytes_per_pixel; - if (byte_x_offset && (width_remainder >= (sizeof(uint32_t) - byte_x_offset))) { + if (byte_x_offset && (width_remainder >= (4 - byte_x_offset))) { return byte_x_offset; } return 0; } -#endif -#if USE_DCMI -void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) { -#else -void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *hdcmi, uint32_t pipe) { -#endif - omv_csi_t *csi = OMV_CONTAINER_OF(hdcmi, omv_csi_t, dcmi); +static void stm_csi_frame_event(omv_csi_t *csi, uint32_t pipe) { framebuffer_t *fb = csi->fb; #if USE_MDMA @@ -452,22 +440,34 @@ void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *hdcmi, uint32_t pi csi->frame_cb.fun(csi->frame_cb.arg); } - #if USE_DCMIPP + #if defined(STM32N6) // Get the destination buffer address. vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); - if (buffer == NULL) { + if (buffer == NULL) { // FIFO is FULL omv_csi_abort(csi, false, false); - } else { - HAL_DCMIPP_PIPE_SetMemoryAddress(hdcmi, pipe, DCMIPP_MEMORY_ADDRESS_0, (uint32_t) buffer->data); + } else if (csi->mipi_if) { + HAL_DCMIPP_PIPE_SetMemoryAddress(&csi->dcmipp, pipe, + DCMIPP_MEMORY_ADDRESS_0, (uint32_t) buffer->data); + } else if (csi->one_shot) { + HAL_DCMI_Stop(&csi->dcmi); + HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_SNAPSHOT, (uint32_t) buffer->data, csi->dma_size); } - #endif + #endif // STM32N6 } -#if USE_DCMI -// This function is called after each line transfer is complete, with a pointer to the -// buffer that was used. At this point, the DMA transfers the next line to the next buffer. -// Using line buffers allows performing post-processing before writing the frame to the -// framebuffer, and help hide external RAM latency. +void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) { + stm_csi_frame_event(OMV_CONTAINER_OF(hdcmi, omv_csi_t, dcmi), 0); +} + +#if USE_DCMIPP +void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *hdcmi, uint32_t pipe) { + stm_csi_frame_event(OMV_CONTAINER_OF(hdcmi, omv_csi_t, dcmipp), pipe); +} +#endif + +#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) +// This function is called after each transfer is complete, +// with a pointer to the buffer that was used. void DCMI_DMAConvCpltUser(DCMI_HandleTypeDef *hdcmi, uint32_t addr) { omv_csi_t *csi = OMV_CONTAINER_OF(hdcmi, omv_csi_t, dcmi); framebuffer_t *fb = csi->fb; @@ -492,14 +492,14 @@ void DCMI_DMAConvCpltUser(DCMI_HandleTypeDef *hdcmi, uint32_t addr) { if (csi->pixformat == PIXFORMAT_JPEG) { if (csi->jpg_format == 3) { - // JPEG MODE 3: Variable line width per frame, with the last line potentially shorter and - // no padding. `offset` is incremented once every max transfer, and the DMA counter holds - // the total size. + // JPEG MODE 3: Variable line width per frame, with the last line + // potentially shorter and no padding. `offset` is incremented once + // every max transfer, and the DMA counter holds the total size. buffer->offset += 1; } else if (csi->jpg_format == 4) { - // JPEG MODE 4: Fixed width and height per frame. Each line starts with two bytes indicating - // valid data length, followed by image data and optional padding (0xFF). `offset` holds the - // total size. + // JPEG MODE 4: Fixed width and height per frame. Each line starts + // with two bytes indicating valid data length, followed by image + // data and optional padding (0xFF). `offset` holds the total size. uint16_t size = __REV16(*((uint16_t *) addr)); if (buffer->offset + size > framebuffer_get_buffer_size(fb)) { buffer->jpeg_buffer_overflow = true; @@ -551,10 +551,9 @@ void DCMI_DMAConvCpltUser(DCMI_HandleTypeDef *hdcmi, uint32_t addr) { omv_csi_copy_line(csi, NULL, src, dst); #endif } -#endif +#endif // #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { - uint32_t length = 0; framebuffer_t *fb = csi->fb; if (csi->pixformat == PIXFORMAT_INVALID) { @@ -573,23 +572,23 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { framebuffer_update_jpeg_buffer(&tmp); } - // Ensure that the raw frame fits into the FB. It will be switched from RGB565 to BAYER - // first to save space before being cropped until it fits. + // Ensure that the raw frame fits into the FB. It will be switched from RGB565 + // to BAYER first to save space before being cropped until it fits. omv_csi_auto_crop_framebuffer(csi); - // Restore frame buffer width and height if they were changed before. BPP is restored later. - // Note that JPEG compression is done first on the framebuffer with the user settings. + // Restore frame buffer width and height if they were changed before. BPP is + // restored later. Note that JPEG compression is done first on the framebuffer + // with the user settings. uint32_t w = fb->u; uint32_t h = fb->v; - // TODO - // If DCMI_DMAConvCpltUser() happens before framebuffer_free_current_buffer(); below then the - // transfer is stopped and it will be re-enabled again right afterwards in the single vbuffer - // case. + // If DCMI_DMAConvCpltUser() happens before framebuffer_free_current_buffer(); + // below then the transfer is stopped and it will be re-enabled again right + // afterwards in the single vbuffer case. framebuffer_free_current_buffer(fb); // Configure and start the capture. - if (!DCMI_IS_ACTIVE()) { + if (!stm_csi_is_active(csi)) { framebuffer_setup_buffers(fb); // Get the destination buffer address. @@ -598,111 +597,108 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { return OMV_CSI_ERROR_FRAMEBUFFER_ERROR; } - #if USE_DCMI - // Setup the size and address of the transfer - uint32_t bytes_per_pixel = omv_csi_get_src_bpp(csi); - uint32_t x_crop = get_dcmi_hw_crop(csi, bytes_per_pixel); - uint32_t line_width_bytes = resolution[csi->framesize][0] * bytes_per_pixel; + if (csi->mipi_if) { + #if USE_DCMIPP + uint32_t bytes_per_pixel = omv_csi_get_dst_bpp(csi); + uint32_t line_width = fb->u * bytes_per_pixel; - // Shrink the captured pixel count by one word to allow cropping to fix alignment. - if (x_crop) { - line_width_bytes -= sizeof(uint32_t); - } - - length = line_width_bytes * h; - - // Error out if the transfer size is not compatible with DMA transfer restrictions. - if ((!line_width_bytes) || - (line_width_bytes % sizeof(uint32_t)) || - (line_width_bytes > (OMV_LINE_BUF_SIZE / 2)) || - (!length) || (length % LINE_WIDTH_ALIGNMENT)) { - return OMV_CSI_ERROR_INVALID_FRAMESIZE; - } - - HAL_DCMI_DisableCrop(&csi->dcmi); - if (csi->pixformat != PIXFORMAT_JPEG) { - // Vertically crop the image. Horizontal cropping is done in software. - HAL_DCMI_ConfigCrop(&csi->dcmi, x_crop, fb->y, line_width_bytes - 1, h - 1); - HAL_DCMI_EnableCrop(&csi->dcmi); - } - - #if USE_MDMA - // Configure MDMA for non-JPEG modes. MDMA will be used to either - // completely offload the transfer, in case of non-transposed mode - // or copy transposed lines. - if (csi->pixformat != PIXFORMAT_JPEG) { - stm_mdma_init(csi, bytes_per_pixel, x_crop); - } - #endif - - // Reset the DMA state and re-enable it. - ((DMA_Stream_TypeDef *) csi->dma.Instance)->CR &= ~(DMA_SxCR_CIRC | DMA_SxCR_CT | DMA_SxCR_DBM); - HAL_NVIC_EnableIRQ(csi->dma_irqn); - - // HAL_DCMI_Start_DMA and HAL_DCMI_Start_DMA_MB both perform circular transfers, - // differing only in size, with an interrupt after every half of the transfer. - if ((csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3)) { - // Start a one-shot transfer to the framebuffer, used only for JPEG mode 3. - uint32_t size = framebuffer_get_buffer_size(fb); - length = IM_MIN(size, (DMA_MAX_TRANSFER * 2U)); - HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_SNAPSHOT, - (uint32_t) buffer->data, length / sizeof(uint32_t)); - // HAL_DCMI_Start_DMA splits bigger transfers. - if (length > DMA_MAX_TRANSFER) { - length /= 2; + if (!line_width || + line_width % OMV_CSI_LINE_ALIGNMENT) { + return OMV_CSI_ERROR_INVALID_FRAMESIZE; } - #if USE_MDMA - } else if ((csi->pixformat != PIXFORMAT_JPEG) && (!csi->transpose)) { - // Special transfer mode that uses DMA in circular mode and MDMA - // to move the lines to the final destination. - ((DMA_Stream_TypeDef *) csi->dma.Instance)->CR |= DMA_SxCR_CIRC; - HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_CONTINUOUS, - (uint32_t) &_line_buf, line_width_bytes / sizeof(uint32_t)); - #endif // USE_MDMA - } else { - // Start a multibuffer (line by line) transfer. - HAL_DCMI_Start_DMA_MB(&csi->dcmi, DCMI_MODE_CONTINUOUS, - (uint32_t) &_line_buf, length / sizeof(uint32_t), h); - } - #else - uint32_t bytes_per_pixel = omv_csi_get_dst_bpp(csi); - uint32_t line_width_bytes = fb->u * bytes_per_pixel; - if (!line_width_bytes || - line_width_bytes % LINE_WIDTH_ALIGNMENT) { - return OMV_CSI_ERROR_INVALID_FRAMESIZE; - } - - // Configure crop - DCMIPP_CropConfTypeDef ccfg = { - .HStart = fb->x, - .VStart = fb->y, - .HSize = fb->u, - .VSize = fb->v, - }; - if (HAL_DCMIPP_PIPE_SetCropConfig(&csi->dcmi, DCMIPP_PIPE, &ccfg) != HAL_OK || - HAL_DCMIPP_PIPE_EnableCrop(&csi->dcmi, DCMIPP_PIPE) != HAL_OK) { - return OMV_CSI_ERROR_CSI_INIT_FAILED; - } - - // Set output pitch - if (HAL_DCMIPP_PIPE_SetPitch(&csi->dcmi, DCMIPP_PIPE, line_width_bytes) != HAL_OK) { - return OMV_CSI_ERROR_CSI_INIT_FAILED; - } - - // Start the DCMIPP - if (!csi->mipi_if) { - if (HAL_DCMIPP_PIPE_Start(&csi->dcmi, DCMIPP_PIPE, (uint32_t) buffer->data, - DCMIPP_MODE_CONTINUOUS) != HAL_OK) { - return OMV_CSI_ERROR_CAPTURE_FAILED; + // Configure crop + DCMIPP_CropConfTypeDef ccfg = { + .HStart = fb->x, + .VStart = fb->y, + .HSize = fb->u, + .VSize = fb->v, + }; + if (HAL_DCMIPP_PIPE_SetCropConfig(&csi->dcmipp, DCMIPP_PIPE, &ccfg) != HAL_OK || + HAL_DCMIPP_PIPE_EnableCrop(&csi->dcmipp, DCMIPP_PIPE) != HAL_OK) { + return OMV_CSI_ERROR_CSI_INIT_FAILED; } - } else { - if (HAL_DCMIPP_CSI_PIPE_Start(&csi->dcmi, DCMIPP_PIPE, DCMIPP_VIRTUAL_CHANNEL0, + + // Set output pitch + if (HAL_DCMIPP_PIPE_SetPitch(&csi->dcmipp, DCMIPP_PIPE, line_width) != HAL_OK) { + return OMV_CSI_ERROR_CSI_INIT_FAILED; + } + + // Start the DCMIPP + if (HAL_DCMIPP_CSI_PIPE_Start(&csi->dcmipp, DCMIPP_PIPE, DCMIPP_VIRTUAL_CHANNEL0, (uint32_t) buffer->data, DCMIPP_MODE_CONTINUOUS) != HAL_OK) { return OMV_CSI_ERROR_CAPTURE_FAILED; } + #endif // USE_DCMIPP + } else { + // Setup the size and address of the transfer + uint32_t bytes_per_pixel = omv_csi_get_src_bpp(csi); + uint32_t x_crop = get_dcmi_hw_crop(csi, bytes_per_pixel); + uint32_t line_width = resolution[csi->framesize][0] * bytes_per_pixel; + + // Shrink the captured pixel count by one word to allow cropping to fix alignment. + if (x_crop) { + line_width -= 4; + } + + csi->dma_size = line_width * h / 4; + + // Error out if the transfer size is not compatible with DMA transfer restrictions. + if ((!line_width) || (line_width % 4) || + #if defined(OMV_LINE_BUF_SIZE) + (line_width > (OMV_LINE_BUF_SIZE / 2)) || + #endif + (!csi->dma_size) || (csi->dma_size % OMV_CSI_LINE_ALIGNMENT)) { + return OMV_CSI_ERROR_INVALID_FRAMESIZE; + } + + HAL_DCMI_DisableCrop(&csi->dcmi); + if (csi->pixformat != PIXFORMAT_JPEG) { + // Vertically crop the image. Horizontal cropping is done in software. + HAL_DCMI_ConfigCrop(&csi->dcmi, x_crop, fb->y, line_width - 1, h - 1); + HAL_DCMI_EnableCrop(&csi->dcmi); + } + + #if USE_MDMA + // Configure MDMA for non-JPEG modes. MDMA will be used to either + // completely offload the transfer, in case of non-transposed mode + // or copy transposed lines. + if (csi->pixformat != PIXFORMAT_JPEG) { + stm_mdma_init(csi, bytes_per_pixel, x_crop); + } + #endif + + // Reset the DMA state and re-enable it. + #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) + OMV_CSI_DMA_CHANNEL->CR &= ~(DMA_SxCR_CIRC | DMA_SxCR_CT | DMA_SxCR_DBM); + #endif + HAL_NVIC_EnableIRQ(csi->dma_irqn); + + // HAL_DCMI_Start_DMA and HAL_DCMI_Start_DMA_MB both perform circular transfers, + // differing only in size, with an interrupt after every half of the transfer. + if ((csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3)) { + // Start a one-shot transfer to the framebuffer, used only for JPEG mode 3. + uint32_t size = framebuffer_get_buffer_size(fb); + csi->dma_size = IM_MIN(size, (OMV_CSI_DMA_MAX_SIZE * 2U)) / 4; + csi->one_shot = true; + HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_SNAPSHOT, (uint32_t) buffer->data, csi->dma_size); + #if USE_MDMA + } else if ((csi->pixformat != PIXFORMAT_JPEG) && (!csi->transpose)) { + // Special transfer mode that uses DMA in circular mode and MDMA + // to move the lines to the final destination. + ((DMA_Stream_TypeDef *) csi->dma.Instance)->CR |= DMA_SxCR_CIRC; + HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_CONTINUOUS, (uint32_t) &_line_buf, line_width / 4); + #endif // USE_MDMA + } else { + #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) + // Start a multibuffer (line by line) transfer. + HAL_DCMI_Start_DMA_MB(&csi->dcmi, DCMI_MODE_CONTINUOUS, (uint32_t) &_line_buf, csi->dma_size, h); + #else + csi->one_shot = true; + HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_SNAPSHOT, (uint32_t) buffer->data, csi->dma_size); + #endif + } } - #endif // USE_DCMI } // Trigger the camera if FSYNC is enabled. @@ -721,12 +717,10 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { framebuffer_flags_t fb_flags = FB_NO_FLAGS; - #if USE_MDMA - // csi->mdma0.State will be HAL_MDMA_STATE_RESET if the MDMA is not initialized. - if (csi->mdma0.State != HAL_MDMA_STATE_RESET) { - fb_flags = FB_INVALIDATE; + // One shot DMA transfers must be invalidated. + if (csi->one_shot) { + fb_flags |= FB_INVALIDATE; } - #endif // Wait for a frame to be ready. vbuffer_t *buffer = NULL; @@ -743,12 +737,10 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } } - #if USE_DMA // In JPEG 3 mode, the transfer must be aborted as it waits for data indefinitely. - if ((csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3)) { + if (!csi->mipi_if && (csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3)) { omv_csi_abort(csi, true, false); } - #endif // We're done receiving data. #if defined(OMV_CSI_FSYNC_PIN) @@ -797,14 +789,12 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { size = buffer->offset; } else { // Offset is the number of length-size transfers performed. - size = buffer->offset * length; + size = buffer->offset * csi->dma_size / 2; // The DMA counter holds the number of bytes per transfer. - #if USE_DMA - if (__HAL_DMA_GET_COUNTER(&csi->dma)) { + if (!csi->mipi_if && __HAL_DMA_GET_COUNTER(&csi->dma)) { // Add in the uncompleted transfer length. - size += ((length / sizeof(uint32_t)) - __HAL_DMA_GET_COUNTER(&csi->dma)) * sizeof(uint32_t); + size += ((csi->dma_size / 2) - __HAL_DMA_GET_COUNTER(&csi->dma)) * 4; } - #endif } // Clean trailing data after 0xFFD9 at the end of the jpeg byte stream. fb->pixfmt = PIXFORMAT_JPEG; @@ -819,7 +809,7 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { framebuffer_init_image(fb, image); #if USE_DCMIPP if (csi->raw_output) { - float luminance = stm_isp_update_awb(&csi->dcmi, DCMIPP_PIPE, w * h); + float luminance = stm_isp_update_awb(&csi->dcmipp, DCMIPP_PIPE, w * h); if (csi->ioctl) { omv_csi_ioctl(csi, OMV_CSI_IOCTL_UPDATE_AGC_AEC, fast_floorf(luminance)); } diff --git a/ports/stm32/omv_portconfig.h b/ports/stm32/omv_portconfig.h index 14680062f..7dec1c7f1 100644 --- a/ports/stm32/omv_portconfig.h +++ b/ports/stm32/omv_portconfig.h @@ -139,19 +139,24 @@ typedef I2C_HandleTypeDef *omv_i2c_dev_t; #if defined(STM32N6) -#define OMV_CSI_PORT_BITS \ - struct { \ - DCMIPP_HandleTypeDef dcmi; \ +#define OMV_CSI_PORT_BITS_DCMIPP \ + struct { \ + DCMIPP_HandleTypeDef dcmipp; \ }; #else +#define OMV_CSI_PORT_BITS_DCMIPP +#endif + #define OMV_CSI_PORT_BITS \ struct { \ + uint32_t dma_size; \ + bool one_shot; \ DMA_HandleTypeDef dma; \ IRQn_Type dma_irqn; \ DCMI_HandleTypeDef dcmi; \ OMV_CSI_PORT_BITS_MDMA \ + OMV_CSI_PORT_BITS_DCMIPP \ }; -#endif #define OMV_CSI_CLK_PORT_BITS \ struct { \ From cb0b0bc224ef20b0f90bf10c7fceee6c27581c0a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 8 Jul 2025 19:38:52 +0200 Subject: [PATCH 6/8] ports/stm32: Add timer helper functions. Signed-off-by: iabdalkader --- ports/stm32/stm_pwm.c | 123 ++++++++++++++++++++++++++++++++++++++++++ ports/stm32/stm_pwm.h | 37 +++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 ports/stm32/stm_pwm.c create mode 100644 ports/stm32/stm_pwm.h diff --git a/ports/stm32/stm_pwm.c b/ports/stm32/stm_pwm.c new file mode 100644 index 000000000..f884cdad9 --- /dev/null +++ b/ports/stm32/stm_pwm.c @@ -0,0 +1,123 @@ +#include +#include +#include +#include STM32_HAL_H +#include "fmath.h" +#include "stm_pwm.h" + +typedef struct _tim_info { + uint32_t period; + uint32_t pulse; +} tim_info_t; + +static uint32_t stm_tim_get_source_clock(TIM_TypeDef *inst) { + uint32_t source = 0; + #if defined (STM32F4) || defined(STM32F7) || defined(STM32H7) + uintptr_t base = ((uintptr_t) inst) & 0xFFFF0000u; + #endif + + #if defined (STM32F4) || defined(STM32F7) + // Timer clock on F4, F7, H7 == APBx * 2. + if (base == APB1PERIPH_BASE) { + source = HAL_RCC_GetPCLK1Freq() * 2; + } else if (base == APB2PERIPH_BASE) { + source = HAL_RCC_GetPCLK2Freq() * 2; + } + #elif defined(STM32H7) + // Timer clock on F4, F7, H7 == APBx * 2. + if (base == D2_APB1PERIPH_BASE) { + source = HAL_RCC_GetPCLK1Freq() * 2; + } else if (base == D2_APB2PERIPH_BASE) { + source = HAL_RCC_GetPCLK2Freq() * 2; + } + #elif defined(STM32N6) + source = HAL_RCC_GetSysClockFreq() >> LL_RCC_GetTIMPrescaler(); + #endif + + return source; +} + +static void stm_tim_calc_period_pulse(TIM_TypeDef *inst, uint32_t frequency, + uint32_t *period, uint32_t *pulse) { + uint32_t tclk = stm_tim_get_source_clock(inst); + + *period = fast_ceilf(tclk / ((float) frequency)) - 1; + *pulse = (*period + 1) / 2; +} + +int stm_pwm_start(TIM_HandleTypeDef *tim, TIM_TypeDef *inst, uint32_t channel, uint32_t frequency) { + if (frequency == 0) { + // If frequency == 0, stop the timer. + stm_pwm_stop(tim, channel); + } else if (tim->Instance) { + // The timer has been initialized, update the frequency and return. + if (stm_pwm_set_frequency(tim, channel, frequency)) { + return -1; + } + } else { + // Otherwise, initialize timer and start it. + uint32_t period, pulse; + + // Calculate period and pulse. + stm_tim_calc_period_pulse(inst, frequency, &period, &pulse); + + // Timer base configuration + tim->Instance = inst; + tim->Init.Period = period; + tim->Init.Prescaler = 0; + tim->Init.CounterMode = TIM_COUNTERMODE_UP; + tim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + tim->Init.RepetitionCounter = 0; + tim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; + + // Timer channel configuration + TIM_OC_InitTypeDef TIMOCHandle; + TIMOCHandle.Pulse = pulse; + TIMOCHandle.OCMode = TIM_OCMODE_PWM1; + TIMOCHandle.OCPolarity = TIM_OCPOLARITY_HIGH; + TIMOCHandle.OCNPolarity = TIM_OCNPOLARITY_HIGH; + TIMOCHandle.OCFastMode = TIM_OCFAST_DISABLE; + TIMOCHandle.OCIdleState = TIM_OCIDLESTATE_RESET; + TIMOCHandle.OCNIdleState = TIM_OCNIDLESTATE_RESET; + + if (HAL_TIM_PWM_Init(tim) != HAL_OK || + HAL_TIM_PWM_ConfigChannel(tim, &TIMOCHandle, channel) != HAL_OK || + HAL_TIM_PWM_Start(tim, channel != HAL_OK)) { + return -1; + } + } + return 0; +} + +int stm_pwm_stop(TIM_HandleTypeDef *tim, uint32_t channel) { + if (tim->Instance) { + HAL_TIM_PWM_Stop(tim, channel); + HAL_TIM_PWM_DeInit(tim); + memset(tim, 0, sizeof(TIM_HandleTypeDef)); + } + return 0; +} + +int stm_pwm_set_frequency(TIM_HandleTypeDef *tim, uint32_t channel, uint32_t frequency) { + uint32_t period, pulse; + + if (tim->Instance) { + // Calculate period and pulse. + stm_tim_calc_period_pulse(tim->Instance, frequency, &period, &pulse); + + __HAL_TIM_SET_AUTORELOAD(tim, period); + __HAL_TIM_SET_COMPARE(tim, channel, pulse); + } + + return 0; +} + +uint32_t stm_pwm_get_frequency(TIM_HandleTypeDef *tim, uint32_t channel) { + if (tim->Instance) { + uint32_t tclk = stm_tim_get_source_clock(tim->Instance); + return tclk / (tim->Init.Period + 1); + } + + return 0; +} + diff --git a/ports/stm32/stm_pwm.h b/ports/stm32/stm_pwm.h new file mode 100644 index 000000000..a70cfe5d0 --- /dev/null +++ b/ports/stm32/stm_pwm.h @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2023-2024 OpenMV, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * STM32 Timer helper functions. + */ +#ifndef __STM_TIM_H__ +#define __STM_TIM_H__ +#include +#include +#include STM32_HAL_H + +int stm_pwm_start(TIM_HandleTypeDef *tim, TIM_TypeDef *inst, uint32_t channel, uint32_t frequency); +int stm_pwm_stop(TIM_HandleTypeDef *tim, uint32_t channel); +uint32_t stm_pwm_get_frequency(TIM_HandleTypeDef *tim, uint32_t channel); +int stm_pwm_set_frequency(TIM_HandleTypeDef *tim, uint32_t channel, uint32_t frequency); + +#endif // __STM_TIM_H__ From 58fe4f33b5bc2f2931e072cfe654471307f18baa Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 8 Jul 2025 19:39:12 +0200 Subject: [PATCH 7/8] ports/stm32: Refactor CSI PWM config. Signed-off-by: iabdalkader --- ports/stm32/omv_csi.c | 58 ++++++------------------------------------- 1 file changed, 7 insertions(+), 51 deletions(-) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 65da2eec5..2ea7aca96 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -43,6 +43,7 @@ #include "omv_csi.h" #include "stm_dma.h" #include "stm_isp.h" +#include "stm_pwm.h" #if defined(DCMIPP) #define USE_DCMIPP (1) @@ -317,66 +318,21 @@ static uint32_t stm_clk_get_frequency(omv_clk_t *clk) { if (!clk->tim.Instance) { return 0; } - return (OMV_CSI_TIM_PCLK_FREQ() * 2) / (clk->tim.Init.Period + 1); + return stm_pwm_get_frequency(&clk->tim, OMV_CSI_TIM_CHANNEL); } static int stm_clk_set_frequency(omv_clk_t *clk, uint32_t frequency) { - #if (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_TIM) - if (frequency == 0) { - if (clk->tim.Init.Period) { - HAL_TIM_PWM_Stop(&clk->tim, OMV_CSI_TIM_CHANNEL); - HAL_TIM_PWM_DeInit(&clk->tim); - memset(&clk->tim, 0, sizeof(clk->tim)); - } - return 0; - } - - clk->tim.Instance = OMV_CSI_TIM; - - // TCLK (PCLK * 2) - int tclk = OMV_CSI_TIM_PCLK_FREQ() * 2; - - // Find highest possible frequency under requested. - int period = fast_ceilf(tclk / ((float) frequency)) - 1; - int pulse = (period + 1) / 2; - - if (clk->tim.Init.Period && (clk->tim.Init.Period != period)) { - // __HAL_TIM_SET_AUTORELOAD sets clk->tim.Init.Period... - __HAL_TIM_SET_AUTORELOAD(&clk->tim, period); - __HAL_TIM_SET_COMPARE(&clk->tim, OMV_CSI_TIM_CHANNEL, pulse); - return 0; - } - - /* Timer base configuration */ - clk->tim.Init.Period = period; - clk->tim.Init.Prescaler = 0; - clk->tim.Init.CounterMode = TIM_COUNTERMODE_UP; - clk->tim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - clk->tim.Init.RepetitionCounter = 0; - clk->tim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; - - /* Timer channel configuration */ - TIM_OC_InitTypeDef TIMOCHandle; - TIMOCHandle.Pulse = pulse; - TIMOCHandle.OCMode = TIM_OCMODE_PWM1; - TIMOCHandle.OCPolarity = TIM_OCPOLARITY_HIGH; - TIMOCHandle.OCNPolarity = TIM_OCNPOLARITY_HIGH; - TIMOCHandle.OCFastMode = TIM_OCFAST_DISABLE; - TIMOCHandle.OCIdleState = TIM_OCIDLESTATE_RESET; - TIMOCHandle.OCNIdleState = TIM_OCNIDLESTATE_RESET; - - if ((HAL_TIM_PWM_Init(&clk->tim) != HAL_OK) - || (HAL_TIM_PWM_ConfigChannel(&clk->tim, &TIMOCHandle, OMV_CSI_TIM_CHANNEL) != HAL_OK) - || (HAL_TIM_PWM_Start(&clk->tim, OMV_CSI_TIM_CHANNEL) != HAL_OK)) { - return OMV_CSI_ERROR_TIM_INIT_FAILED; - } - #elif (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_MCO) + #if (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_MCO) // Pass through the MCO1 clock with source input set to HSE (12MHz). // Note MCO1 is multiplexed on OPENMV2/TIM1 only. HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1); #elif (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_OSC) // An external oscillator is used for the csi clock. // Configure and enable external oscillator if needed. + #elif (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_TIM) + if (stm_pwm_start(&clk->tim, OMV_CSI_TIM, OMV_CSI_TIM_CHANNEL, frequency)) { + return OMV_CSI_ERROR_TIM_INIT_FAILED; + } #else #error "OMV_CSI_CLK_SOURCE is not set!" #endif // (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_TIM) From ba500065dee12954f155c84e1dab0dc22dc6248c Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 8 Jul 2025 19:38:31 +0200 Subject: [PATCH 8/8] boards/all: Remove unused TIM_PCLK macro. Signed-off-by: iabdalkader --- boards/ARDUINO_GIGA/omv_boardconfig.h | 1 - boards/ARDUINO_NICLA_VISION/omv_boardconfig.h | 1 - boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h | 1 - boards/OPENMV2/omv_boardconfig.h | 1 - boards/OPENMV3/omv_boardconfig.h | 1 - boards/OPENMV4/omv_boardconfig.h | 1 - boards/OPENMV4P/omv_boardconfig.h | 1 - boards/OPENMVPT/omv_boardconfig.h | 1 - boards/OPENMV_N6/omv_boardconfig.h | 1 - 9 files changed, 9 deletions(-) diff --git a/boards/ARDUINO_GIGA/omv_boardconfig.h b/boards/ARDUINO_GIGA/omv_boardconfig.h index 10d7098d9..b36add445 100644 --- a/boards/ARDUINO_GIGA/omv_boardconfig.h +++ b/boards/ARDUINO_GIGA/omv_boardconfig.h @@ -220,7 +220,6 @@ #define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() -#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_DMA_CHANNEL (DMA2_Stream1) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_MEMCPY_ENABLE (1) diff --git a/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h b/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h index f7037a70c..1913afde3 100644 --- a/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h +++ b/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h @@ -217,7 +217,6 @@ #define OMV_CSI_TIM_CLK_DISABLE() __TIM3_CLK_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM3_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM3_CLK_SLEEP_DISABLE() -#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK1Freq() #define OMV_CSI_DMA_CHANNEL (DMA2_Stream1) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_MEMCPY_ENABLE (1) diff --git a/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h b/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h index 30876d7a7..dbb96825e 100644 --- a/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h +++ b/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h @@ -222,7 +222,6 @@ #define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() -#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_DMA_CHANNEL (DMA2_Stream1) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_MEMCPY_ENABLE (1) diff --git a/boards/OPENMV2/omv_boardconfig.h b/boards/OPENMV2/omv_boardconfig.h index f745ddd77..032ab8078 100644 --- a/boards/OPENMV2/omv_boardconfig.h +++ b/boards/OPENMV2/omv_boardconfig.h @@ -147,7 +147,6 @@ #define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() -#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_DMA_CHANNEL (DMA2_Stream1) #define OMV_CSI_DMA_REQUEST (DMA_CHANNEL_1) #define OMV_CSI_HW_CROP_ENABLE (1) diff --git a/boards/OPENMV3/omv_boardconfig.h b/boards/OPENMV3/omv_boardconfig.h index 26fa29a78..128ff57fa 100644 --- a/boards/OPENMV3/omv_boardconfig.h +++ b/boards/OPENMV3/omv_boardconfig.h @@ -147,7 +147,6 @@ #define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() -#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_DMA_CHANNEL (DMA2_Stream1) #define OMV_CSI_DMA_REQUEST (DMA_CHANNEL_1) #define OMV_CSI_HW_CROP_ENABLE (1) diff --git a/boards/OPENMV4/omv_boardconfig.h b/boards/OPENMV4/omv_boardconfig.h index 47f751a62..51f439195 100644 --- a/boards/OPENMV4/omv_boardconfig.h +++ b/boards/OPENMV4/omv_boardconfig.h @@ -214,7 +214,6 @@ #define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() -#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_DMA_CHANNEL (DMA2_Stream1) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_MEMCPY_ENABLE (1) diff --git a/boards/OPENMV4P/omv_boardconfig.h b/boards/OPENMV4P/omv_boardconfig.h index 3be8f3014..f343f6699 100644 --- a/boards/OPENMV4P/omv_boardconfig.h +++ b/boards/OPENMV4P/omv_boardconfig.h @@ -213,7 +213,6 @@ #define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() -#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_DMA_CHANNEL (DMA2_Stream1) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_MEMCPY_ENABLE (1) diff --git a/boards/OPENMVPT/omv_boardconfig.h b/boards/OPENMVPT/omv_boardconfig.h index 966a79bbb..642f53d19 100644 --- a/boards/OPENMVPT/omv_boardconfig.h +++ b/boards/OPENMVPT/omv_boardconfig.h @@ -205,7 +205,6 @@ #define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() -#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_DMA_CHANNEL (DMA2_Stream1) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_MEMCPY_ENABLE (1) diff --git a/boards/OPENMV_N6/omv_boardconfig.h b/boards/OPENMV_N6/omv_boardconfig.h index 8e3eab133..ad7dad9ff 100644 --- a/boards/OPENMV_N6/omv_boardconfig.h +++ b/boards/OPENMV_N6/omv_boardconfig.h @@ -297,7 +297,6 @@ #define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() -#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_DMA_CHANNEL (HPDMA1_Channel12) #define OMV_CSI_DMA_REQUEST (HPDMA1_REQUEST_DCMI_PSSI) #define OMV_CSI_DMA_MEMCPY_ENABLE (0)