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

ports/stm32: Refactor DMA code.
This commit is contained in:
Ibrahim Abdelkader 2025-07-05 23:46:27 +03:00 committed by GitHub
commit 5ba66423de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 903 additions and 714 deletions

View File

@ -222,6 +222,8 @@
#define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE()
#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_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_DMA_MEMCPY_ENABLE (1)
#define OMV_CSI_HW_CROP_ENABLE (1) #define OMV_CSI_HW_CROP_ENABLE (1)

View File

@ -218,6 +218,8 @@
#define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM3_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM3_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM3_CLK_SLEEP_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM3_CLK_SLEEP_DISABLE()
#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK1Freq() #define OMV_CSI_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_DMA_MEMCPY_ENABLE (1)
#define OMV_CSI_HW_CROP_ENABLE (1) #define OMV_CSI_HW_CROP_ENABLE (1)

View File

@ -224,6 +224,8 @@
#define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE()
#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_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_DMA_MEMCPY_ENABLE (1)
#define OMV_CSI_HW_CROP_ENABLE (1) #define OMV_CSI_HW_CROP_ENABLE (1)

View File

@ -148,6 +148,8 @@
#define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE()
#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_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_HW_CROP_ENABLE (1)
#define OMV_CSI_D0_PIN (&omv_pin_C6_DCMI) #define OMV_CSI_D0_PIN (&omv_pin_C6_DCMI)

View File

@ -148,6 +148,8 @@
#define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE()
#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_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_HW_CROP_ENABLE (1)
#define OMV_CSI_D0_PIN (&omv_pin_C6_DCMI) #define OMV_CSI_D0_PIN (&omv_pin_C6_DCMI)

View File

@ -215,6 +215,8 @@
#define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE()
#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_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_DMA_MEMCPY_ENABLE (1)
#define OMV_CSI_HW_CROP_ENABLE (1) #define OMV_CSI_HW_CROP_ENABLE (1)

View File

@ -215,6 +215,8 @@
#define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE()
#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_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_DMA_MEMCPY_ENABLE (1)
#define OMV_CSI_HW_CROP_ENABLE (1) #define OMV_CSI_HW_CROP_ENABLE (1)

View File

@ -207,6 +207,8 @@
#define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE() #define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() #define OMV_CSI_TIM_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE()
#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() #define OMV_CSI_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_DMA_MEMCPY_ENABLE (1)
#define OMV_CSI_HW_CROP_ENABLE (1) #define OMV_CSI_HW_CROP_ENABLE (1)

View File

@ -226,6 +226,8 @@
#define OMV_SPI5_DMA_TX_REQUEST (GPDMA1_REQUEST_SPI5_TX) #define OMV_SPI5_DMA_TX_REQUEST (GPDMA1_REQUEST_SPI5_TX)
#define OMV_SPI5_DMA_RX_CHANNEL (GPDMA1_Channel9) #define OMV_SPI5_DMA_RX_CHANNEL (GPDMA1_Channel9)
#define OMV_SPI5_DMA_RX_REQUEST (GPDMA1_REQUEST_SPI5_RX) #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 // CSI SPI bus
#define OMV_CSI_SPI_ID (OMV_SPI5_ID) #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_STREAM GPDMA1_Channel10
#define OMV_MDF_FLT0_DMA_REQUEST GPDMA1_REQUEST_ADF1_FLT0 #define OMV_MDF_FLT0_DMA_REQUEST GPDMA1_REQUEST_ADF1_FLT0
#define OMV_MDF_FLT0_DMA_IRQ GPDMA1_Channel10_IRQn #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 // Camera Interface
#define OMV_CSI_CLK_SOURCE (OMV_CSI_CLK_SOURCE_TIM) #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_CHANNEL (HPDMA1_Channel12)
#define OMV_CSI_DMA_REQUEST (HPDMA1_REQUEST_DCMI_PSSI) #define OMV_CSI_DMA_REQUEST (HPDMA1_REQUEST_DCMI_PSSI)
#define OMV_CSI_DMA_MEMCPY_ENABLE (0) #define OMV_CSI_DMA_MEMCPY_ENABLE (0)
#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_HW_CROP_ENABLE (1)
#define OMV_CSI_MAX_DEVICES (3) #define OMV_CSI_MAX_DEVICES (3)

