mirror of
https://github.com/openmv/openmv.git
synced 2025-09-26 23:09:13 +08:00
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
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:
commit
05aedec7cb
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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__
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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; 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) {
|
||||
// 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; 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;
|
||||
}
|
||||
|
||||
// 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; i<DCMIPP_NUM_OF_PIPES; i++) {
|
||||
csi->dcmi.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));
|
||||
}
|
||||
|
@ -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 { \
|
||||
|
@ -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
|
||||
|
||||
|
123
ports/stm32/stm_pwm.c
Normal file
123
ports/stm32/stm_pwm.c
Normal 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
37
ports/stm32/stm_pwm.h
Normal 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__
|
Loading…
Reference in New Issue
Block a user