Merge pull request #2757 from openmv/decouple_csi_instance
Some checks are pending
🔥 Firmware Build / build-firmware (ARDUINO_GIGA) (push) Waiting to run
🔥 Firmware Build / build-firmware (ARDUINO_NANO_33_BLE_SENSE) (push) Waiting to run
🔥 Firmware Build / build-firmware (ARDUINO_NANO_RP2040_CONNECT) (push) Waiting to run
🔥 Firmware Build / build-firmware (ARDUINO_NICLA_VISION) (push) Waiting to run
🔥 Firmware Build / build-firmware (ARDUINO_PORTENTA_H7) (push) Waiting to run
🔥 Firmware Build / build-firmware (DOCKER) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV2) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV3) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV4) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV4P) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMVPT) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV_AE3) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV_RT1060) (push) Waiting to run
🔥 Firmware Build / code-size-report (push) Blocked by required conditions
🔥 Firmware Build / stable-release (push) Blocked by required conditions
🔥 Firmware Build / development-release (push) Blocked by required conditions

ports/stm32: Support DCMI and DCMIPP.
This commit is contained in:
Ibrahim Abdelkader 2025-07-12 18:24:00 +03:00 committed by GitHub
commit 05aedec7cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 530 additions and 465 deletions

View File

@ -220,7 +220,6 @@
#define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #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_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #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_CHANNEL (DMA2_Stream1)
#define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI)
#define OMV_CSI_DMA_MEMCPY_ENABLE (1) #define OMV_CSI_DMA_MEMCPY_ENABLE (1)

View File

@ -217,7 +217,6 @@
#define OMV_CSI_TIM_CLK_DISABLE() __TIM3_CLK_DISABLE() #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_ENABLE() __TIM3_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM3_CLK_SLEEP_DISABLE() #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_CHANNEL (DMA2_Stream1)
#define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI)
#define OMV_CSI_DMA_MEMCPY_ENABLE (1) #define OMV_CSI_DMA_MEMCPY_ENABLE (1)

View File

@ -222,7 +222,6 @@
#define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #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_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #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_CHANNEL (DMA2_Stream1)
#define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI)
#define OMV_CSI_DMA_MEMCPY_ENABLE (1) #define OMV_CSI_DMA_MEMCPY_ENABLE (1)

View File

@ -147,7 +147,6 @@
#define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #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_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #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_CHANNEL (DMA2_Stream1)
#define OMV_CSI_DMA_REQUEST (DMA_CHANNEL_1) #define OMV_CSI_DMA_REQUEST (DMA_CHANNEL_1)
#define OMV_CSI_HW_CROP_ENABLE (1) #define OMV_CSI_HW_CROP_ENABLE (1)

View File

@ -147,7 +147,6 @@
#define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #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_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #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_CHANNEL (DMA2_Stream1)
#define OMV_CSI_DMA_REQUEST (DMA_CHANNEL_1) #define OMV_CSI_DMA_REQUEST (DMA_CHANNEL_1)
#define OMV_CSI_HW_CROP_ENABLE (1) #define OMV_CSI_HW_CROP_ENABLE (1)

View File

@ -214,7 +214,6 @@
#define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #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_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #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_CHANNEL (DMA2_Stream1)
#define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI)
#define OMV_CSI_DMA_MEMCPY_ENABLE (1) #define OMV_CSI_DMA_MEMCPY_ENABLE (1)

View File

@ -213,7 +213,6 @@
#define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #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_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #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_CHANNEL (DMA2_Stream1)
#define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI)
#define OMV_CSI_DMA_MEMCPY_ENABLE (1) #define OMV_CSI_DMA_MEMCPY_ENABLE (1)

View File

@ -205,7 +205,6 @@
#define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #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_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #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_CHANNEL (DMA2_Stream1)
#define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI)
#define OMV_CSI_DMA_MEMCPY_ENABLE (1) #define OMV_CSI_DMA_MEMCPY_ENABLE (1)

