mirror of
https://github.com/openmv/openmv.git
synced 2025-09-26 23:09:13 +08:00
Merge pull request #2749 from openmv/refactor_dma_code
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: Refactor DMA code.
This commit is contained in:
commit
5ba66423de
@ -222,6 +222,8 @@
|
||||
#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)
|
||||
#define OMV_CSI_HW_CROP_ENABLE (1)
|
||||
|
||||
|
@ -218,6 +218,8 @@
|
||||
#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)
|
||||
#define OMV_CSI_HW_CROP_ENABLE (1)
|
||||
|
||||
|
@ -224,6 +224,8 @@
|
||||
#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)
|
||||
#define OMV_CSI_HW_CROP_ENABLE (1)
|
||||
|
||||
|
@ -148,6 +148,8 @@
|
||||
#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)
|
||||
|
||||
#define OMV_CSI_D0_PIN (&omv_pin_C6_DCMI)
|
||||
|
@ -148,6 +148,8 @@
|
||||
#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)
|
||||
|
||||
#define OMV_CSI_D0_PIN (&omv_pin_C6_DCMI)
|
||||
|
@ -215,6 +215,8 @@
|
||||
#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)
|
||||
#define OMV_CSI_HW_CROP_ENABLE (1)
|
||||
|
||||
|
@ -215,6 +215,8 @@
|
||||
#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)
|
||||
#define OMV_CSI_HW_CROP_ENABLE (1)
|
||||
|
||||
|
@ -207,6 +207,8 @@
|
||||
#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)
|
||||
#define OMV_CSI_HW_CROP_ENABLE (1)
|
||||
|
||||
|
@ -226,6 +226,8 @@
|
||||
#define OMV_SPI5_DMA_TX_REQUEST (GPDMA1_REQUEST_SPI5_TX)
|
||||
#define OMV_SPI5_DMA_RX_CHANNEL (GPDMA1_Channel9)
|
||||
#define OMV_SPI5_DMA_RX_REQUEST (GPDMA1_REQUEST_SPI5_RX)
|
||||
#define OMV_SPI_DMA_LIST_PORTS (DMA_LINK_ALLOCATED_PORT0)
|
||||
#define OMV_SPI_DMA_XFER_PORTS (DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT1)
|
||||
|
||||
// CSI SPI bus
|
||||
#define OMV_CSI_SPI_ID (OMV_SPI5_ID)
|
||||
@ -259,6 +261,8 @@
|
||||
#define OMV_MDF_FLT0_DMA_STREAM GPDMA1_Channel10
|
||||
#define OMV_MDF_FLT0_DMA_REQUEST GPDMA1_REQUEST_ADF1_FLT0
|
||||
#define OMV_MDF_FLT0_DMA_IRQ GPDMA1_Channel10_IRQn
|
||||
#define OMV_MDF_DMA_LIST_PORTS (DMA_LINK_ALLOCATED_PORT0)
|
||||
#define OMV_MDF_DMA_XFER_PORTS (DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT1)
|
||||
|
||||
// Camera Interface
|
||||
#define OMV_CSI_CLK_SOURCE (OMV_CSI_CLK_SOURCE_TIM)
|
||||
@ -274,6 +278,8 @@
|
||||
#define OMV_CSI_DMA_CHANNEL (HPDMA1_Channel12)
|
||||
#define OMV_CSI_DMA_REQUEST (HPDMA1_REQUEST_DCMI_PSSI)
|
||||
#define OMV_CSI_DMA_MEMCPY_ENABLE (0)
|
||||
#define OMV_CSI_DMA_LIST_PORTS (DMA_LINK_ALLOCATED_PORT0)
|
||||
#define OMV_CSI_DMA_XFER_PORTS (DMA_SRC_ALLOCATED_PORT1 | DMA_DEST_ALLOCATED_PORT0)
|
||||
#define OMV_CSI_HW_CROP_ENABLE (1)
|
||||
#define OMV_CSI_MAX_DEVICES (3)
|
||||
|
||||
|
@ -1,323 +0,0 @@
|
||||
/*
|
||||
* 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 DMA helper functions.
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include STM32_HAL_H
|
||||
#include "py/mphal.h"
|
||||
|
||||
#include "omv_boardconfig.h"
|
||||
#include "omv_common.h"
|
||||
#include "dma_utils.h"
|
||||
|
||||
#if defined(GPDMA1)
|
||||
static DMA_HandleTypeDef *dma_handle[32];
|
||||
#else
|
||||
// Defined in micropython/ports/stm32/dma.c or in uvc/src/main.c
|
||||
extern DMA_HandleTypeDef *dma_handle[16];
|
||||
#endif
|
||||
|
||||
uint8_t dma_utils_channel_to_irqn(void *dma_channel) {
|
||||
if (0) {
|
||||
#if defined(DMA1_Stream0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFFF00) == DMA1_BASE) {
|
||||
return ((DMA_Stream_TypeDef *) dma_channel - DMA1_Stream0) + DMA1_Stream0_IRQn;
|
||||
#endif
|
||||
#if defined(DMA2_Stream0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFFF00) == DMA2_BASE) {
|
||||
return ((DMA_Stream_TypeDef *) dma_channel - DMA2_Stream0) + DMA2_Stream0_IRQn;
|
||||
#endif
|
||||
#if defined(GPDMA1_Channel0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFF000) == GPDMA1_BASE) {
|
||||
return ((DMA_Channel_TypeDef *) dma_channel - GPDMA1_Channel0) + GPDMA1_Channel0_IRQn;
|
||||
#endif
|
||||
#if defined(HPDMA1_Channel0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFF000) == HPDMA1_BASE) {
|
||||
return ((DMA_Channel_TypeDef *) dma_channel - HPDMA1_Channel0) + HPDMA1_Channel0_IRQn;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This returns a DMA ID that can be used to index into the dma_handle
|
||||
// array defined in micropython. Setting a DMA handle in that array allows
|
||||
// DMA IRQ handlers (which are all defined in micropython) to use it.
|
||||
uint8_t dma_utils_channel_to_id(void *dma_channel) {
|
||||
if (0) {
|
||||
#if defined(DMA1_Stream0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFFF00) == DMA1_BASE) {
|
||||
return ((DMA_Stream_TypeDef *) dma_channel - DMA1_Stream0);
|
||||
#endif
|
||||
#if defined(DMA2_Stream0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFFF00) == DMA2_BASE) {
|
||||
return ((DMA_Stream_TypeDef *) dma_channel - DMA2_Stream0) + 8;
|
||||
#endif
|
||||
#if defined(GPDMA1_Channel0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFF000) == GPDMA1_BASE) {
|
||||
return ((DMA_Channel_TypeDef *) dma_channel - GPDMA1_Channel0);
|
||||
#endif
|
||||
#if defined(HPDMA1_Channel0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFF000) == HPDMA1_BASE) {
|
||||
return ((DMA_Channel_TypeDef *) dma_channel - HPDMA1_Channel0) + 16;
|
||||
#endif
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dma_utils_set_irq_descr(void *dma_channel, DMA_HandleTypeDef *dma_descr) {
|
||||
uint8_t dma_id = dma_utils_channel_to_id(dma_channel);
|
||||
if (dma_id != -1) {
|
||||
dma_handle[dma_id] = dma_descr;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t dma_utils_mpu_region_size(uint32_t size) {
|
||||
#if (__ARM_ARCH <= 7)
|
||||
switch (size) {
|
||||
case 0x00000020U: {
|
||||
return MPU_REGION_SIZE_32B;
|
||||
}
|
||||
case 0x00000040U: {
|
||||
return MPU_REGION_SIZE_64B;
|
||||
}
|
||||
case 0x00000080U: {
|
||||
return MPU_REGION_SIZE_128B;
|
||||
}
|
||||
case 0x00000100U: {
|
||||
return MPU_REGION_SIZE_256B;
|
||||
}
|
||||
case 0x00000200U: {
|
||||
return MPU_REGION_SIZE_512B;
|
||||
}
|
||||
case 0x00000400U: {
|
||||
return MPU_REGION_SIZE_1KB;
|
||||
}
|
||||
case 0x00000800U: {
|
||||
return MPU_REGION_SIZE_2KB;
|
||||
}
|
||||
case 0x00001000U: {
|
||||
return MPU_REGION_SIZE_4KB;
|
||||
}
|
||||
case 0x00002000U: {
|
||||
return MPU_REGION_SIZE_8KB;
|
||||
}
|
||||
case 0x00004000U: {
|
||||
return MPU_REGION_SIZE_16KB;
|
||||
}
|
||||
case 0x00008000U: {
|
||||
return MPU_REGION_SIZE_32KB;
|
||||
}
|
||||
case 0x00010000U: {
|
||||
return MPU_REGION_SIZE_64KB;
|
||||
}
|
||||
case 0x00020000U: {
|
||||
return MPU_REGION_SIZE_128KB;
|
||||
}
|
||||
case 0x00040000U: {
|
||||
return MPU_REGION_SIZE_256KB;
|
||||
}
|
||||
case 0x00080000U: {
|
||||
return MPU_REGION_SIZE_512KB;
|
||||
}
|
||||
case 0x00100000U: {
|
||||
return MPU_REGION_SIZE_1MB;
|
||||
}
|
||||
case 0x00200000U: {
|
||||
return MPU_REGION_SIZE_2MB;
|
||||
}
|
||||
case 0x00400000U: {
|
||||
return MPU_REGION_SIZE_4MB;
|
||||
}
|
||||
case 0x00800000U: {
|
||||
return MPU_REGION_SIZE_8MB;
|
||||
}
|
||||
case 0x01000000U: {
|
||||
return MPU_REGION_SIZE_16MB;
|
||||
}
|
||||
case 0x02000000U: {
|
||||
return MPU_REGION_SIZE_32MB;
|
||||
}
|
||||
case 0x04000000U: {
|
||||
return MPU_REGION_SIZE_64MB;
|
||||
}
|
||||
case 0x08000000U: {
|
||||
return MPU_REGION_SIZE_128MB;
|
||||
}
|
||||
case 0x10000000U: {
|
||||
return MPU_REGION_SIZE_256MB;
|
||||
}
|
||||
case 0x20000000U: {
|
||||
return MPU_REGION_SIZE_512MB;
|
||||
}
|
||||
case 0x40000000U: {
|
||||
return MPU_REGION_SIZE_1GB;
|
||||
}
|
||||
case 0x80000000U: {
|
||||
return MPU_REGION_SIZE_2GB;
|
||||
}
|
||||
default: {
|
||||
return MPU_REGION_SIZE_4GB;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(GPDMA1)
|
||||
static inline void dma_utils_irq_handler(size_t irqn) {
|
||||
if (dma_handle[irqn] != NULL) {
|
||||
HAL_DMA_IRQHandler(dma_handle[irqn]);
|
||||
}
|
||||
}
|
||||
|
||||
void GPDMA1_Channel0_IRQHandler(void) {
|
||||
dma_utils_irq_handler(0);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel1_IRQHandler(void) {
|
||||
dma_utils_irq_handler(1);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel2_IRQHandler(void) {
|
||||
dma_utils_irq_handler(2);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel3_IRQHandler(void) {
|
||||
dma_utils_irq_handler(3);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel4_IRQHandler(void) {
|
||||
dma_utils_irq_handler(4);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel5_IRQHandler(void) {
|
||||
dma_utils_irq_handler(5);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel6_IRQHandler(void) {
|
||||
dma_utils_irq_handler(6);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel7_IRQHandler(void) {
|
||||
dma_utils_irq_handler(7);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel8_IRQHandler(void) {
|
||||
dma_utils_irq_handler(8);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel9_IRQHandler(void) {
|
||||
dma_utils_irq_handler(9);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel10_IRQHandler(void) {
|
||||
dma_utils_irq_handler(10);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel11_IRQHandler(void) {
|
||||
dma_utils_irq_handler(11);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel12_IRQHandler(void) {
|
||||
dma_utils_irq_handler(12);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel13_IRQHandler(void) {
|
||||
dma_utils_irq_handler(13);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel14_IRQHandler(void) {
|
||||
dma_utils_irq_handler(14);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel15_IRQHandler(void) {
|
||||
dma_utils_irq_handler(15);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel0_IRQHandler(void) {
|
||||
dma_utils_irq_handler(16);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel1_IRQHandler(void) {
|
||||
dma_utils_irq_handler(17);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel2_IRQHandler(void) {
|
||||
dma_utils_irq_handler(18);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel3_IRQHandler(void) {
|
||||
dma_utils_irq_handler(19);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel4_IRQHandler(void) {
|
||||
dma_utils_irq_handler(20);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel5_IRQHandler(void) {
|
||||
dma_utils_irq_handler(21);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel6_IRQHandler(void) {
|
||||
dma_utils_irq_handler(22);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel7_IRQHandler(void) {
|
||||
dma_utils_irq_handler(23);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel8_IRQHandler(void) {
|
||||
dma_utils_irq_handler(24);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel9_IRQHandler(void) {
|
||||
dma_utils_irq_handler(25);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel10_IRQHandler(void) {
|
||||
dma_utils_irq_handler(26);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel11_IRQHandler(void) {
|
||||
dma_utils_irq_handler(27);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel12_IRQHandler(void) {
|
||||
dma_utils_irq_handler(28);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel13_IRQHandler(void) {
|
||||
dma_utils_irq_handler(29);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel14_IRQHandler(void) {
|
||||
dma_utils_irq_handler(30);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel15_IRQHandler(void) {
|
||||
dma_utils_irq_handler(31);
|
||||
}
|
||||
#endif
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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 DMA helper functions.
|
||||
*/
|
||||
#ifndef __DMA_UTILS_H__
|
||||
#define __DMA_UTILS_H__
|
||||
|
||||
#ifdef MDMA
|
||||
#define MDMA_CHAN_TO_INSTANCE(x) \
|
||||
(MDMA_Channel_TypeDef *) (MDMA_Channel0_BASE + ((MDMA_Channel1_BASE - MDMA_Channel0_BASE) * x))
|
||||
#endif
|
||||
|
||||
uint8_t dma_utils_channel_to_irqn(void *dma_channel);
|
||||
uint8_t dma_utils_channel_to_id(void *dma_channel);
|
||||
int dma_utils_set_irq_descr(void *dma_channel, DMA_HandleTypeDef *dma_descr);
|
||||
uint8_t dma_utils_mpu_region_size(uint32_t size);
|
||||
#endif // __DMA_UTILS_H__
|
@ -84,7 +84,7 @@
|
||||
|
||||
#include "usbdbg.h"
|
||||
#include "sdram.h"
|
||||
#include "xspi_psram.h"
|
||||
#include "stm_xspi.h"
|
||||
#include "fb_alloc.h"
|
||||
#include "dma_alloc.h"
|
||||
#include "file_utils.h"
|
||||
@ -164,13 +164,13 @@ int main(void) {
|
||||
#endif
|
||||
|
||||
#if OMV_XSPI_PSRAM_SIZE
|
||||
if (xspi_psram_init() != 0) {
|
||||
if (stm_xspi_psram_init() != 0) {
|
||||
__fatal_error("Failed to init XSPI PSRAM!");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OMV_XSPI_PSRAM_STARTUP_TEST
|
||||
if (xspi_psram_test(true) == 0) {
|
||||
if (stm_xspi_psram_test(true) == 0) {
|
||||
__fatal_error("XSPI PSRAM test failed");
|
||||
}
|
||||
#endif
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include "fb_alloc.h"
|
||||
#include "omv_boardconfig.h"
|
||||
#include "omv_common.h"
|
||||
#include "dma_utils.h"
|
||||
#include "stm_dma.h"
|
||||
|
||||
#if MICROPY_PY_AUDIO
|
||||
|
||||
@ -73,8 +73,8 @@ static MDF_HandleTypeDef hmdf;
|
||||
static MDF_FilterConfigTypeDef hmdf_filter[OMV_AUDIO_MAX_CHANNELS];
|
||||
|
||||
// NOTE: Only 1 filter is supported right now.
|
||||
static DMA_QListTypeDef hdma_queue;
|
||||
static DMA_NodeTypeDef OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(hdma_node, 32), ".dma_buffer");
|
||||
static DMA_QListTypeDef dma_queue;
|
||||
static DMA_NodeTypeDef OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(dma_nodes, 32), ".dma_buffer");
|
||||
static DMA_HandleTypeDef hdma_filter[OMV_AUDIO_MAX_CHANNELS];
|
||||
|
||||
#define PDM_BUFFER_SIZE (512 * 2)
|
||||
@ -267,28 +267,18 @@ static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *k
|
||||
// Enable the DMA clock
|
||||
OMV_SAI_DMA_CLK_ENABLE();
|
||||
|
||||
// Configure the SAI DMA
|
||||
hdma_sai_rx.Instance = OMV_SAI_DMA_STREAM;
|
||||
hdma_sai_rx.Init.Request = OMV_SAI_DMA_REQUEST;
|
||||
hdma_sai_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||
hdma_sai_rx.Init.PeriphInc = DMA_PINC_DISABLE;
|
||||
hdma_sai_rx.Init.MemInc = DMA_MINC_ENABLE;
|
||||
hdma_sai_rx.Init.PeriphDataAlignment = (g_channels == 1) ? DMA_PDATAALIGN_BYTE : DMA_PDATAALIGN_HALFWORD;
|
||||
hdma_sai_rx.Init.MemDataAlignment = (g_channels == 1) ? DMA_MDATAALIGN_BYTE : DMA_MDATAALIGN_HALFWORD;
|
||||
hdma_sai_rx.Init.Mode = DMA_CIRCULAR;
|
||||
hdma_sai_rx.Init.Priority = DMA_PRIORITY_HIGH;
|
||||
hdma_sai_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
|
||||
hdma_sai_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
|
||||
hdma_sai_rx.Init.MemBurst = DMA_MBURST_SINGLE;
|
||||
hdma_sai_rx.Init.PeriphBurst = DMA_MBURST_SINGLE;
|
||||
__HAL_LINKDMA(&hsai, hdmarx, hdma_sai_rx);
|
||||
|
||||
// Initialize the DMA stream
|
||||
HAL_DMA_DeInit(&hdma_sai_rx);
|
||||
if (HAL_DMA_Init(&hdma_sai_rx) != HAL_OK) {
|
||||
uint32_t ssize = (g_channels == 1) ? 1 : 2;
|
||||
uint32_t dsize = (g_channels == 1) ? 1 : 2;
|
||||
|
||||
if (stm_dma_init(&hdma_sai_rx, OMV_SAI_DMA_STREAM, OMV_SAI_DMA_REQUEST,
|
||||
DMA_PERIPH_TO_MEMORY, ssize, dsize, 0, &stm_dma_sai_init, true)) {
|
||||
RAISE_OS_EXCEPTION("SAI DMA init failed!");
|
||||
}
|
||||
|
||||
// Link DMA handle.
|
||||
__HAL_LINKDMA(&hsai, hdmarx, hdma_sai_rx);
|
||||
|
||||
// Configure and enable SAI DMA IRQ Channel
|
||||
NVIC_SetPriority(OMV_SAI_DMA_IRQ, IRQ_PRI_DMA21);
|
||||
HAL_NVIC_EnableIRQ(OMV_SAI_DMA_IRQ);
|
||||
@ -364,27 +354,17 @@ static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *k
|
||||
OMV_DFSDM_DMA_CLK_ENABLE();
|
||||
|
||||
// Configure the DFSDM Filter 0 DMA/IRQ
|
||||
hdma_filter[0].Instance = OMV_DFSDM_FLT0_DMA_STREAM;
|
||||
hdma_filter[0].Init.Request = OMV_DFSDM_FLT0_DMA_REQUEST;
|
||||
hdma_filter[0].Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||
hdma_filter[0].Init.PeriphInc = DMA_PINC_DISABLE;
|
||||
hdma_filter[0].Init.MemInc = DMA_MINC_ENABLE;
|
||||
hdma_filter[0].Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||
hdma_filter[0].Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||
hdma_filter[0].Init.Mode = DMA_CIRCULAR;
|
||||
hdma_filter[0].Init.Priority = DMA_PRIORITY_HIGH;
|
||||
if (stm_dma_init(&hdma_filter[0], OMV_DFSDM_FLT0_DMA_STREAM, OMV_DFSDM_FLT0_DMA_REQUEST,
|
||||
DMA_PERIPH_TO_MEMORY, 4, 4, 0, &stm_dma_dfsdm_init, true)) {
|
||||
RAISE_OS_EXCEPTION("DFSDM DMA init failed!");
|
||||
}
|
||||
|
||||
// Link DMA handles.
|
||||
__HAL_LINKDMA(&hdfsdm_filter[0], hdmaInj, hdma_filter[0]);
|
||||
__HAL_LINKDMA(&hdfsdm_filter[0], hdmaReg, hdma_filter[0]);
|
||||
|
||||
// Set DMA IRQ handle
|
||||
dma_utils_set_irq_descr(OMV_DFSDM_FLT0_DMA_STREAM, &hdma_filter[0]);
|
||||
|
||||
// Initialize the DMA stream
|
||||
HAL_DMA_DeInit(&hdma_filter[0]);
|
||||
if (HAL_DMA_Init(&hdma_filter[0]) != HAL_OK) {
|
||||
RAISE_OS_EXCEPTION("SAI DFSDM init failed!");
|
||||
}
|
||||
stm_dma_set_irq_descr(OMV_DFSDM_FLT0_DMA_STREAM, &hdma_filter[0]);
|
||||
|
||||
// Configure and enable DFSDM Filter 0 DMA IRQ.
|
||||
NVIC_SetPriority(OMV_DFSDM_FLT0_DMA_IRQ, IRQ_PRI_DMA21);
|
||||
@ -405,10 +385,13 @@ static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *k
|
||||
hmdf.Init.SerialInterface.ClockSource = MDF_SITF_CCK0_SOURCE;
|
||||
hmdf.Init.SerialInterface.Threshold = 31;
|
||||
hmdf.Init.FilterBistream = MDF_BITSTREAM0_FALLING;
|
||||
|
||||
// Initialize MDF.
|
||||
if (HAL_MDF_Init(&hmdf) != HAL_OK) {
|
||||
RAISE_OS_EXCEPTION("MDF init failed!");
|
||||
}
|
||||
|
||||
// No init is called on this filter config.
|
||||
hmdf_filter[0].DataSource = MDF_DATA_SOURCE_BSMX;
|
||||
hmdf_filter[0].Delay = 0;
|
||||
hmdf_filter[0].CicMode = MDF_ONE_FILTER_SINC4;
|
||||
@ -425,56 +408,23 @@ static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *k
|
||||
hmdf_filter[0].FifoThreshold = MDF_FIFO_THRESHOLD_NOT_EMPTY;
|
||||
hmdf_filter[0].DiscardSamples = 0;
|
||||
|
||||
DMA_NodeConfTypeDef dma_ncfg;
|
||||
|
||||
dma_ncfg.NodeType = DMA_GPDMA_LINEAR_NODE;
|
||||
dma_ncfg.Init.Mode = DMA_NORMAL;
|
||||
dma_ncfg.Init.Request = OMV_MDF_FLT0_DMA_REQUEST;
|
||||
dma_ncfg.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
|
||||
dma_ncfg.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||
dma_ncfg.Init.SrcInc = DMA_SINC_FIXED;
|
||||
dma_ncfg.Init.DestInc = DMA_DINC_INCREMENTED;
|
||||
dma_ncfg.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
|
||||
dma_ncfg.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
|
||||
dma_ncfg.Init.SrcBurstLength = 1;
|
||||
dma_ncfg.Init.DestBurstLength = 1;
|
||||
dma_ncfg.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT1;
|
||||
dma_ncfg.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
|
||||
|
||||
dma_ncfg.SrcSecure = DMA_CHANNEL_SRC_SEC;
|
||||
dma_ncfg.DestSecure = DMA_CHANNEL_DEST_SEC;
|
||||
dma_ncfg.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
|
||||
dma_ncfg.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
|
||||
dma_ncfg.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
|
||||
|
||||
if (HAL_DMAEx_List_BuildNode(&dma_ncfg, &hdma_node) != HAL_OK ||
|
||||
HAL_DMAEx_List_InsertNode(&hdma_queue, NULL, &hdma_node) != HAL_OK ||
|
||||
HAL_DMAEx_List_SetCircularMode(&hdma_queue) != HAL_OK) {
|
||||
// Initialize DMA.
|
||||
if (stm_dma_init(&hdma_filter[0], OMV_MDF_FLT0_DMA_STREAM, OMV_MDF_FLT0_DMA_REQUEST,
|
||||
DMA_PERIPH_TO_MEMORY, 4, 4, OMV_MDF_DMA_XFER_PORTS, &stm_dma_mdf_init,
|
||||
true)) {
|
||||
RAISE_OS_EXCEPTION("MDF DMA init failed!");
|
||||
}
|
||||
|
||||
hdma_filter[0].Instance = OMV_MDF_FLT0_DMA_STREAM;
|
||||
hdma_filter[0].InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
|
||||
hdma_filter[0].InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
|
||||
hdma_filter[0].InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
|
||||
hdma_filter[0].InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
|
||||
hdma_filter[0].InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
|
||||
|
||||
if (HAL_DMAEx_List_Init(&hdma_filter[0]) != HAL_OK ||
|
||||
HAL_DMAEx_List_LinkQ(&hdma_filter[0], &hdma_queue) != HAL_OK) {
|
||||
// Initialize DMA circular mode.
|
||||
if (stm_dma_ll_init(&hdma_filter[0], &dma_queue, &dma_nodes, 1, OMV_MDF_DMA_LIST_PORTS)) {
|
||||
RAISE_OS_EXCEPTION("MDF DMA init failed!");
|
||||
}
|
||||
|
||||
// Link DMA handle.
|
||||
__HAL_LINKDMA(&hmdf, hdma, hdma_filter[0]);
|
||||
|
||||
if (HAL_DMA_ConfigChannelAttributes(&hdma_filter[0],
|
||||
DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC |
|
||||
DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC) != HAL_OK) {
|
||||
RAISE_OS_EXCEPTION("MDF DMA init failed!");
|
||||
}
|
||||
|
||||
// Set DMA IRQ handle
|
||||
dma_utils_set_irq_descr(OMV_MDF_FLT0_DMA_STREAM, &hdma_filter[0]);
|
||||
stm_dma_set_irq_descr(OMV_MDF_FLT0_DMA_STREAM, &hdma_filter[0]);
|
||||
|
||||
// Configure and enable MDF Filter 0 DMA IRQ.
|
||||
NVIC_SetPriority(OMV_MDF_FLT0_IRQ, IRQ_PRI_DMA21);
|
||||
|
@ -41,7 +41,7 @@
|
||||
#include "omv_gpio.h"
|
||||
#include "omv_i2c.h"
|
||||
#include "omv_csi.h"
|
||||
#include "dma_utils.h"
|
||||
#include "stm_dma.h"
|
||||
|
||||
#if defined(DMA2)
|
||||
#define USE_DMA (1)
|
||||
@ -50,7 +50,6 @@
|
||||
|
||||
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
|
||||
#define USE_MDMA (1)
|
||||
#define MDMA_BUFFER_SIZE (64)
|
||||
#endif
|
||||
|
||||
#if !defined(DCMIPP)
|
||||
@ -63,6 +62,10 @@
|
||||
#define DCMIPP_PIPE (DCMIPP_PIPE1)
|
||||
#endif
|
||||
|
||||
#ifndef OMV_CSI_DMA_XFER_PORTS
|
||||
#define OMV_CSI_DMA_XFER_PORTS (0)
|
||||
#endif
|
||||
|
||||
#define LINE_WIDTH_ALIGNMENT (16)
|
||||
|
||||
extern uint8_t _line_buf;
|
||||
@ -103,37 +106,21 @@ void omv_csi_mdma_irq_handler(void) {
|
||||
static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
|
||||
if (config == OMV_CSI_CONFIG_INIT) {
|
||||
#if USE_DMA
|
||||
// DMA Stream configuration
|
||||
csi->dma.Instance = DMA2_Stream1;
|
||||
#if defined(STM32H7)
|
||||
csi->dma.Init.Request = DMA_REQUEST_DCMI;
|
||||
#else
|
||||
csi->dma.Init.Channel = DMA_CHANNEL_1;
|
||||
#endif
|
||||
csi->dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||
csi->dma.Init.MemInc = DMA_MINC_ENABLE;
|
||||
csi->dma.Init.PeriphInc = DMA_PINC_DISABLE;
|
||||
csi->dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||
csi->dma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||
csi->dma.Init.Mode = DMA_NORMAL;
|
||||
csi->dma.Init.Priority = DMA_PRIORITY_HIGH;
|
||||
csi->dma.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
|
||||
csi->dma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
|
||||
csi->dma.Init.MemBurst = DMA_MBURST_INC4;
|
||||
csi->dma.Init.PeriphBurst = DMA_PBURST_SINGLE;
|
||||
|
||||
// Initialize the DMA stream
|
||||
HAL_DMA_DeInit(&csi->dma);
|
||||
if (HAL_DMA_Init(&csi->dma) != HAL_OK) {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
// Set DMA IRQ handle
|
||||
dma_utils_set_irq_descr(DMA2_Stream1, &csi->dma);
|
||||
stm_dma_set_irq_descr(OMV_CSI_DMA_CHANNEL, &csi->dma);
|
||||
|
||||
// Configure the DMA IRQ Channel
|
||||
NVIC_SetPriority(DMA2_Stream1_IRQn, IRQ_PRI_DMA21);
|
||||
|
||||
csi->dma_irqn = stm_dma_channel_to_irqn(OMV_CSI_DMA_CHANNEL);
|
||||
NVIC_SetPriority(csi->dma_irqn, IRQ_PRI_DMA21);
|
||||
|
||||
#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);
|
||||
@ -342,7 +329,7 @@ static int stm_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
|
||||
} else {
|
||||
HAL_DMA_Abort(&csi->dma);
|
||||
}
|
||||
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
|
||||
HAL_NVIC_DisableIRQ(csi->dma_irqn);
|
||||
#endif
|
||||
|
||||
#if USE_MDMA
|
||||
@ -494,7 +481,7 @@ void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t p
|
||||
// Clear out any stale flags.
|
||||
DMA2->LIFCR = DMA_FLAG_TCIF1_5 | DMA_FLAG_HTIF1_5;
|
||||
// Re-enable the DMA IRQ to catch the next start line.
|
||||
HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
|
||||
HAL_NVIC_EnableIRQ(csi->dma_irqn);
|
||||
#endif
|
||||
|
||||
// Reset DCMI_DMAConvCpltUser frame drop state.
|
||||
@ -541,7 +528,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
|
||||
if (csi->drop_frame) {
|
||||
#if USE_MDMA
|
||||
if (!csi->transpose) {
|
||||
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
|
||||
HAL_NVIC_DisableIRQ(csi->dma_irqn);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
@ -590,23 +577,13 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
|
||||
bytes_per_pixel = sizeof(uint8_t);
|
||||
}
|
||||
|
||||
// For all non-JPEG and non-transposed modes image capture can be completely offload to MDMA.
|
||||
#if USE_MDMA
|
||||
// For non-JPEG, non-transposed modes, offload the capture to MDMA.
|
||||
// Note that MDMA is started here, not in FRAME/VSYNC callbacks, to
|
||||
// maximize the time before the frame has to be dropped.
|
||||
if (!csi->transpose) {
|
||||
// NOTE: MDMA is started here, not in FRAME/VSYNC callbacks, to maximize the time before
|
||||
// the frame has to be dropped.
|
||||
uint32_t line_width_bytes = fb->u * bytes_per_pixel;
|
||||
// mdma0 will copy this line of the image to the final destination.
|
||||
__HAL_UNLOCK(&csi->mdma0);
|
||||
csi->mdma0.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi->mdma0, (uint32_t) src, (uint32_t) dst,
|
||||
line_width_bytes, 1);
|
||||
// mdma1 will copy all remaining lines of the image to the final destination.
|
||||
__HAL_UNLOCK(&csi->mdma1);
|
||||
csi->mdma1.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi->mdma1, (uint32_t) src, (uint32_t) (dst + line_width_bytes),
|
||||
line_width_bytes, fb->v - 1);
|
||||
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
|
||||
stm_mdma_start(csi, (uint32_t) src, (uint32_t) dst, fb->u * bytes_per_pixel, fb->v);
|
||||
HAL_NVIC_DisableIRQ(csi->dma_irqn);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@ -618,7 +595,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
|
||||
}
|
||||
|
||||
#if USE_MDMA
|
||||
// Two MDMA channels are used to maximize the time available for each channel to finish the transfer.
|
||||
// Two MDMA channels are used to maximize the time available to finish the transfer.
|
||||
omv_csi_copy_line(csi, (buffer->offset % 2) ? &csi->mdma1 : &csi->mdma0, src, dst);
|
||||
#else
|
||||
omv_csi_copy_line(csi, NULL, src, dst);
|
||||
@ -626,125 +603,6 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_MDMA
|
||||
// Configures an MDMA channel to completely offload the CPU in copying one line of pixels.
|
||||
static void omv_csi_mdma_config(omv_csi_t *csi, MDMA_InitTypeDef *init, uint32_t bytes_per_pixel) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
init->Request = MDMA_REQUEST_SW;
|
||||
init->TransferTriggerMode = MDMA_REPEAT_BLOCK_TRANSFER;
|
||||
init->Priority = MDMA_PRIORITY_VERY_HIGH;
|
||||
init->DataAlignment = MDMA_DATAALIGN_PACKENABLE;
|
||||
init->BufferTransferLength = MDMA_BUFFER_SIZE;
|
||||
// The source address is 1KB aligned. So, a burst size of 16 beats (AHB Max) should not break.
|
||||
// Destination lines may not be aligned however so the burst size must be computed.
|
||||
init->SourceBurst = MDMA_SOURCE_BURST_16BEATS;
|
||||
init->SourceBlockAddressOffset = 0;
|
||||
init->DestBlockAddressOffset = 0;
|
||||
|
||||
if ((csi->pixformat == PIXFORMAT_RGB565 && csi->rgb_swap) ||
|
||||
(csi->pixformat == PIXFORMAT_YUV422 && csi->yuv_swap)) {
|
||||
init->Endianness = MDMA_LITTLE_BYTE_ENDIANNESS_EXCHANGE;
|
||||
} else {
|
||||
init->Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
|
||||
}
|
||||
|
||||
uint32_t line_offset_bytes = (fb->x * bytes_per_pixel) - get_dcmi_hw_crop(csi, bytes_per_pixel);
|
||||
uint32_t line_width_bytes = fb->u * bytes_per_pixel;
|
||||
|
||||
if (csi->transpose) {
|
||||
line_width_bytes = bytes_per_pixel;
|
||||
init->DestBlockAddressOffset = (fb->v - 1) * bytes_per_pixel;
|
||||
}
|
||||
|
||||
// YUV422 Source -> Y Destination
|
||||
if ((csi->pixformat == PIXFORMAT_GRAYSCALE) && (csi->mono_bpp == 2)) {
|
||||
line_width_bytes /= 2;
|
||||
if (csi->transpose) {
|
||||
init->DestBlockAddressOffset /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// The destination will be 32-byte aligned, so the line width is broken into the largest
|
||||
// power of 2. The source may have an offset, further limiting this to a sub power of 2.
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
if (!(line_width_bytes % (1 << i))) {
|
||||
for (int j = IM_MIN(i, 2); j >= 0; j--) {
|
||||
if (!(line_offset_bytes % (1 << j))) {
|
||||
init->SourceInc = MDMA_CTCR_SINC_1 | (j << MDMA_CTCR_SINCOS_Pos);
|
||||
init->SourceDataSize = j << MDMA_CTCR_SSIZE_Pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
init->DestinationInc = MDMA_CTCR_DINC_1 | (i << MDMA_CTCR_DINCOS_Pos);
|
||||
init->DestDataSize = i << MDMA_CTCR_DSIZE_Pos;
|
||||
|
||||
// Find the burst size we can break the destination transfer up into.
|
||||
uint32_t count = MDMA_BUFFER_SIZE >> i;
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
if (!(count % (1 << i))) {
|
||||
init->DestBurst = i << MDMA_CTCR_DBURST_Pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// YUV422 Source -> Y Destination
|
||||
if ((csi->pixformat == PIXFORMAT_GRAYSCALE) && (csi->mono_bpp == 2)) {
|
||||
init->SourceInc = MDMA_SRC_INC_HALFWORD;
|
||||
init->SourceDataSize = MDMA_SRC_DATASIZE_BYTE;
|
||||
}
|
||||
}
|
||||
|
||||
static void omv_csi_mdma_enable(omv_csi_t *csi, uint32_t bytes_per_pixel) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
omv_csi_mdma_config(csi, &csi->mdma0.Init, bytes_per_pixel);
|
||||
memcpy(&csi->mdma1.Init, &csi->mdma0.Init, sizeof(MDMA_InitTypeDef));
|
||||
HAL_MDMA_Init(&csi->mdma0);
|
||||
|
||||
// If we are not transposing the image we can fully offload image capture from the CPU.
|
||||
if (!csi->transpose) {
|
||||
// MDMA will trigger on each TC from DMA and transfer one line to the frame buffer.
|
||||
csi->mdma1.Init.Request = MDMA_REQUEST_DMA2_Stream1_TC;
|
||||
csi->mdma1.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER;
|
||||
// We setup MDMA to repeatedly reset itself to transfer the same line buffer.
|
||||
csi->mdma1.Init.SourceBlockAddressOffset = -(fb->u * bytes_per_pixel);
|
||||
}
|
||||
|
||||
HAL_MDMA_Init(&csi->mdma1);
|
||||
if (!csi->transpose) {
|
||||
HAL_MDMA_ConfigPostRequestMask(&csi->mdma1, (uint32_t) &DMA2->LIFCR, DMA_FLAG_TCIF1_5);
|
||||
}
|
||||
}
|
||||
|
||||
int omv_csi_dma_memcpy(omv_csi_t *csi, void *dma, void *dst, void *src, int bpp, bool transposed) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
MDMA_HandleTypeDef *handle = dma;
|
||||
|
||||
// Drop the frame if MDMA is not keeping up as the image will be corrupted.
|
||||
if (handle->Instance->CCR & MDMA_CCR_EN) {
|
||||
csi->drop_frame = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If MDMA is still running, HAL_MDMA_Start() will start a new transfer.
|
||||
__HAL_UNLOCK(handle);
|
||||
handle->State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(handle,
|
||||
(uint32_t) src,
|
||||
(uint32_t) dst,
|
||||
transposed ? bpp : (fb->u * bpp),
|
||||
transposed ? fb->u : 1);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_DCMIPP
|
||||
void omv_csi_update_awb(omv_csi_t *csi, uint32_t n_pixels) {
|
||||
uint32_t avg[3];
|
||||
@ -865,15 +723,17 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
}
|
||||
|
||||
#if USE_MDMA
|
||||
// Enable MDMA transfer from the DCMI line buffer for non-JPEG modes.
|
||||
// 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) {
|
||||
omv_csi_mdma_enable(csi, bytes_per_pixel);
|
||||
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(DMA2_Stream1_IRQn);
|
||||
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.
|
||||
@ -889,10 +749,9 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
}
|
||||
#if USE_MDMA
|
||||
} else if ((csi->pixformat != PIXFORMAT_JPEG) && (!csi->transpose)) {
|
||||
// Start an MDMA transfer, which completely offloads the capture to MDMA.
|
||||
// DMA to circular mode writing the same line over and over again.
|
||||
// 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;
|
||||
// DCMI will transfer to same line and MDMA will move to final location.
|
||||
HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_CONTINUOUS,
|
||||
(uint32_t) &_line_buf, line_width_bytes / sizeof(uint32_t));
|
||||
#endif // USE_MDMA
|
||||
|
@ -147,6 +147,7 @@ typedef I2C_HandleTypeDef *omv_i2c_dev_t;
|
||||
#define OMV_CSI_PORT_BITS \
|
||||
struct { \
|
||||
DMA_HandleTypeDef dma; \
|
||||
IRQn_Type dma_irqn; \
|
||||
DCMI_HandleTypeDef dcmi; \
|
||||
OMV_CSI_PORT_BITS_MDMA \
|
||||
};
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
# Set startup and system files for CMSIS Makefile.
|
||||
SYSTEM ?= st/system_stm32
|
||||
LDSCRIPT ?= stm32
|
||||
LDSCRIPT ?= stm
|
||||
STARTUP ?= st/startup_$(shell echo $(MCU) | tr '[:upper:]' '[:lower:]')
|
||||
MCU_SERIES := $(shell echo $(MCU) | cut -c6-7 | tr '[:upper:]' '[:lower:]')
|
||||
MCU_LOWER := $(shell echo $(MCU) | tr '[:upper:]' '[:lower:]')
|
||||
|
@ -33,7 +33,7 @@
|
||||
#include "omv_boardconfig.h"
|
||||
#include "irq.h"
|
||||
#include "omv_common.h"
|
||||
#include "dma_utils.h"
|
||||
#include "stm_dma.h"
|
||||
#include "omv_gpio.h"
|
||||
#include "omv_spi.h"
|
||||
|
||||
@ -93,6 +93,10 @@ DEFINE_SPI_INSTANCE(6)
|
||||
} while (0)
|
||||
|
||||
|
||||
#ifndef OMV_SPI_DMA_XFER_PORTS
|
||||
#define OMV_SPI_DMA_XFER_PORTS (0)
|
||||
#endif
|
||||
|
||||
static omv_spi_t *omv_spi_descr_all[6] = { NULL };
|
||||
#if defined(STM32N6)
|
||||
static DMA_NodeTypeDef OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(omv_dma_nodes_all[6 * 2], 32), OMV_VOSPI_DMA_BUFFER);
|
||||
@ -289,105 +293,56 @@ int omv_spi_transfer_abort(omv_spi_t *spi) {
|
||||
}
|
||||
|
||||
static int omv_spi_dma_init(omv_spi_t *spi, uint32_t direction, omv_spi_config_t *config) {
|
||||
DMA_HandleTypeDef *dma_descr;
|
||||
DMA_HandleTypeDef *dma_descr = NULL;
|
||||
|
||||
if (direction == DMA_MEMORY_TO_PERIPH) {
|
||||
dma_descr = &spi->dma_descr_tx;
|
||||
} else {
|
||||
dma_descr = &spi->dma_descr_rx;
|
||||
}
|
||||
|
||||
DMA_InitTypeDef *dma_init = &dma_descr->Init;
|
||||
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
|
||||
// Configure the SPI DMA steam.
|
||||
dma_init->Mode = (config->dma_flags & OMV_SPI_DMA_CIRCULAR) ? DMA_CIRCULAR : DMA_NORMAL;
|
||||
dma_init->Priority = DMA_PRIORITY_HIGH;
|
||||
dma_init->Direction = direction;
|
||||
// When the DMA is configured in direct mode (the FIFO is disabled), the source and
|
||||
// destination transfer widths are equal, and both defined by PSIZE (MSIZE is ignored).
|
||||
// Additionally, burst transfers are not possible (MBURST and PBURST are both ignored).
|
||||
dma_init->FIFOMode = DMA_FIFOMODE_DISABLE;
|
||||
dma_init->FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
|
||||
// Note MBURST and PBURST are ignored.
|
||||
dma_init->MemBurst = DMA_MBURST_SINGLE;
|
||||
dma_init->PeriphBurst = DMA_PBURST_SINGLE;
|
||||
dma_init->MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||
#if defined(STM32H7)
|
||||
dma_init->PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||
#else
|
||||
dma_init->PeriphDataAlignment = (config->datasize == 8) ? DMA_PDATAALIGN_BYTE : DMA_PDATAALIGN_HALFWORD;
|
||||
#endif
|
||||
dma_init->MemInc = DMA_MINC_ENABLE;
|
||||
dma_init->PeriphInc = DMA_PINC_DISABLE;
|
||||
#if defined(STM32N6)
|
||||
DMA_NodeTypeDef *dma_nodes = NULL;
|
||||
DMA_QListTypeDef *dma_queue = NULL;
|
||||
|
||||
// Initialize the DMA stream
|
||||
HAL_DMA_DeInit(dma_descr);
|
||||
if (HAL_DMA_Init(dma_descr) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
#elif defined(STM32N6)
|
||||
// Configure the SPI DMA steam.
|
||||
dma_init->Mode = DMA_NORMAL;
|
||||
dma_init->Priority = DMA_HIGH_PRIORITY;
|
||||
dma_init->BlkHWRequest = DMA_BREQ_SINGLE_BURST;
|
||||
dma_init->Direction = direction;
|
||||
dma_init->SrcInc = (direction == DMA_PERIPH_TO_MEMORY) ? DMA_SINC_FIXED : DMA_SINC_INCREMENTED;
|
||||
dma_init->DestInc = (direction == DMA_MEMORY_TO_PERIPH) ? DMA_DINC_FIXED : DMA_DINC_INCREMENTED;
|
||||
dma_init->SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
|
||||
dma_init->DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
|
||||
dma_init->SrcBurstLength = 1;
|
||||
dma_init->DestBurstLength = 1;
|
||||
dma_init->TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
|
||||
dma_init->TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT1;
|
||||
|
||||
// Initialize the DMA stream
|
||||
if (!(config->dma_flags & OMV_SPI_DMA_CIRCULAR)) {
|
||||
HAL_DMA_DeInit(dma_descr);
|
||||
if (HAL_DMA_Init(dma_descr) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
if (direction == DMA_MEMORY_TO_PERIPH) {
|
||||
dma_queue = &spi->dma_queue_tx;
|
||||
dma_nodes = &omv_dma_nodes_all[(spi->id - 1) * 2 + 0];
|
||||
} else {
|
||||
DMA_NodeTypeDef *dma_node;
|
||||
DMA_QListTypeDef *dma_queue;
|
||||
|
||||
DMA_NodeConfTypeDef dma_ncfg = {
|
||||
.Init = *dma_init,
|
||||
.NodeType = DMA_GPDMA_LINEAR_NODE,
|
||||
.SrcSecure = DMA_CHANNEL_SRC_SEC,
|
||||
.DestSecure = DMA_CHANNEL_DEST_SEC,
|
||||
};
|
||||
|
||||
if (direction == DMA_MEMORY_TO_PERIPH) {
|
||||
dma_queue = &spi->dma_queue_tx;
|
||||
dma_node = &omv_dma_nodes_all[(spi->id - 1) * 2 + 0];
|
||||
} else {
|
||||
dma_queue = &spi->dma_queue_rx;
|
||||
dma_node = &omv_dma_nodes_all[(spi->id - 1) * 2 + 1];
|
||||
}
|
||||
|
||||
if (HAL_DMAEx_List_BuildNode(&dma_ncfg, dma_node) != HAL_OK ||
|
||||
HAL_DMAEx_List_InsertNode(dma_queue, NULL, dma_node) != HAL_OK ||
|
||||
HAL_DMAEx_List_SetCircularMode(dma_queue) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dma_descr->InitLinkedList.Priority = DMA_HIGH_PRIORITY;
|
||||
dma_descr->InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
|
||||
dma_descr->InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
|
||||
dma_descr->InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
|
||||
dma_descr->InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
|
||||
|
||||
if (HAL_DMAEx_List_Init(dma_descr) != HAL_OK ||
|
||||
HAL_DMAEx_List_LinkQ(dma_descr, dma_queue) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
dma_queue = &spi->dma_queue_rx;
|
||||
dma_nodes = &omv_dma_nodes_all[(spi->id - 1) * 2 + 1];
|
||||
}
|
||||
#endif
|
||||
|
||||
if (HAL_DMA_ConfigChannelAttributes(dma_descr,
|
||||
DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC |
|
||||
DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC) != HAL_OK) {
|
||||
uint32_t ssize = 4;
|
||||
uint32_t dsize = 4;
|
||||
|
||||
#if defined(STM32F4) || defined(STM32F7)
|
||||
ssize = config->datasize / 8;
|
||||
#endif
|
||||
|
||||
#if defined(STM32H7) || defined(STM32N6)
|
||||
uint32_t request = dma_descr->Init.Request;
|
||||
#else
|
||||
uint32_t request = dma_descr->Init.Channel;
|
||||
#endif
|
||||
|
||||
bool circular = config->dma_flags & OMV_SPI_DMA_CIRCULAR;
|
||||
|
||||
// Initialize DMA.
|
||||
if (stm_dma_init(dma_descr, dma_descr->Instance, request, direction, ssize,
|
||||
dsize, OMV_SPI_DMA_XFER_PORTS, &stm_dma_spi_init, circular)) {
|
||||
return -1;
|
||||
}
|
||||
#endif // STM32N6
|
||||
|
||||
#if defined(STM32N6)
|
||||
if (circular) {
|
||||
// Initialize DMA in circular mode.
|
||||
if (stm_dma_ll_init(dma_descr, dma_queue, dma_nodes, 1, OMV_SPI_DMA_LIST_PORTS)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Link the DMA handle to SPI handle.
|
||||
if (direction == DMA_MEMORY_TO_PERIPH) {
|
||||
@ -397,10 +352,10 @@ static int omv_spi_dma_init(omv_spi_t *spi, uint32_t direction, omv_spi_config_t
|
||||
}
|
||||
|
||||
// Set the SPI handle used by the DMA channel's IRQ handler.
|
||||
dma_utils_set_irq_descr(dma_descr->Instance, dma_descr);
|
||||
stm_dma_set_irq_descr(dma_descr->Instance, dma_descr);
|
||||
|
||||
// Get DMA channel's IRQ number.
|
||||
uint8_t dma_irqn = dma_utils_channel_to_irqn(dma_descr->Instance);
|
||||
uint8_t dma_irqn = stm_dma_channel_to_irqn(dma_descr->Instance);
|
||||
|
||||
// Configure and enable DMA IRQ channel.
|
||||
NVIC_SetPriority(dma_irqn, IRQ_PRI_DMA);
|
||||
|
699
ports/stm32/stm_dma.c
Normal file
699
ports/stm32/stm_dma.c
Normal file
@ -0,0 +1,699 @@
|
||||
/*
|
||||
* 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 DMA helper functions.
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include STM32_HAL_H
|
||||
#include "py/mphal.h"
|
||||
|
||||
#include "omv_boardconfig.h"
|
||||
#include "omv_common.h"
|
||||
#include "stm_dma.h"
|
||||
|
||||
#if defined(GPDMA1)
|
||||
static DMA_HandleTypeDef *dma_handle[32];
|
||||
#else
|
||||
// Defined in micropython/ports/stm32/dma.c or in uvc/src/main.c
|
||||
extern DMA_HandleTypeDef *dma_handle[16];
|
||||
#endif
|
||||
|
||||
const DMA_InitTypeDef stm_dma_csi_init = {
|
||||
#if defined(STM32N6)
|
||||
.BlkHWRequest = DMA_BREQ_SINGLE_BURST,
|
||||
.Priority = DMA_HIGH_PRIORITY,
|
||||
.SrcBurstLength = 1,
|
||||
// The maximum allowed AXI burst length 16 for HPDMA.
|
||||
// TODO: Should set dynamically for GPDMA or other buses.
|
||||
.DestBurstLength = 16,
|
||||
.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER,
|
||||
#elif defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
|
||||
.PeriphInc = DMA_PINC_DISABLE,
|
||||
.MemInc = DMA_MINC_ENABLE,
|
||||
.Priority = DMA_PRIORITY_HIGH,
|
||||
.FIFOMode = DMA_FIFOMODE_ENABLE,
|
||||
.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL,
|
||||
.MemBurst = DMA_MBURST_INC4,
|
||||
.PeriphBurst = DMA_PBURST_SINGLE,
|
||||
#else
|
||||
#error Unsupported MCU
|
||||
#endif
|
||||
};
|
||||
|
||||
const DMA_InitTypeDef stm_dma_spi_init = {
|
||||
#if defined(STM32N6)
|
||||
.BlkHWRequest = DMA_BREQ_SINGLE_BURST,
|
||||
.Priority = DMA_HIGH_PRIORITY,
|
||||
.SrcBurstLength = 1,
|
||||
.DestBurstLength = 1,
|
||||
.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER,
|
||||
#elif defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
|
||||
.PeriphInc = DMA_PINC_DISABLE,
|
||||
.MemInc = DMA_MINC_ENABLE,
|
||||
.Priority = DMA_PRIORITY_HIGH,
|
||||
// If the FIFO is disabled (DMA direct mode), the source and
|
||||
// destination transfer widths are equal and both are defined
|
||||
// by PSIZE (MSIZE is ignored).
|
||||
.FIFOMode = DMA_FIFOMODE_DISABLE,
|
||||
.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL,
|
||||
// Note MBURST and PBURST are ignored in direct mode.
|
||||
.MemBurst = DMA_MBURST_SINGLE,
|
||||
.PeriphBurst = DMA_PBURST_SINGLE,
|
||||
#else
|
||||
#error Unsupported MCU
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(OMV_SAI)
|
||||
const DMA_InitTypeDef stm_dma_sai_init = {
|
||||
.PeriphInc = DMA_PINC_DISABLE,
|
||||
.MemInc = DMA_MINC_ENABLE,
|
||||
.Priority = DMA_PRIORITY_HIGH,
|
||||
.FIFOMode = DMA_FIFOMODE_ENABLE,
|
||||
.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL,
|
||||
.MemBurst = DMA_MBURST_SINGLE,
|
||||
.PeriphBurst = DMA_PBURST_SINGLE,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(OMV_DFSDM)
|
||||
const DMA_InitTypeDef stm_dma_dfsdm_init = {
|
||||
.PeriphInc = DMA_PINC_DISABLE,
|
||||
.MemInc = DMA_MINC_ENABLE,
|
||||
.Priority = DMA_PRIORITY_HIGH,
|
||||
.FIFOMode = DMA_FIFOMODE_DISABLE, // Note: wasn't set
|
||||
.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL,
|
||||
.MemBurst = DMA_MBURST_SINGLE,
|
||||
.PeriphBurst = DMA_PBURST_SINGLE,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(OMV_MDF)
|
||||
const DMA_InitTypeDef stm_dma_mdf_init = {
|
||||
.BlkHWRequest = DMA_BREQ_SINGLE_BURST,
|
||||
.Priority = DMA_HIGH_PRIORITY,
|
||||
.SrcBurstLength = 1,
|
||||
.DestBurstLength = 1,
|
||||
.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER,
|
||||
};
|
||||
#endif
|
||||
|
||||
uint8_t stm_dma_channel_to_irqn(void *dma_channel) {
|
||||
if (0) {
|
||||
#if defined(DMA1_Stream0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFFF00) == DMA1_BASE) {
|
||||
return ((DMA_Stream_TypeDef *) dma_channel - DMA1_Stream0) + DMA1_Stream0_IRQn;
|
||||
#endif
|
||||
#if defined(DMA2_Stream0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFFF00) == DMA2_BASE) {
|
||||
return ((DMA_Stream_TypeDef *) dma_channel - DMA2_Stream0) + DMA2_Stream0_IRQn;
|
||||
#endif
|
||||
#if defined(GPDMA1_Channel0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFF000) == GPDMA1_BASE) {
|
||||
return ((DMA_Channel_TypeDef *) dma_channel - GPDMA1_Channel0) + GPDMA1_Channel0_IRQn;
|
||||
#endif
|
||||
#if defined(HPDMA1_Channel0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFF000) == HPDMA1_BASE) {
|
||||
return ((DMA_Channel_TypeDef *) dma_channel - HPDMA1_Channel0) + HPDMA1_Channel0_IRQn;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This returns a DMA ID that can be used to index into the dma_handle
|
||||
// array defined in micropython. Setting a DMA handle in that array allows
|
||||
// DMA IRQ handlers (which are all defined in micropython) to use it.
|
||||
uint8_t stm_dma_channel_to_id(void *dma_channel) {
|
||||
if (0) {
|
||||
#if defined(DMA1_Stream0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFFF00) == DMA1_BASE) {
|
||||
return ((DMA_Stream_TypeDef *) dma_channel - DMA1_Stream0);
|
||||
#endif
|
||||
#if defined(DMA2_Stream0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFFF00) == DMA2_BASE) {
|
||||
return ((DMA_Stream_TypeDef *) dma_channel - DMA2_Stream0) + 8;
|
||||
#endif
|
||||
#if defined(GPDMA1_Channel0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFF000) == GPDMA1_BASE) {
|
||||
return ((DMA_Channel_TypeDef *) dma_channel - GPDMA1_Channel0);
|
||||
#endif
|
||||
#if defined(HPDMA1_Channel0)
|
||||
} else if ((((uint32_t) dma_channel) & 0xFFFFF000) == HPDMA1_BASE) {
|
||||
return ((DMA_Channel_TypeDef *) dma_channel - HPDMA1_Channel0) + 16;
|
||||
#endif
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int stm_dma_set_irq_descr(void *dma_channel, DMA_HandleTypeDef *dma_descr) {
|
||||
uint8_t dma_id = stm_dma_channel_to_id(dma_channel);
|
||||
if (dma_id != -1) {
|
||||
dma_handle[dma_id] = dma_descr;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t stm_dma_mpu_region_size(uint32_t size) {
|
||||
#if (__ARM_ARCH <= 7)
|
||||
switch (size) {
|
||||
case 0x00000020U: {
|
||||
return MPU_REGION_SIZE_32B;
|
||||
}
|
||||
case 0x00000040U: {
|
||||
return MPU_REGION_SIZE_64B;
|
||||
}
|
||||
case 0x00000080U: {
|
||||
return MPU_REGION_SIZE_128B;
|
||||
}
|
||||
case 0x00000100U: {
|
||||
return MPU_REGION_SIZE_256B;
|
||||
}
|
||||
case 0x00000200U: {
|
||||
return MPU_REGION_SIZE_512B;
|
||||
}
|
||||
case 0x00000400U: {
|
||||
return MPU_REGION_SIZE_1KB;
|
||||
}
|
||||
case 0x00000800U: {
|
||||
return MPU_REGION_SIZE_2KB;
|
||||
}
|
||||
case 0x00001000U: {
|
||||
return MPU_REGION_SIZE_4KB;
|
||||
}
|
||||
case 0x00002000U: {
|
||||
return MPU_REGION_SIZE_8KB;
|
||||
}
|
||||
case 0x00004000U: {
|
||||
return MPU_REGION_SIZE_16KB;
|
||||
}
|
||||
case 0x00008000U: {
|
||||
return MPU_REGION_SIZE_32KB;
|
||||
}
|
||||
case 0x00010000U: {
|
||||
return MPU_REGION_SIZE_64KB;
|
||||
}
|
||||
case 0x00020000U: {
|
||||
return MPU_REGION_SIZE_128KB;
|
||||
}
|
||||
case 0x00040000U: {
|
||||
return MPU_REGION_SIZE_256KB;
|
||||
}
|
||||
case 0x00080000U: {
|
||||
return MPU_REGION_SIZE_512KB;
|
||||
}
|
||||
case 0x00100000U: {
|
||||
return MPU_REGION_SIZE_1MB;
|
||||
}
|
||||
case 0x00200000U: {
|
||||
return MPU_REGION_SIZE_2MB;
|
||||
}
|
||||
case 0x00400000U: {
|
||||
return MPU_REGION_SIZE_4MB;
|
||||
}
|
||||
case 0x00800000U: {
|
||||
return MPU_REGION_SIZE_8MB;
|
||||
}
|
||||
case 0x01000000U: {
|
||||
return MPU_REGION_SIZE_16MB;
|
||||
}
|
||||
case 0x02000000U: {
|
||||
return MPU_REGION_SIZE_32MB;
|
||||
}
|
||||
case 0x04000000U: {
|
||||
return MPU_REGION_SIZE_64MB;
|
||||
}
|
||||
case 0x08000000U: {
|
||||
return MPU_REGION_SIZE_128MB;
|
||||
}
|
||||
case 0x10000000U: {
|
||||
return MPU_REGION_SIZE_256MB;
|
||||
}
|
||||
case 0x20000000U: {
|
||||
return MPU_REGION_SIZE_512MB;
|
||||
}
|
||||
case 0x40000000U: {
|
||||
return MPU_REGION_SIZE_1GB;
|
||||
}
|
||||
case 0x80000000U: {
|
||||
return MPU_REGION_SIZE_2GB;
|
||||
}
|
||||
default: {
|
||||
return MPU_REGION_SIZE_4GB;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(HPDMA1_Channel0)
|
||||
static bool stm_dma_is_hp_channel(void *dma_channel) {
|
||||
return ((((uint32_t) dma_channel) & 0xFFFFF000) == HPDMA1_BASE);
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint32_t stm_dma_width(uint32_t size, bool source) {
|
||||
#if defined(STM32N6)
|
||||
switch(size) {
|
||||
case 1: return (source) ? DMA_SRC_DATAWIDTH_BYTE : DMA_DEST_DATAWIDTH_BYTE;
|
||||
case 2: return (source) ? DMA_SRC_DATAWIDTH_HALFWORD : DMA_DEST_DATAWIDTH_HALFWORD;
|
||||
case 4: return (source) ? DMA_SRC_DATAWIDTH_WORD : DMA_DEST_DATAWIDTH_WORD;
|
||||
case 8: return (source) ? DMA_SRC_DATAWIDTH_DOUBLEWORD : DMA_DEST_DATAWIDTH_DOUBLEWORD;
|
||||
default: return -1;
|
||||
}
|
||||
#else
|
||||
switch(size) {
|
||||
case 1: return (source) ? DMA_PDATAALIGN_BYTE : DMA_MDATAALIGN_BYTE;
|
||||
case 2: return (source) ? DMA_PDATAALIGN_HALFWORD : DMA_MDATAALIGN_HALFWORD;
|
||||
case 4: return (source) ? DMA_PDATAALIGN_WORD : DMA_MDATAALIGN_WORD;
|
||||
default: return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int stm_dma_init(DMA_HandleTypeDef *dma_descr, void *dma_channel, uint32_t request,
|
||||
uint32_t direction, uint32_t ssize, uint32_t dsize, uint32_t ports,
|
||||
const DMA_InitTypeDef *init, bool circular) {
|
||||
bool dma_init_done = true;
|
||||
DMA_InitTypeDef *dma_init = &dma_descr->Init;
|
||||
|
||||
// Set channel
|
||||
dma_descr->Instance = dma_channel;
|
||||
|
||||
// Copy static init.
|
||||
memcpy(dma_init, init, sizeof(DMA_InitTypeDef));
|
||||
|
||||
// Set request
|
||||
#if defined(STM32H7) || defined(STM32N6)
|
||||
dma_init->Request = request;
|
||||
#else
|
||||
dma_init->Channel = request;
|
||||
#endif
|
||||
|
||||
// Set direction
|
||||
dma_init->Direction = direction;
|
||||
|
||||
// Set src/dest increment
|
||||
#if defined(STM32N6)
|
||||
if (direction == DMA_PERIPH_TO_MEMORY) {
|
||||
dma_init->SrcInc = DMA_SINC_FIXED;
|
||||
dma_init->DestInc = DMA_DINC_INCREMENTED;
|
||||
} else {
|
||||
dma_init->SrcInc = DMA_SINC_INCREMENTED;
|
||||
dma_init->DestInc = DMA_DINC_FIXED;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Configure src/dest size/alignment
|
||||
#if defined(STM32N6)
|
||||
dma_init->SrcDataWidth = stm_dma_width(ssize, true);
|
||||
dma_init->DestDataWidth = stm_dma_width(dsize, false);
|
||||
#else
|
||||
dma_init->PeriphDataAlignment = stm_dma_width(ssize, true);
|
||||
dma_init->MemDataAlignment = stm_dma_width(dsize, false);
|
||||
#endif
|
||||
|
||||
// Set mode.
|
||||
#if defined(STM32N6)
|
||||
dma_init->Mode = DMA_NORMAL;
|
||||
#else
|
||||
dma_init->Mode = circular ? DMA_CIRCULAR : DMA_NORMAL;
|
||||
#endif
|
||||
|
||||
// Set allocated ports.
|
||||
#if defined(STM32N6)
|
||||
dma_init->TransferAllocatedPort = ports;
|
||||
#endif
|
||||
|
||||
// F4, F7, H7 or N6 in non-circular mode.
|
||||
#if defined(STM32N6)
|
||||
dma_init_done = !circular;
|
||||
#endif
|
||||
|
||||
if (dma_init_done) {
|
||||
HAL_DMA_DeInit(dma_descr);
|
||||
if (HAL_DMA_Init(dma_descr) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(STM32N6)
|
||||
int stm_dma_ll_init(DMA_HandleTypeDef *dma_descr, DMA_QListTypeDef *dma_queue,
|
||||
DMA_NodeTypeDef *dma_nodes, size_t nodes_count, uint32_t ports) {
|
||||
bool is_hp = stm_dma_is_hp_channel(dma_descr->Instance);
|
||||
|
||||
DMA_NodeConfTypeDef node_conf = {
|
||||
.SrcSecure = DMA_CHANNEL_SRC_SEC,
|
||||
.DestSecure = DMA_CHANNEL_DEST_SEC,
|
||||
.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE,
|
||||
.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED,
|
||||
.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED,
|
||||
.NodeType = is_hp ? DMA_HPDMA_LINEAR_NODE : DMA_GPDMA_LINEAR_NODE,
|
||||
};
|
||||
|
||||
// Copy Node DMA init.
|
||||
memcpy(&node_conf.Init, &dma_descr->Init, sizeof(DMA_InitTypeDef));
|
||||
|
||||
// Clear DMA queue and node(s).
|
||||
memset(dma_queue, 0, sizeof(DMA_QListTypeDef));
|
||||
memset(dma_nodes, 0, sizeof(DMA_NodeTypeDef) * nodes_count);
|
||||
|
||||
DMA_NodeTypeDef *prev_node = NULL;
|
||||
for (size_t i=0; i<nodes_count; i++) {
|
||||
if (HAL_DMAEx_List_BuildNode(&node_conf, &dma_nodes[i]) != HAL_OK ||
|
||||
HAL_DMAEx_List_InsertNode(dma_queue, prev_node, &dma_nodes[i]) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
prev_node = &dma_nodes[i];
|
||||
}
|
||||
|
||||
if (HAL_DMAEx_List_SetCircularMode(dma_queue) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dma_descr->InitLinkedList.Priority = DMA_HIGH_PRIORITY;
|
||||
dma_descr->InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
|
||||
dma_descr->InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
|
||||
dma_descr->InitLinkedList.LinkAllocatedPort = ports;
|
||||
dma_descr->InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
|
||||
|
||||
if (HAL_DMAEx_List_Init(dma_descr) != HAL_OK ||
|
||||
HAL_DMAEx_List_LinkQ(dma_descr, dma_queue) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t chan_flags = DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC |
|
||||
DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC;
|
||||
if (HAL_DMA_ConfigChannelAttributes(dma_descr, chan_flags) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_hp) {
|
||||
DMA_IsolationConfigTypeDef isocfg = {
|
||||
.CidFiltering = DMA_ISOLATION_ON,
|
||||
.StaticCid = DMA_CHANNEL_STATIC_CID_1,
|
||||
};
|
||||
if (HAL_DMA_SetIsolationAttributes(dma_descr, &isocfg) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OMV_MDMA_CHANNEL_DCMI_0
|
||||
void stm_mdma_init(omv_csi_t *csi, uint32_t bytes_per_pixel, uint32_t x_crop) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
stm_mdma_init_channel(csi, &csi->mdma0.Init, bytes_per_pixel, x_crop);
|
||||
memcpy(&csi->mdma1.Init, &csi->mdma0.Init, sizeof(MDMA_InitTypeDef));
|
||||
HAL_MDMA_Init(&csi->mdma0);
|
||||
|
||||
// If we are not transposing the image we can fully offload image capture from the CPU.
|
||||
if (!csi->transpose) {
|
||||
// MDMA will trigger on each TC from DMA and transfer one line to the frame buffer.
|
||||
csi->mdma1.Init.Request = MDMA_REQUEST_DMA2_Stream1_TC;
|
||||
csi->mdma1.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER;
|
||||
// We setup MDMA to repeatedly reset itself to transfer the same line buffer.
|
||||
csi->mdma1.Init.SourceBlockAddressOffset = -(fb->u * bytes_per_pixel);
|
||||
}
|
||||
|
||||
HAL_MDMA_Init(&csi->mdma1);
|
||||
if (!csi->transpose) {
|
||||
HAL_MDMA_ConfigPostRequestMask(&csi->mdma1, (uint32_t) &DMA2->LIFCR, DMA_FLAG_TCIF1_5);
|
||||
}
|
||||
}
|
||||
|
||||
// Configures an MDMA channel to completely offload the CPU in copying one line of pixels.
|
||||
void stm_mdma_init_channel(omv_csi_t *csi, MDMA_InitTypeDef *init, uint32_t bytes_per_pixel, uint32_t x_crop) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
init->Request = MDMA_REQUEST_SW;
|
||||
init->TransferTriggerMode = MDMA_REPEAT_BLOCK_TRANSFER;
|
||||
init->Priority = MDMA_PRIORITY_VERY_HIGH;
|
||||
init->DataAlignment = MDMA_DATAALIGN_PACKENABLE;
|
||||
init->BufferTransferLength = MDMA_BUFFER_SIZE;
|
||||
// The source address is 1KB aligned. So, a burst size of 16 beats
|
||||
// (AHB Max) should not break. Destination lines may not be aligned
|
||||
// however so the burst size must be computed.
|
||||
init->SourceBurst = MDMA_SOURCE_BURST_16BEATS;
|
||||
init->SourceBlockAddressOffset = 0;
|
||||
init->DestBlockAddressOffset = 0;
|
||||
|
||||
if ((csi->pixformat == PIXFORMAT_RGB565 && csi->rgb_swap) ||
|
||||
(csi->pixformat == PIXFORMAT_YUV422 && csi->yuv_swap)) {
|
||||
init->Endianness = MDMA_LITTLE_BYTE_ENDIANNESS_EXCHANGE;
|
||||
} else {
|
||||
init->Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
|
||||
}
|
||||
|
||||
uint32_t line_offset_bytes = (fb->x * bytes_per_pixel) - x_crop;
|
||||
uint32_t line_width_bytes = fb->u * bytes_per_pixel;
|
||||
|
||||
if (csi->transpose) {
|
||||
line_width_bytes = bytes_per_pixel;
|
||||
init->DestBlockAddressOffset = (fb->v - 1) * bytes_per_pixel;
|
||||
}
|
||||
|
||||
// YUV422 Source -> Y Destination
|
||||
if ((csi->pixformat == PIXFORMAT_GRAYSCALE) && (csi->mono_bpp == 2)) {
|
||||
line_width_bytes /= 2;
|
||||
if (csi->transpose) {
|
||||
init->DestBlockAddressOffset /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// The destination will be 32-byte aligned, so the line width is broken
|
||||
// into the largest power of 2. The source may have an offset, further
|
||||
// limiting this to a sub power of 2.
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
if (!(line_width_bytes % (1 << i))) {
|
||||
for (int j = IM_MIN(i, 2); j >= 0; j--) {
|
||||
if (!(line_offset_bytes % (1 << j))) {
|
||||
init->SourceInc = MDMA_CTCR_SINC_1 | (j << MDMA_CTCR_SINCOS_Pos);
|
||||
init->SourceDataSize = j << MDMA_CTCR_SSIZE_Pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
init->DestinationInc = MDMA_CTCR_DINC_1 | (i << MDMA_CTCR_DINCOS_Pos);
|
||||
init->DestDataSize = i << MDMA_CTCR_DSIZE_Pos;
|
||||
|
||||
// Find the burst size we can break the destination transfer up into.
|
||||
uint32_t count = MDMA_BUFFER_SIZE >> i;
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
if (!(count % (1 << i))) {
|
||||
init->DestBurst = i << MDMA_CTCR_DBURST_Pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// YUV422 Source -> Y Destination
|
||||
if ((csi->pixformat == PIXFORMAT_GRAYSCALE) && (csi->mono_bpp == 2)) {
|
||||
init->SourceInc = MDMA_SRC_INC_HALFWORD;
|
||||
init->SourceDataSize = MDMA_SRC_DATASIZE_BYTE;
|
||||
}
|
||||
}
|
||||
|
||||
void stm_mdma_start(omv_csi_t *csi, uint32_t src, uint32_t dst, uint32_t line_width, uint32_t line_count) {
|
||||
// mdma0 will copy this line of the image to the final destination.
|
||||
__HAL_UNLOCK(&csi->mdma0);
|
||||
csi->mdma0.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi->mdma0, src, dst, line_width, 1);
|
||||
|
||||
// mdma1 will copy all remaining lines of the image to the final destination.
|
||||
__HAL_UNLOCK(&csi->mdma1);
|
||||
csi->mdma1.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi->mdma1, src, dst + line_width, line_width, line_count - 1);
|
||||
}
|
||||
|
||||
int omv_csi_dma_memcpy(omv_csi_t *csi, void *dma, void *dst, void *src, int bpp, bool transposed) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
MDMA_HandleTypeDef *handle = dma;
|
||||
|
||||
// Drop the frame if MDMA is not keeping up as the image will be corrupted.
|
||||
if (handle->Instance->CCR & MDMA_CCR_EN) {
|
||||
csi->drop_frame = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If MDMA is still running, HAL_MDMA_Start() will start a new transfer.
|
||||
__HAL_UNLOCK(handle);
|
||||
handle->State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(handle,
|
||||
(uint32_t) src,
|
||||
(uint32_t) dst,
|
||||
transposed ? bpp : (fb->u * bpp),
|
||||
transposed ? fb->u : 1);
|
||||
return 0;
|
||||
}
|
||||
#endif // OMV_MDMA_CHANNEL_DCMI_0
|
||||
|
||||
#if defined(GPDMA1)
|
||||
static inline void stm_dma_irq_handler(size_t irqn) {
|
||||
if (dma_handle[irqn] != NULL) {
|
||||
HAL_DMA_IRQHandler(dma_handle[irqn]);
|
||||
}
|
||||
}
|
||||
|
||||
void GPDMA1_Channel0_IRQHandler(void) {
|
||||
stm_dma_irq_handler(0);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel1_IRQHandler(void) {
|
||||
stm_dma_irq_handler(1);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel2_IRQHandler(void) {
|
||||
stm_dma_irq_handler(2);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel3_IRQHandler(void) {
|
||||
stm_dma_irq_handler(3);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel4_IRQHandler(void) {
|
||||
stm_dma_irq_handler(4);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel5_IRQHandler(void) {
|
||||
stm_dma_irq_handler(5);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel6_IRQHandler(void) {
|
||||
stm_dma_irq_handler(6);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel7_IRQHandler(void) {
|
||||
stm_dma_irq_handler(7);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel8_IRQHandler(void) {
|
||||
stm_dma_irq_handler(8);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel9_IRQHandler(void) {
|
||||
stm_dma_irq_handler(9);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel10_IRQHandler(void) {
|
||||
stm_dma_irq_handler(10);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel11_IRQHandler(void) {
|
||||
stm_dma_irq_handler(11);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel12_IRQHandler(void) {
|
||||
stm_dma_irq_handler(12);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel13_IRQHandler(void) {
|
||||
stm_dma_irq_handler(13);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel14_IRQHandler(void) {
|
||||
stm_dma_irq_handler(14);
|
||||
}
|
||||
|
||||
void GPDMA1_Channel15_IRQHandler(void) {
|
||||
stm_dma_irq_handler(15);
|
||||
}
|
||||
#endif // GPDMA1
|
||||
|
||||
#if defined(HPDMA1)
|
||||
void HPDMA1_Channel0_IRQHandler(void) {
|
||||
stm_dma_irq_handler(16);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel1_IRQHandler(void) {
|
||||
stm_dma_irq_handler(17);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel2_IRQHandler(void) {
|
||||
stm_dma_irq_handler(18);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel3_IRQHandler(void) {
|
||||
stm_dma_irq_handler(19);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel4_IRQHandler(void) {
|
||||
stm_dma_irq_handler(20);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel5_IRQHandler(void) {
|
||||
stm_dma_irq_handler(21);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel6_IRQHandler(void) {
|
||||
stm_dma_irq_handler(22);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel7_IRQHandler(void) {
|
||||
stm_dma_irq_handler(23);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel8_IRQHandler(void) {
|
||||
stm_dma_irq_handler(24);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel9_IRQHandler(void) {
|
||||
stm_dma_irq_handler(25);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel10_IRQHandler(void) {
|
||||
stm_dma_irq_handler(26);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel11_IRQHandler(void) {
|
||||
stm_dma_irq_handler(27);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel12_IRQHandler(void) {
|
||||
stm_dma_irq_handler(28);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel13_IRQHandler(void) {
|
||||
stm_dma_irq_handler(29);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel14_IRQHandler(void) {
|
||||
stm_dma_irq_handler(30);
|
||||
}
|
||||
|
||||
void HPDMA1_Channel15_IRQHandler(void) {
|
||||
stm_dma_irq_handler(31);
|
||||
}
|
||||
#endif
|
63
ports/stm32/stm_dma.h
Normal file
63
ports/stm32/stm_dma.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 DMA helper functions.
|
||||
*/
|
||||
#ifndef __STM_DMA_H__
|
||||
#define __STM_DMA_H__
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include STM32_HAL_H
|
||||
|
||||
extern const DMA_InitTypeDef stm_dma_csi_init;
|
||||
extern const DMA_InitTypeDef stm_dma_spi_init;
|
||||
extern const DMA_InitTypeDef stm_dma_sai_init;
|
||||
extern const DMA_InitTypeDef stm_dma_dfsdm_init;
|
||||
extern const DMA_InitTypeDef stm_dma_mdf_init;
|
||||
|
||||
uint8_t stm_dma_channel_to_irqn(void *dma_channel);
|
||||
uint8_t stm_dma_channel_to_id(void *dma_channel);
|
||||
int stm_dma_set_irq_descr(void *dma_channel, DMA_HandleTypeDef *dma_descr);
|
||||
uint8_t stm_dma_mpu_region_size(uint32_t size);
|
||||
int stm_dma_init(DMA_HandleTypeDef *dma_descr, void *dma_channel, uint32_t request,
|
||||
uint32_t direction, uint32_t ssize, uint32_t dsize, uint32_t ports,
|
||||
const DMA_InitTypeDef *init, bool circular);
|
||||
|
||||
#if defined(STM32N6)
|
||||
int stm_dma_ll_init(DMA_HandleTypeDef *dma_descr, DMA_QListTypeDef *dma_queue,
|
||||
DMA_NodeTypeDef *dma_nodes, size_t nodes_count, uint32_t ports);
|
||||
#endif
|
||||
|
||||
#ifdef OMV_MDMA_CHANNEL_DCMI_0
|
||||
#include "omv_csi.h"
|
||||
|
||||
#define MDMA_BUFFER_SIZE (64)
|
||||
#define MDMA_CHAN_TO_INSTANCE(x) \
|
||||
(MDMA_Channel_TypeDef *) (MDMA_Channel0_BASE + ((MDMA_Channel1_BASE - MDMA_Channel0_BASE) * x))
|
||||
|
||||
void stm_mdma_init(omv_csi_t *csi, uint32_t bytes_per_pixel, uint32_t x_crop);
|
||||
void stm_mdma_init_channel(omv_csi_t *csi, MDMA_InitTypeDef *init, uint32_t bytes_per_pixel, uint32_t x_crop);
|
||||
void stm_mdma_start(omv_csi_t *csi, uint32_t src, uint32_t dst, uint32_t line_width, uint32_t line_count);
|
||||
#endif // OMV_MDMA_CHANNEL_DCMI_0
|
||||
|
||||
#endif // __STM_DMA_H__
|
@ -32,7 +32,7 @@
|
||||
// Define pin objects in this file.
|
||||
#define OMV_GPIO_DEFINE_PINS (1)
|
||||
#include "omv_gpio.h"
|
||||
#include "dma_utils.h"
|
||||
#include "stm_dma.h"
|
||||
|
||||
#if defined(MPU_REGION_NUMBER15)
|
||||
#define MPU_REGION_NUMBER_MAX (MPU_REGION_NUMBER15)
|
||||
@ -116,7 +116,7 @@ void HAL_MspInit(void) {
|
||||
MPU_InitStruct.Number = region_number--;
|
||||
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
|
||||
MPU_InitStruct.BaseAddress = buf->addr;
|
||||
MPU_InitStruct.Size = dma_utils_mpu_region_size(buf->size);
|
||||
MPU_InitStruct.Size = stm_dma_mpu_region_size(buf->size);
|
||||
HAL_MPU_ConfigRegion(&MPU_InitStruct);
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@
|
||||
|
||||
#include STM32_HAL_H
|
||||
#include "irq.h"
|
||||
#include "dma_utils.h"
|
||||
#include "stm_dma.h"
|
||||
|
||||
#define JPEG_CODEC_TIMEOUT (1000)
|
||||
#define JPEG_ALLOC_PADDING ((__SCB_DCACHE_LINE_SIZE) * 4)
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include STM32_HAL_H
|
||||
#include "omv_boardconfig.h"
|
||||
#include "stm_xspi.h"
|
||||
|
||||
#if defined(OMV_XSPI_PSRAM_ID)
|
||||
|
||||
@ -164,7 +165,7 @@ static int xspi_psram_memory_map(XSPI_HandleTypeDef *xspi, uint32_t burst_enable
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xspi_psram_init() {
|
||||
int stm_xspi_psram_init(void) {
|
||||
uint32_t xspi_clk = 0;
|
||||
|
||||
// Reset and enable XSPI clock.
|
||||
@ -225,7 +226,7 @@ extern void __fatal_error(const char *msg);
|
||||
#pragma GCC diagnostic ignored "-Wstringop-overflow"
|
||||
#endif
|
||||
|
||||
bool __attribute__((optimize("Os"))) xspi_psram_test(bool exhaustive) {
|
||||
bool __attribute__((optimize("Os"))) stm_xspi_psram_test(bool exhaustive) {
|
||||
uint8_t const pattern = 0xaa;
|
||||
uint8_t const antipattern = 0x55;
|
||||
volatile uint8_t *const mem_base = (uint8_t *) 0x90000000;
|
@ -30,7 +30,7 @@
|
||||
*
|
||||
* STM32 XSPI PSRAM driver.
|
||||
*/
|
||||
#ifndef __XSPI_PSRAM_H__
|
||||
int xspi_psram_init();
|
||||
bool xspi_psram_test(bool exhaustive);
|
||||
#endif // __XSPI_PSRAM_H__
|
||||
#ifndef __STM_XSPI_H__
|
||||
int stm_xspi_psram_init();
|
||||
bool stm_xspi_psram_test(bool exhaustive);
|
||||
#endif // __STM_XSPI_H__
|
Loading…
Reference in New Issue
Block a user