View File

@ -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

View File

@ -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__

View File

@ -84,7 +84,7 @@
#include "usbdbg.h" #include "usbdbg.h"
#include "sdram.h" #include "sdram.h"
#include "xspi_psram.h" #include "stm_xspi.h"
#include "fb_alloc.h" #include "fb_alloc.h"
#include "dma_alloc.h" #include "dma_alloc.h"
#include "file_utils.h" #include "file_utils.h"
@ -164,13 +164,13 @@ int main(void) {
#endif #endif
#if OMV_XSPI_PSRAM_SIZE #if OMV_XSPI_PSRAM_SIZE
if (xspi_psram_init() != 0) { if (stm_xspi_psram_init() != 0) {
__fatal_error("Failed to init XSPI PSRAM!"); __fatal_error("Failed to init XSPI PSRAM!");
} }
#endif #endif
#if OMV_XSPI_PSRAM_STARTUP_TEST #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"); __fatal_error("XSPI PSRAM test failed");
} }
#endif #endif

View File

@ -39,7 +39,7 @@
#include "fb_alloc.h" #include "fb_alloc.h"
#include "omv_boardconfig.h" #include "omv_boardconfig.h"
#include "omv_common.h" #include "omv_common.h"
#include "dma_utils.h" #include "stm_dma.h"
#if MICROPY_PY_AUDIO #if MICROPY_PY_AUDIO
@ -73,8 +73,8 @@ static MDF_HandleTypeDef hmdf;
static MDF_FilterConfigTypeDef hmdf_filter[OMV_AUDIO_MAX_CHANNELS]; static MDF_FilterConfigTypeDef hmdf_filter[OMV_AUDIO_MAX_CHANNELS];
// NOTE: Only 1 filter is supported right now. // NOTE: Only 1 filter is supported right now.
static DMA_QListTypeDef hdma_queue; static DMA_QListTypeDef dma_queue;
static DMA_NodeTypeDef OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(hdma_node, 32), ".dma_buffer"); static DMA_NodeTypeDef OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(dma_nodes, 32), ".dma_buffer");
static DMA_HandleTypeDef hdma_filter[OMV_AUDIO_MAX_CHANNELS]; static DMA_HandleTypeDef hdma_filter[OMV_AUDIO_MAX_CHANNELS];
#define PDM_BUFFER_SIZE (512 * 2) #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 // Enable the DMA clock
OMV_SAI_DMA_CLK_ENABLE(); 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 // Initialize the DMA stream
HAL_DMA_DeInit(&hdma_sai_rx); uint32_t ssize = (g_channels == 1) ? 1 : 2;
if (HAL_DMA_Init(&hdma_sai_rx) != HAL_OK) { 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!"); RAISE_OS_EXCEPTION("SAI DMA init failed!");
} }
// Link DMA handle.
__HAL_LINKDMA(&hsai, hdmarx, hdma_sai_rx);
// Configure and enable SAI DMA IRQ Channel // Configure and enable SAI DMA IRQ Channel
NVIC_SetPriority(OMV_SAI_DMA_IRQ, IRQ_PRI_DMA21); NVIC_SetPriority(OMV_SAI_DMA_IRQ, IRQ_PRI_DMA21);
HAL_NVIC_EnableIRQ(OMV_SAI_DMA_IRQ); 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(); OMV_DFSDM_DMA_CLK_ENABLE();
// Configure the DFSDM Filter 0 DMA/IRQ // Configure the DFSDM Filter 0 DMA/IRQ
hdma_filter[0].Instance = OMV_DFSDM_FLT0_DMA_STREAM; if (stm_dma_init(&hdma_filter[0], OMV_DFSDM_FLT0_DMA_STREAM, OMV_DFSDM_FLT0_DMA_REQUEST,
hdma_filter[0].Init.Request = OMV_DFSDM_FLT0_DMA_REQUEST; DMA_PERIPH_TO_MEMORY, 4, 4, 0, &stm_dma_dfsdm_init, true)) {
hdma_filter[0].Init.Direction = DMA_PERIPH_TO_MEMORY; RAISE_OS_EXCEPTION("DFSDM DMA init failed!");
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;
// Link DMA handles.
__HAL_LINKDMA(&hdfsdm_filter[0], hdmaInj, hdma_filter[0]); __HAL_LINKDMA(&hdfsdm_filter[0], hdmaInj, hdma_filter[0]);
__HAL_LINKDMA(&hdfsdm_filter[0], hdmaReg, hdma_filter[0]); __HAL_LINKDMA(&hdfsdm_filter[0], hdmaReg, hdma_filter[0]);
// Set DMA IRQ handle // Set DMA IRQ handle
dma_utils_set_irq_descr(OMV_DFSDM_FLT0_DMA_STREAM, &hdma_filter[0]); stm_dma_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!");
}
// Configure and enable DFSDM Filter 0 DMA IRQ. // Configure and enable DFSDM Filter 0 DMA IRQ.
NVIC_SetPriority(OMV_DFSDM_FLT0_DMA_IRQ, IRQ_PRI_DMA21); 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.ClockSource = MDF_SITF_CCK0_SOURCE;
hmdf.Init.SerialInterface.Threshold = 31; hmdf.Init.SerialInterface.Threshold = 31;
hmdf.Init.FilterBistream = MDF_BITSTREAM0_FALLING; hmdf.Init.FilterBistream = MDF_BITSTREAM0_FALLING;
// Initialize MDF.
if (HAL_MDF_Init(&hmdf) != HAL_OK) { if (HAL_MDF_Init(&hmdf) != HAL_OK) {
RAISE_OS_EXCEPTION("MDF init failed!"); 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].DataSource = MDF_DATA_SOURCE_BSMX;
hmdf_filter[0].Delay = 0; hmdf_filter[0].Delay = 0;
hmdf_filter[0].CicMode = MDF_ONE_FILTER_SINC4; 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].FifoThreshold = MDF_FIFO_THRESHOLD_NOT_EMPTY;
hmdf_filter[0].DiscardSamples = 0; hmdf_filter[0].DiscardSamples = 0;
DMA_NodeConfTypeDef dma_ncfg; // Initialize DMA.
if (stm_dma_init(&hdma_filter[0], OMV_MDF_FLT0_DMA_STREAM, OMV_MDF_FLT0_DMA_REQUEST,
dma_ncfg.NodeType = DMA_GPDMA_LINEAR_NODE; DMA_PERIPH_TO_MEMORY, 4, 4, OMV_MDF_DMA_XFER_PORTS, &stm_dma_mdf_init,
dma_ncfg.Init.Mode = DMA_NORMAL; true)) {
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) {
RAISE_OS_EXCEPTION("MDF DMA init failed!"); RAISE_OS_EXCEPTION("MDF DMA init failed!");
} }
hdma_filter[0].Instance = OMV_MDF_FLT0_DMA_STREAM; // Initialize DMA circular mode.
hdma_filter[0].InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT; if (stm_dma_ll_init(&hdma_filter[0], &dma_queue, &dma_nodes, 1, OMV_MDF_DMA_LIST_PORTS)) {
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) {
RAISE_OS_EXCEPTION("MDF DMA init failed!"); RAISE_OS_EXCEPTION("MDF DMA init failed!");
} }
// Link DMA handle.
__HAL_LINKDMA(&hmdf, hdma, hdma_filter[0]); __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 // 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. // Configure and enable MDF Filter 0 DMA IRQ.
NVIC_SetPriority(OMV_MDF_FLT0_IRQ, IRQ_PRI_DMA21); NVIC_SetPriority(OMV_MDF_FLT0_IRQ, IRQ_PRI_DMA21);