View File

@ -297,7 +297,6 @@
#define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE() #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_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #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_CHANNEL (HPDMA1_Channel12)
#define OMV_CSI_DMA_REQUEST (HPDMA1_REQUEST_DCMI_PSSI) #define OMV_CSI_DMA_REQUEST (HPDMA1_REQUEST_DCMI_PSSI)
#define OMV_CSI_DMA_MEMCPY_ENABLE (0) #define OMV_CSI_DMA_MEMCPY_ENABLE (0)

View File

@ -74,4 +74,10 @@
#define OMV_PROFILE_PRINT(F) #define OMV_PROFILE_PRINT(F)
#endif #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__ #endif //__OMV_COMMON_H__

View File

@ -1114,12 +1114,6 @@ HAL_StatusTypeDef HAL_DCMI_UnRegisterCallback(DCMI_HandleTypeDef *hdcmi, HAL_DCM
/** @defgroup DCMI_Private_Functions DCMI Private Functions /** @defgroup DCMI_Private_Functions DCMI Private Functions
* @{ * @{
*/ */
__weak void DCMI_DMAConvCpltUser(uint32_t addr)
{
}
/** /**
* @brief DMA conversion complete callback. * @brief DMA conversion complete callback.
* @param hdma pointer to a DMA_HandleTypeDef structure that contains * @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) static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma)
{ {
DCMI_HandleTypeDef* hdcmi; 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 = (DCMI_HandleTypeDef*) ((DMA_HandleTypeDef*)hdma)->Parent;
hdcmi->State= HAL_DCMI_STATE_READY; stream = (DMA_Stream_TypeDef*) (hdcmi->DMA_Handle->Instance);
// Note: we don't need to adjust memory addresses because they stay the same. // Note: we don't need to adjust memory addresses because they stay the same.
if(hdcmi->XferCount != 0) { if (hdcmi->XferCount != 0) {
hdcmi->XferCount--; 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 // Current traget is M0 call user callback with M1
DCMI_DMAConvCpltUser(hdcmi->DMA_Handle->Instance->M1AR); DCMI_DMAConvCpltUser(hdcmi, stream->M1AR);
} else { } else {
// Current traget is M1 call user callback with M0 // 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 */ /* Check if the frame is transferred */
if(hdcmi->XferCount == 0) { if (hdcmi->XferCount == 0) {
/* Reload XferCount */ /* Reload XferCount */
hdcmi->XferCount = hdcmi->XferTransferNumber; hdcmi->XferCount = hdcmi->XferTransferNumber;
/* Enable the Frame interrupt */ /* Re-enable frame interrupt */
__HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME); __HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME);
/* When snapshot mode, set dcmi state to ready */ /* When snapshot mode, set dcmi state to ready */

View File

@ -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) static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma)
{ {
DCMI_HandleTypeDef* hdcmi; 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. // Note: we don't need to adjust memory addresses because they stay the same.
if (hdcmi->XferCount != 0) { if (hdcmi->XferCount != 0) {
hdcmi->XferCount--; 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 // Current traget is M0 call user callback with M1
DCMI_DMAConvCpltUser(hdcmi->DMA_Handle->Instance->M1AR); DCMI_DMAConvCpltUser(hdcmi, stream->M1AR);
} else { } else {
// Current traget is M1 call user callback with M0 // 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 */ /* Check if the frame is transferred */
if(hdcmi->XferCount == 0) { if (hdcmi->XferCount == 0) {
/* Reload XferCount */ /* Reload XferCount */
hdcmi->XferCount = hdcmi->XferTransferNumber; hdcmi->XferCount = hdcmi->XferTransferNumber;
/* Enable the Frame interrupt */ /* Re-enable frame interrupt */
__HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME); __HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME);
/* When snapshot mode, set dcmi state to ready */ /* When snapshot mode, set dcmi state to ready */
@ -1196,6 +1196,7 @@ static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma)
} }
} }
} }
/** /**
* @brief DMA error callback * @brief DMA error callback
* @param hdma pointer to a DMA_HandleTypeDef structure that contains * @param hdma pointer to a DMA_HandleTypeDef structure that contains

View File

@ -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; DCMI_HandleTypeDef* hdcmi;
DMA_Stream_TypeDef *stream; DMA_Stream_TypeDef *stream;
extern void DCMI_DMAConvCpltUser(DCMI_HandleTypeDef* hdcmi, uint32_t addr);
hdcmi = (DCMI_HandleTypeDef*) ((DMA_HandleTypeDef*)hdma)->Parent; hdcmi = (DCMI_HandleTypeDef*) ((DMA_HandleTypeDef*)hdma)->Parent;
stream = (DMA_Stream_TypeDef*) (hdcmi->DMA_Handle->Instance); 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) { if ((stream->CR & DMA_SxCR_CT) == 0) {
// Current traget is M0 call user callback with M1 // Current traget is M0 call user callback with M1
DCMI_DMAConvCpltUser(stream->M1AR); DCMI_DMAConvCpltUser(hdcmi, stream->M1AR);
} else { } else {
// Current traget is M1 call user callback with M0 // Current traget is M1 call user callback with M0
DCMI_DMAConvCpltUser(stream->M0AR); DCMI_DMAConvCpltUser(hdcmi, stream->M0AR);
} }
/* Check if the frame is transferred */ /* Check if the frame is transferred */

