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) 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__ 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 */ diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 52def82ac..2ea7aca96 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -43,57 +43,71 @@ #include "omv_csi.h" #include "stm_dma.h" #include "stm_isp.h" +#include "stm_pwm.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 + +#ifndef OMV_CSI_LINE_ALIGNMENT +#define OMV_CSI_LINE_ALIGNMENT (16) +#endif + +typedef enum { + CSI_HANDLE_DCMI = 0, + CSI_HANDLE_DCMIPP = 1, +} csi_handle_t; extern uint8_t _line_buf; -extern uint32_t hal_get_exti_gpio(uint32_t line); +// Stores the CSI handle associated with DCMI/DCMIPP. +static omv_csi_t *stm_csi_all[2] = { 0 }; + +#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 -#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); - HAL_DCMIPP_CSI_IRQHandler(&csi->dcmi); + omv_csi_t *csi = stm_csi_all[CSI_HANDLE_DCMIPP]; + HAL_DCMIPP_CSI_IRQHandler(&csi->dcmipp); } void DCMIPP_IRQHandler(void) { - omv_csi_t *csi = omv_csi_get(-1); - HAL_DCMIPP_IRQHandler(&csi->dcmi); + omv_csi_t *csi = stm_csi_all[CSI_HANDLE_DCMIPP]; + HAL_DCMIPP_IRQHandler(&csi->dcmipp); } #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); @@ -104,193 +118,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; - // Configure and enable DCMI IRQ Channel - NVIC_SetPriority(DCMIPP_IRQn, IRQ_PRI_DCMI); - HAL_NVIC_EnableIRQ(DCMIPP_IRQn); + // Link the DMA handle to the DCMI handle + __HAL_LINKDMA(&csi->dcmi, DMA_Handle, csi->dma); - // 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; + // Initialize the DCMI + HAL_DCMI_DeInit(&csi->dcmi); + if (HAL_DCMI_Init(&csi->dcmi) != HAL_OK) { + return OMV_CSI_ERROR_CSI_INIT_FAILED; + } - // Link the DMA handle to the DCMI handle - __HAL_LINKDMA(&csi->dcmi, DMA_Handle, csi->dma); + // Store CSI handle used for DCMI + stm_csi_all[CSI_HANDLE_DCMI] = csi; - // Initialize the DCMI - HAL_DCMI_DeInit(&csi->dcmi); - if (HAL_DCMI_Init(&csi->dcmi) != HAL_OK) { - return -1; - } + // 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; - // 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 + HAL_DCMIPP_DeInit(&csi->dcmipp); + if (HAL_DCMIPP_Init(&csi->dcmipp) != HAL_OK) { + return OMV_CSI_ERROR_CSI_INIT_FAILED; + } - // 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; } @@ -308,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 -1; - } - #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) @@ -388,29 +353,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 *dcmipp, uint32_t pipe) { -#endif - omv_csi_t *csi = omv_csi_get(-1); +static void stm_csi_frame_event(omv_csi_t *csi, uint32_t pipe) { framebuffer_t *fb = csi->fb; #if USE_MDMA @@ -438,24 +396,36 @@ void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t p 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(dcmipp, 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 DCMI_DMAConvCpltUser(uint32_t addr) { - omv_csi_t *csi = omv_csi_get(-1); +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; // Throttle frames to match the current frame rate. @@ -478,14 +448,14 @@ void DCMI_DMAConvCpltUser(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; @@ -537,10 +507,9 @@ void DCMI_DMAConvCpltUser(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) { @@ -559,23 +528,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. @@ -584,111 +553,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. @@ -707,12 +673,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; @@ -729,12 +693,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) @@ -783,14 +745,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; @@ -805,7 +765,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 { \ 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 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__