View File

@ -41,7 +41,7 @@
#include "omv_gpio.h" #include "omv_gpio.h"
#include "omv_i2c.h" #include "omv_i2c.h"
#include "omv_csi.h" #include "omv_csi.h"
#include "dma_utils.h" #include "stm_dma.h"
#if defined(DMA2) #if defined(DMA2)
#define USE_DMA (1) #define USE_DMA (1)
@ -50,7 +50,6 @@
#if defined(OMV_MDMA_CHANNEL_DCMI_0) #if defined(OMV_MDMA_CHANNEL_DCMI_0)
#define USE_MDMA (1) #define USE_MDMA (1)
#define MDMA_BUFFER_SIZE (64)
#endif #endif
#if !defined(DCMIPP) #if !defined(DCMIPP)
@ -63,6 +62,10 @@
#define DCMIPP_PIPE (DCMIPP_PIPE1) #define DCMIPP_PIPE (DCMIPP_PIPE1)
#endif #endif
#ifndef OMV_CSI_DMA_XFER_PORTS
#define OMV_CSI_DMA_XFER_PORTS (0)
#endif
#define LINE_WIDTH_ALIGNMENT (16) #define LINE_WIDTH_ALIGNMENT (16)
extern uint8_t _line_buf; 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) { static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
if (config == OMV_CSI_CONFIG_INIT) { if (config == OMV_CSI_CONFIG_INIT) {
#if USE_DMA #if USE_DMA
// DMA Stream configuration
csi->dma.Instance = DMA2_Stream1; // Configure and initialize DMA.
#if defined(STM32H7) if (stm_dma_init(&csi->dma, OMV_CSI_DMA_CHANNEL, OMV_CSI_DMA_REQUEST,
csi->dma.Init.Request = DMA_REQUEST_DCMI; DMA_PERIPH_TO_MEMORY, 4, 4, OMV_CSI_DMA_XFER_PORTS,
#else &stm_dma_csi_init, true)) {
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) {
return OMV_CSI_ERROR_DMA_INIT_FAILED; return OMV_CSI_ERROR_DMA_INIT_FAILED;
} }
// Set DMA IRQ handle // 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 // 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 #if USE_MDMA
csi->mdma0.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_0); csi->mdma0.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_0);
csi->mdma1.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_1); 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 { } else {
HAL_DMA_Abort(&csi->dma); HAL_DMA_Abort(&csi->dma);
} }
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn); HAL_NVIC_DisableIRQ(csi->dma_irqn);
#endif #endif
#if USE_MDMA #if USE_MDMA
@ -494,7 +481,7 @@ void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t p
// Clear out any stale flags. // Clear out any stale flags.
DMA2->LIFCR = DMA_FLAG_TCIF1_5 | DMA_FLAG_HTIF1_5; DMA2->LIFCR = DMA_FLAG_TCIF1_5 | DMA_FLAG_HTIF1_5;
// Re-enable the DMA IRQ to catch the next start line. // Re-enable the DMA IRQ to catch the next start line.
HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn); HAL_NVIC_EnableIRQ(csi->dma_irqn);
#endif #endif
// Reset DCMI_DMAConvCpltUser frame drop state. // Reset DCMI_DMAConvCpltUser frame drop state.
@ -541,7 +528,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
if (csi->drop_frame) { if (csi->drop_frame) {
#if USE_MDMA #if USE_MDMA
if (!csi->transpose) { if (!csi->transpose) {
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn); HAL_NVIC_DisableIRQ(csi->dma_irqn);
} }
#endif #endif
return; return;
@ -590,23 +577,13 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
bytes_per_pixel = sizeof(uint8_t); 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 #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) { if (!csi->transpose) {
// NOTE: MDMA is started here, not in FRAME/VSYNC callbacks, to maximize the time before stm_mdma_start(csi, (uint32_t) src, (uint32_t) dst, fb->u * bytes_per_pixel, fb->v);
// the frame has to be dropped. HAL_NVIC_DisableIRQ(csi->dma_irqn);
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);
return; return;
} }
#endif #endif
@ -618,7 +595,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
} }
#if USE_MDMA #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); omv_csi_copy_line(csi, (buffer->offset % 2) ? &csi->mdma1 : &csi->mdma0, src, dst);
#else #else
omv_csi_copy_line(csi, NULL, src, dst); omv_csi_copy_line(csi, NULL, src, dst);
@ -626,125 +603,6 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
} }
#endif #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 #if USE_DCMIPP
void omv_csi_update_awb(omv_csi_t *csi, uint32_t n_pixels) { void omv_csi_update_awb(omv_csi_t *csi, uint32_t n_pixels) {
uint32_t avg[3]; 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 #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) { if (csi->pixformat != PIXFORMAT_JPEG) {
omv_csi_mdma_enable(csi, bytes_per_pixel); stm_mdma_init(csi, bytes_per_pixel, x_crop);
} }
#endif #endif
// Reset the DMA state and re-enable it. // Reset the DMA state and re-enable it.
((DMA_Stream_TypeDef *) csi->dma.Instance)->CR &= ~(DMA_SxCR_CIRC | DMA_SxCR_CT | DMA_SxCR_DBM); ((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, // 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. // 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 #if USE_MDMA
} else if ((csi->pixformat != PIXFORMAT_JPEG) && (!csi->transpose)) { } else if ((csi->pixformat != PIXFORMAT_JPEG) && (!csi->transpose)) {
// Start an MDMA transfer, which completely offloads the capture to MDMA. // Special transfer mode that uses DMA in circular mode and MDMA
// DMA to circular mode writing the same line over and over again. // to move the lines to the final destination.
((DMA_Stream_TypeDef *) csi->dma.Instance)->CR |= DMA_SxCR_CIRC; ((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, HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_CONTINUOUS,
(uint32_t) &_line_buf, line_width_bytes / sizeof(uint32_t)); (uint32_t) &_line_buf, line_width_bytes / sizeof(uint32_t));
#endif // USE_MDMA #endif // USE_MDMA

View File

@ -147,6 +147,7 @@ typedef I2C_HandleTypeDef *omv_i2c_dev_t;
#define OMV_CSI_PORT_BITS \ #define OMV_CSI_PORT_BITS \
struct { \ struct { \
DMA_HandleTypeDef dma; \ DMA_HandleTypeDef dma; \
IRQn_Type dma_irqn; \
DCMI_HandleTypeDef dcmi; \ DCMI_HandleTypeDef dcmi; \
OMV_CSI_PORT_BITS_MDMA \ OMV_CSI_PORT_BITS_MDMA \
}; };

View File

@ -22,7 +22,7 @@
# Set startup and system files for CMSIS Makefile. # Set startup and system files for CMSIS Makefile.
SYSTEM ?= st/system_stm32 SYSTEM ?= st/system_stm32
LDSCRIPT ?= stm32 LDSCRIPT ?= stm
STARTUP ?= st/startup_$(shell echo $(MCU) | tr '[:upper:]' '[:lower:]') STARTUP ?= st/startup_$(shell echo $(MCU) | tr '[:upper:]' '[:lower:]')
MCU_SERIES := $(shell echo $(MCU) | cut -c6-7 | tr '[:upper:]' '[:lower:]') MCU_SERIES := $(shell echo $(MCU) | cut -c6-7 | tr '[:upper:]' '[:lower:]')
MCU_LOWER := $(shell echo $(MCU) | tr '[:upper:]' '[:lower:]') MCU_LOWER := $(shell echo $(MCU) | tr '[:upper:]' '[:lower:]')

View File

@ -33,7 +33,7 @@
#include "omv_boardconfig.h" #include "omv_boardconfig.h"
#include "irq.h" #include "irq.h"
#include "omv_common.h" #include "omv_common.h"
#include "dma_utils.h" #include "stm_dma.h"
#include "omv_gpio.h" #include "omv_gpio.h"
#include "omv_spi.h" #include "omv_spi.h"
@ -93,6 +93,10 @@ DEFINE_SPI_INSTANCE(6)
} while (0) } 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 }; static omv_spi_t *omv_spi_descr_all[6] = { NULL };
#if defined(STM32N6) #if defined(STM32N6)
static DMA_NodeTypeDef OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(omv_dma_nodes_all[6 * 2], 32), OMV_VOSPI_DMA_BUFFER); 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) { 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) { if (direction == DMA_MEMORY_TO_PERIPH) {
dma_descr = &spi->dma_descr_tx; dma_descr = &spi->dma_descr_tx;
} else { } else {
dma_descr = &spi->dma_descr_rx; dma_descr = &spi->dma_descr_rx;
} }
DMA_InitTypeDef *dma_init = &dma_descr->Init; #if defined(STM32N6)
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) DMA_NodeTypeDef *dma_nodes = NULL;
// Configure the SPI DMA steam. DMA_QListTypeDef *dma_queue = NULL;
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;
// Initialize the DMA stream if (direction == DMA_MEMORY_TO_PERIPH) {
HAL_DMA_DeInit(dma_descr); dma_queue = &spi->dma_queue_tx;
if (HAL_DMA_Init(dma_descr) != HAL_OK) { dma_nodes = &omv_dma_nodes_all[(spi->id - 1) * 2 + 0];
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;
}
} else { } else {
DMA_NodeTypeDef *dma_node; dma_queue = &spi->dma_queue_rx;
DMA_QListTypeDef *dma_queue; dma_nodes = &omv_dma_nodes_all[(spi->id - 1) * 2 + 1];
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;
}
} }
#endif
if (HAL_DMA_ConfigChannelAttributes(dma_descr, uint32_t ssize = 4;
DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC | uint32_t dsize = 4;
DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC) != HAL_OK) {
#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; 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. // Link the DMA handle to SPI handle.
if (direction == DMA_MEMORY_TO_PERIPH) { 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. // 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. // 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. // Configure and enable DMA IRQ channel.
NVIC_SetPriority(dma_irqn, IRQ_PRI_DMA); NVIC_SetPriority(dma_irqn, IRQ_PRI_DMA);

699
ports/stm32/stm_dma.c Normal file
View 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
View 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__

View File

@ -32,7 +32,7 @@
// Define pin objects in this file. // Define pin objects in this file.
#define OMV_GPIO_DEFINE_PINS (1) #define OMV_GPIO_DEFINE_PINS (1)
#include "omv_gpio.h" #include "omv_gpio.h"
#include "dma_utils.h" #include "stm_dma.h"
#if defined(MPU_REGION_NUMBER15) #if defined(MPU_REGION_NUMBER15)
#define MPU_REGION_NUMBER_MAX (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.Number = region_number--;
MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = buf->addr; 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); HAL_MPU_ConfigRegion(&MPU_InitStruct);
} }
} }