View File

@ -43,57 +43,71 @@
#include "omv_csi.h" #include "omv_csi.h"
#include "stm_dma.h" #include "stm_dma.h"
#include "stm_isp.h" #include "stm_isp.h"
#include "stm_pwm.h"
#if defined(DMA2) #if defined(DCMIPP)
#define USE_DMA (1) #define USE_DCMIPP (1)
#define DMA_MAX_TRANSFER (0xFFFFU * 4U) // 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 #endif
#if defined(OMV_MDMA_CHANNEL_DCMI_0) #if defined(OMV_MDMA_CHANNEL_DCMI_0)
#define USE_MDMA (1) #define USE_MDMA (1)
#endif #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 #ifndef OMV_CSI_DMA_XFER_PORTS
#define OMV_CSI_DMA_XFER_PORTS (0) #define OMV_CSI_DMA_XFER_PORTS (0)
#endif #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 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) { 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); HAL_DCMI_IRQHandler(&csi->dcmi);
} }
#endif
#if USE_DCMIPP #if USE_DCMIPP
void CSI_IRQHandler(void) { 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); HAL_DCMIPP_CSI_IRQHandler(&csi->dcmipp);
} }
void DCMIPP_IRQHandler(void) { 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); HAL_DCMIPP_IRQHandler(&csi->dcmipp);
} }
#endif #endif
#if USE_MDMA #if USE_MDMA
void omv_csi_mdma_irq_handler(void) { 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)) { if (MDMA->GISR0 & (1 << OMV_MDMA_CHANNEL_DCMI_0)) {
HAL_MDMA_IRQHandler(&csi->mdma0); HAL_MDMA_IRQHandler(&csi->mdma0);
@ -104,193 +118,189 @@ void omv_csi_mdma_irq_handler(void) {
} }
#endif #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) { static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
if (config == OMV_CSI_CONFIG_INIT) { 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 defined(STM32N6)
if (stm_dma_init(&csi->dma, OMV_CSI_DMA_CHANNEL, OMV_CSI_DMA_REQUEST, // Initialize DMA in circular mode.
DMA_PERIPH_TO_MEMORY, 4, 4, OMV_CSI_DMA_XFER_PORTS, if (stm_dma_ll_init(&csi->dma, &dma_queue, dma_nodes,
&stm_dma_csi_init, true)) { OMV_ARRAY_SIZE(dma_nodes), OMV_CSI_DMA_LIST_PORTS)) {
return OMV_CSI_ERROR_DMA_INIT_FAILED; return OMV_CSI_ERROR_CSI_INIT_FAILED;
} }
#endif
// Set DMA IRQ handle // Set DMA IRQ handle
stm_dma_set_irq_descr(OMV_CSI_DMA_CHANNEL, &csi->dma); 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);
#if USE_MDMA // Configure the DMA IRQ Channel
csi->mdma0.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_0); csi->dma_irqn = stm_dma_channel_to_irqn(OMV_CSI_DMA_CHANNEL);
csi->mdma1.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_1); NVIC_SetPriority(csi->dma_irqn, IRQ_PRI_DMA21);
#endif
#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. csi->dcmi.Instance = DCMI;
#if USE_DCMIPP csi->dcmi.Init.VSPolarity = csi->vsync_pol ? DCMI_VSPOLARITY_HIGH : DCMI_VSPOLARITY_LOW;
// Initialize the DCMIPP csi->dcmi.Init.HSPolarity = csi->hsync_pol ? DCMI_HSPOLARITY_HIGH : DCMI_HSPOLARITY_LOW;
csi->dcmi.Instance = DCMIPP; csi->dcmi.Init.PCKPolarity = csi->pixck_pol ? DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING;
if (HAL_DCMIPP_Init(&csi->dcmi) != HAL_OK) { csi->dcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
return -1; 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 // Link the DMA handle to the DCMI handle
NVIC_SetPriority(DCMIPP_IRQn, IRQ_PRI_DCMI); __HAL_LINKDMA(&csi->dcmi, DMA_Handle, csi->dma);
HAL_NVIC_EnableIRQ(DCMIPP_IRQn);
// Configure and enable CSI IRQ Channel // Initialize the DCMI
NVIC_SetPriority(CSI_IRQn, IRQ_PRI_DCMI); HAL_DCMI_DeInit(&csi->dcmi);
HAL_NVIC_EnableIRQ(CSI_IRQn); if (HAL_DCMI_Init(&csi->dcmi) != HAL_OK) {
#else return OMV_CSI_ERROR_CSI_INIT_FAILED;
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;
// Link the DMA handle to the DCMI handle // Store CSI handle used for DCMI
__HAL_LINKDMA(&csi->dcmi, DMA_Handle, csi->dma); stm_csi_all[CSI_HANDLE_DCMI] = csi;
// Initialize the DCMI // Configure and enable DCMI IRQ Channel
HAL_DCMI_DeInit(&csi->dcmi); NVIC_SetPriority(DCMI_IRQn, IRQ_PRI_DCMI);
if (HAL_DCMI_Init(&csi->dcmi) != HAL_OK) { HAL_NVIC_EnableIRQ(DCMI_IRQn);
return -1; } else {
} #if USE_DCMIPP
// Initialize the DCMIPP
csi->dcmipp.Instance = DCMIPP;
// Configure and enable DCMI IRQ Channel HAL_DCMIPP_DeInit(&csi->dcmipp);
NVIC_SetPriority(DCMI_IRQn, IRQ_PRI_DCMI); if (HAL_DCMIPP_Init(&csi->dcmipp) != HAL_OK) {
HAL_NVIC_EnableIRQ(DCMI_IRQn); return OMV_CSI_ERROR_CSI_INIT_FAILED;
#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 // Select and configure the DCMIPP source.
// that abort() doesn't reset the state unless the pipe is active.
csi->dcmi.State = HAL_DCMIPP_STATE_INIT;
for (size_t i=0; i<DCMIPP_NUM_OF_PIPES; i++) {
csi->dcmi.PipeState[i] = HAL_DCMIPP_PIPE_STATE_RESET;
}
// Select and configure the DCMIPP source.
if (csi->mipi_if) {
DCMIPP_CSI_ConfTypeDef scfg = { DCMIPP_CSI_ConfTypeDef scfg = {
.NumberOfLanes = DCMIPP_CSI_TWO_DATA_LANES, .NumberOfLanes = DCMIPP_CSI_TWO_DATA_LANES,
.DataLaneMapping = DCMIPP_CSI_PHYSICAL_DATA_LANES, .DataLaneMapping = DCMIPP_CSI_PHYSICAL_DATA_LANES,
.PHYBitrate = (csi->mipi_brate == 850) ? DCMIPP_CSI_PHY_BT_850 : DCMIPP_CSI_PHY_BT_1200, .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; return OMV_CSI_ERROR_CSI_INIT_FAILED;
} }
// Configure CSI virtual channel and pipe. // Configure CSI virtual channel and pipe.
DCMIPP_CSI_PIPE_ConfTypeDef pcfg = { DCMIPP_CSI_PIPE_ConfTypeDef csi_pcfg = {
.DataTypeMode = DCMIPP_DTMODE_DTIDA, .DataTypeMode = DCMIPP_DTMODE_DTIDA,
.DataTypeIDA = DCMIPP_DT_RAW10, .DataTypeIDA = DCMIPP_DT_RAW10,
.DataTypeIDB = DCMIPP_DT_RAW10, .DataTypeIDB = DCMIPP_DT_RAW10,
}; };
if (HAL_DCMIPP_CSI_SetVCConfig(&csi->dcmi, DCMIPP_VIRTUAL_CHANNEL0, if (HAL_DCMIPP_CSI_SetVCConfig(&csi->dcmipp, DCMIPP_VIRTUAL_CHANNEL0,
DCMIPP_CSI_DT_BPP10) != HAL_OK) { DCMIPP_CSI_DT_BPP10) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED; 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 (HAL_DCMIPP_CSI_PIPE_SetConfig(&csi->dcmipp, DCMIPP_PIPE, &csi_pcfg) != HAL_OK) {
if (stm_isp_config_pipeline(&csi->dcmi, DCMIPP_PIPE, csi->pixformat, csi->raw_output)) { return OMV_CSI_ERROR_CSI_INIT_FAILED;
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; i<DCMIPP_NUM_OF_PIPES; i++) {
csi->dcmipp.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; return 0;
} }
// Stop the DCMI from generating more DMA requests, and disable the DMA. // 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) { 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; 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) { if (!csi->mipi_if) {
HAL_DCMIPP_PIPE_Stop(&csi->dcmi, DCMIPP_PIPE); DCMI->CR &= ~DCMI_CR_ENABLE;
} else { while (DCMI->CR & DCMI_CR_ENABLE);
HAL_DCMIPP_CSI_PIPE_Stop(&csi->dcmi, DCMIPP_PIPE, DCMIPP_VIRTUAL_CHANNEL0);
}
for (size_t i=0; i<DCMIPP_NUM_OF_PIPES; i++) {
csi->dcmi.PipeState[i] = HAL_DCMIPP_PIPE_STATE_RESET;
}
#endif
#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; return 0;
} }
@ -308,66 +318,21 @@ static uint32_t stm_clk_get_frequency(omv_clk_t *clk) {
if (!clk->tim.Instance) { if (!clk->tim.Instance) {
return 0; 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) { static int stm_clk_set_frequency(omv_clk_t *clk, uint32_t frequency) {
#if (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_TIM) #if (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_MCO)
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)
// Pass through the MCO1 clock with source input set to HSE (12MHz). // Pass through the MCO1 clock with source input set to HSE (12MHz).
// Note MCO1 is multiplexed on OPENMV2/TIM1 only. // Note MCO1 is multiplexed on OPENMV2/TIM1 only.
HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1); HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);
#elif (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_OSC) #elif (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_OSC)
// An external oscillator is used for the csi clock. // An external oscillator is used for the csi clock.
// Configure and enable external oscillator if needed. // 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 #else
#error "OMV_CSI_CLK_SOURCE is not set!" #error "OMV_CSI_CLK_SOURCE is not set!"
#endif // (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_TIM) #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; return 0;
} }
#if USE_DCMI
// If the image is cropped by more than 1 word in width, align the line start to a word // 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 // 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. // 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) { static uint32_t get_dcmi_hw_crop(omv_csi_t *csi, uint32_t bytes_per_pixel) {
framebuffer_t *fb = csi->fb; 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; 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 byte_x_offset;
} }
return 0; return 0;
} }
#endif
#if USE_DCMI static void stm_csi_frame_event(omv_csi_t *csi, uint32_t pipe) {
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);
framebuffer_t *fb = csi->fb; framebuffer_t *fb = csi->fb;
#if USE_MDMA #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); csi->frame_cb.fun(csi->frame_cb.arg);
} }
#if USE_DCMIPP #if defined(STM32N6)
// Get the destination buffer address. // Get the destination buffer address.
vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
if (buffer == NULL) { if (buffer == NULL) { // FIFO is FULL
omv_csi_abort(csi, false, false); omv_csi_abort(csi, false, false);
} else { } else if (csi->mipi_if) {
HAL_DCMIPP_PIPE_SetMemoryAddress(dcmipp, pipe, DCMIPP_MEMORY_ADDRESS_0, (uint32_t) buffer->data); 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 void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) {
// This function is called after each line transfer is complete, with a pointer to the stm_csi_frame_event(OMV_CONTAINER_OF(hdcmi, omv_csi_t, dcmi), 0);
// 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. #if USE_DCMIPP
void DCMI_DMAConvCpltUser(uint32_t addr) { void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *hdcmi, uint32_t pipe) {
omv_csi_t *csi = omv_csi_get(-1); 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; framebuffer_t *fb = csi->fb;
// Throttle frames to match the current frame rate. // 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->pixformat == PIXFORMAT_JPEG) {
if (csi->jpg_format == 3) { if (csi->jpg_format == 3) {
// JPEG MODE 3: Variable line width per frame, with the last line potentially shorter and // JPEG MODE 3: Variable line width per frame, with the last line
// no padding. `offset` is incremented once every max transfer, and the DMA counter holds // potentially shorter and no padding. `offset` is incremented once
// the total size. // every max transfer, and the DMA counter holds the total size.
buffer->offset += 1; buffer->offset += 1;
} else if (csi->jpg_format == 4) { } else if (csi->jpg_format == 4) {
// JPEG MODE 4: Fixed width and height per frame. Each line starts with two bytes indicating // JPEG MODE 4: Fixed width and height per frame. Each line starts
// valid data length, followed by image data and optional padding (0xFF). `offset` holds the // with two bytes indicating valid data length, followed by image
// total size. // data and optional padding (0xFF). `offset` holds the total size.
uint16_t size = __REV16(*((uint16_t *) addr)); uint16_t size = __REV16(*((uint16_t *) addr));
if (buffer->offset + size > framebuffer_get_buffer_size(fb)) { if (buffer->offset + size > framebuffer_get_buffer_size(fb)) {
buffer->jpeg_buffer_overflow = true; buffer->jpeg_buffer_overflow = true;
@ -537,10 +507,9 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
omv_csi_copy_line(csi, NULL, src, dst); omv_csi_copy_line(csi, NULL, src, dst);
#endif #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) { static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
uint32_t length = 0;
framebuffer_t *fb = csi->fb; framebuffer_t *fb = csi->fb;
if (csi->pixformat == PIXFORMAT_INVALID) { 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); framebuffer_update_jpeg_buffer(&tmp);
} }
// Ensure that the raw frame fits into the FB. It will be switched from RGB565 to BAYER // Ensure that the raw frame fits into the FB. It will be switched from RGB565
// first to save space before being cropped until it fits. // to BAYER first to save space before being cropped until it fits.
omv_csi_auto_crop_framebuffer(csi); omv_csi_auto_crop_framebuffer(csi);
// Restore frame buffer width and height if they were changed before. BPP is restored later. // Restore frame buffer width and height if they were changed before. BPP is
// Note that JPEG compression is done first on the framebuffer with the user settings. // restored later. Note that JPEG compression is done first on the framebuffer
// with the user settings.
uint32_t w = fb->u; uint32_t w = fb->u;
uint32_t h = fb->v; uint32_t h = fb->v;
// TODO // If DCMI_DMAConvCpltUser() happens before framebuffer_free_current_buffer();
// If DCMI_DMAConvCpltUser() happens before framebuffer_free_current_buffer(); below then the // below then the transfer is stopped and it will be re-enabled again right
// transfer is stopped and it will be re-enabled again right afterwards in the single vbuffer // afterwards in the single vbuffer case.
// case.
framebuffer_free_current_buffer(fb); framebuffer_free_current_buffer(fb);
// Configure and start the capture. // Configure and start the capture.
if (!DCMI_IS_ACTIVE()) { if (!stm_csi_is_active(csi)) {
framebuffer_setup_buffers(fb); framebuffer_setup_buffers(fb);
// Get the destination buffer address. // 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; return OMV_CSI_ERROR_FRAMEBUFFER_ERROR;
} }
#if USE_DCMI if (csi->mipi_if) {
// Setup the size and address of the transfer #if USE_DCMIPP
uint32_t bytes_per_pixel = omv_csi_get_src_bpp(csi); uint32_t bytes_per_pixel = omv_csi_get_dst_bpp(csi);
uint32_t x_crop = get_dcmi_hw_crop(csi, bytes_per_pixel); uint32_t line_width = fb->u * bytes_per_pixel;
uint32_t line_width_bytes = resolution[csi->framesize][0] * bytes_per_pixel;
// Shrink the captured pixel count by one word to allow cropping to fix alignment. if (!line_width ||
if (x_crop) { line_width % OMV_CSI_LINE_ALIGNMENT) {
line_width_bytes -= sizeof(uint32_t); return OMV_CSI_ERROR_INVALID_FRAMESIZE;
}
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 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 || // Configure crop
line_width_bytes % LINE_WIDTH_ALIGNMENT) { DCMIPP_CropConfTypeDef ccfg = {
return OMV_CSI_ERROR_INVALID_FRAMESIZE; .HStart = fb->x,
} .VStart = fb->y,
.HSize = fb->u,
// Configure crop .VSize = fb->v,
DCMIPP_CropConfTypeDef ccfg = { };
.HStart = fb->x, if (HAL_DCMIPP_PIPE_SetCropConfig(&csi->dcmipp, DCMIPP_PIPE, &ccfg) != HAL_OK ||
.VStart = fb->y, HAL_DCMIPP_PIPE_EnableCrop(&csi->dcmipp, DCMIPP_PIPE) != HAL_OK) {
.HSize = fb->u, return OMV_CSI_ERROR_CSI_INIT_FAILED;
.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;
} }
} 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) { (uint32_t) buffer->data, DCMIPP_MODE_CONTINUOUS) != HAL_OK) {
return OMV_CSI_ERROR_CAPTURE_FAILED; 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. // 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; framebuffer_flags_t fb_flags = FB_NO_FLAGS;
#if USE_MDMA // One shot DMA transfers must be invalidated.
// csi->mdma0.State will be HAL_MDMA_STATE_RESET if the MDMA is not initialized. if (csi->one_shot) {
if (csi->mdma0.State != HAL_MDMA_STATE_RESET) { fb_flags |= FB_INVALIDATE;
fb_flags = FB_INVALIDATE;
} }
#endif
// Wait for a frame to be ready. // Wait for a frame to be ready.
vbuffer_t *buffer = NULL; 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. // 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); omv_csi_abort(csi, true, false);
} }
#endif
// We're done receiving data. // We're done receiving data.
#if defined(OMV_CSI_FSYNC_PIN) #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; size = buffer->offset;
} else { } else {
// Offset is the number of length-size transfers performed. // 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. // The DMA counter holds the number of bytes per transfer.
#if USE_DMA if (!csi->mipi_if && __HAL_DMA_GET_COUNTER(&csi->dma)) {
if (__HAL_DMA_GET_COUNTER(&csi->dma)) {
// Add in the uncompleted transfer length. // 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. // Clean trailing data after 0xFFD9 at the end of the jpeg byte stream.
fb->pixfmt = PIXFORMAT_JPEG; 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); framebuffer_init_image(fb, image);
#if USE_DCMIPP #if USE_DCMIPP
if (csi->raw_output) { 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) { if (csi->ioctl) {
omv_csi_ioctl(csi, OMV_CSI_IOCTL_UPDATE_AGC_AEC, fast_floorf(luminance)); omv_csi_ioctl(csi, OMV_CSI_IOCTL_UPDATE_AGC_AEC, fast_floorf(luminance));
} }

View File

@ -139,19 +139,24 @@ typedef I2C_HandleTypeDef *omv_i2c_dev_t;
#if defined(STM32N6) #if defined(STM32N6)
#define OMV_CSI_PORT_BITS \ #define OMV_CSI_PORT_BITS_DCMIPP \
struct { \ struct { \
DCMIPP_HandleTypeDef dcmi; \ DCMIPP_HandleTypeDef dcmipp; \
}; };
#else #else
#define OMV_CSI_PORT_BITS_DCMIPP
#endif
#define OMV_CSI_PORT_BITS \ #define OMV_CSI_PORT_BITS \
struct { \ struct { \
uint32_t dma_size; \
bool one_shot; \
DMA_HandleTypeDef dma; \ DMA_HandleTypeDef dma; \
IRQn_Type dma_irqn; \ IRQn_Type dma_irqn; \
DCMI_HandleTypeDef dcmi; \ DCMI_HandleTypeDef dcmi; \
OMV_CSI_PORT_BITS_MDMA \ OMV_CSI_PORT_BITS_MDMA \
OMV_CSI_PORT_BITS_DCMIPP \
}; };
#endif
#define OMV_CSI_CLK_PORT_BITS \ #define OMV_CSI_CLK_PORT_BITS \
struct { \ struct { \

View File

@ -514,22 +514,6 @@ void HAL_DCMI_MspDeInit(DCMI_HandleTypeDef *hdcmi) {
#if defined(DCMIPP) #if defined(DCMIPP)
void HAL_DCMIPP_MspInit(DCMIPP_HandleTypeDef *hdcmipp) { 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. // Enable DCMIPP clock.
__HAL_RCC_DCMIPP_CLK_ENABLE(); __HAL_RCC_DCMIPP_CLK_ENABLE();
__HAL_RCC_DCMIPP_CLK_SLEEP_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_CLK_SLEEP_ENABLE();
__HAL_RCC_CSI_FORCE_RESET(); __HAL_RCC_CSI_FORCE_RESET();
__HAL_RCC_CSI_RELEASE_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) { 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. // Disable DCMI clock.
__HAL_RCC_DCMIPP_FORCE_RESET(); __HAL_RCC_DCMIPP_FORCE_RESET();
__HAL_RCC_DCMIPP_RELEASE_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_RELEASE_RESET();
__HAL_RCC_CSI_CLK_DISABLE(); __HAL_RCC_CSI_CLK_DISABLE();
__HAL_RCC_CSI_CLK_SLEEP_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 #endif

123
ports/stm32/stm_pwm.c Normal file
View File

@ -0,0 +1,123 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#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;
}

37
ports/stm32/stm_pwm.h Normal file
View File

@ -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 <stdint.h>
#include <stdbool.h>
#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__