View File

@ -32,7 +32,7 @@
#include STM32_HAL_H #include STM32_HAL_H
#include "irq.h" #include "irq.h"
#include "dma_utils.h" #include "stm_dma.h"
#define JPEG_CODEC_TIMEOUT (1000) #define JPEG_CODEC_TIMEOUT (1000)
#define JPEG_ALLOC_PADDING ((__SCB_DCACHE_LINE_SIZE) * 4) #define JPEG_ALLOC_PADDING ((__SCB_DCACHE_LINE_SIZE) * 4)

View File

@ -36,6 +36,7 @@
#include STM32_HAL_H #include STM32_HAL_H
#include "omv_boardconfig.h" #include "omv_boardconfig.h"
#include "stm_xspi.h"
#if defined(OMV_XSPI_PSRAM_ID) #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; return 0;
} }
int xspi_psram_init() { int stm_xspi_psram_init(void) {
uint32_t xspi_clk = 0; uint32_t xspi_clk = 0;
// Reset and enable XSPI clock. // Reset and enable XSPI clock.
@ -225,7 +226,7 @@ extern void __fatal_error(const char *msg);
#pragma GCC diagnostic ignored "-Wstringop-overflow" #pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif #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 pattern = 0xaa;
uint8_t const antipattern = 0x55; uint8_t const antipattern = 0x55;
volatile uint8_t *const mem_base = (uint8_t *) 0x90000000; volatile uint8_t *const mem_base = (uint8_t *) 0x90000000;

View File

@ -30,7 +30,7 @@
* *
* STM32 XSPI PSRAM driver. * STM32 XSPI PSRAM driver.
*/ */
#ifndef __XSPI_PSRAM_H__ #ifndef __STM_XSPI_H__
int xspi_psram_init(); int stm_xspi_psram_init();
bool xspi_psram_test(bool exhaustive); bool stm_xspi_psram_test(bool exhaustive);
#endif // __XSPI_PSRAM_H__ #endif // __STM_XSPI_H__