From 85179136ab2d0528381d0c7c9519f4a0822fe58f Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 3 Jul 2025 20:35:47 +0200 Subject: [PATCH 1/5] ports/stm32: Rename port files consistently. Just a search/replace/rename for consistency with other ports. Signed-off-by: iabdalkader --- ports/stm32/main.c | 6 +- ports/stm32/modules/py_audio.c | 6 +- ports/stm32/omv_csi.c | 4 +- ports/stm32/omv_portconfig.mk | 2 +- ports/stm32/omv_spi.c | 6 +- ports/stm32/{stm32.ld.S => stm.ld.S} | 0 ports/stm32/{dma_utils.c => stm_dma.c} | 78 +++++++++++----------- ports/stm32/{dma_utils.h => stm_dma.h} | 20 ++---- ports/stm32/{stm32_hal_msp.c => stm_hal.c} | 4 +- ports/stm32/{jpeg.c => stm_jpeg.c} | 2 +- ports/stm32/{stm32_nema.c => stm_nema.c} | 0 ports/stm32/{xspi_psram.c => stm_xspi.c} | 5 +- ports/stm32/{xspi_psram.h => stm_xspi.h} | 8 +-- 13 files changed, 68 insertions(+), 73 deletions(-) rename ports/stm32/{stm32.ld.S => stm.ld.S} (100%) rename ports/stm32/{dma_utils.c => stm_dma.c} (84%) rename ports/stm32/{dma_utils.h => stm_dma.h} (71%) rename ports/stm32/{stm32_hal_msp.c => stm_hal.c} (99%) rename ports/stm32/{jpeg.c => stm_jpeg.c} (99%) rename ports/stm32/{stm32_nema.c => stm_nema.c} (100%) rename ports/stm32/{xspi_psram.c => stm_xspi.c} (98%) rename ports/stm32/{xspi_psram.h => stm_xspi.h} (93%) diff --git a/ports/stm32/main.c b/ports/stm32/main.c index ae33f957a..37448e0f2 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -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 diff --git a/ports/stm32/modules/py_audio.c b/ports/stm32/modules/py_audio.c index 899e6f0b6..8c5e24a7e 100644 --- a/ports/stm32/modules/py_audio.c +++ b/ports/stm32/modules/py_audio.c @@ -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 @@ -378,7 +378,7 @@ static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *k __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]); + stm_dma_set_irq_descr(OMV_DFSDM_FLT0_DMA_STREAM, &hdma_filter[0]); // Initialize the DMA stream HAL_DMA_DeInit(&hdma_filter[0]); @@ -474,7 +474,7 @@ static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *k } // 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); diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 81ad2a197..c0c1ae17d 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -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) @@ -129,7 +129,7 @@ static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) { } // Set DMA IRQ handle - dma_utils_set_irq_descr(DMA2_Stream1, &csi->dma); + stm_dma_set_irq_descr(DMA2_Stream1, &csi->dma); // Configure the DMA IRQ Channel NVIC_SetPriority(DMA2_Stream1_IRQn, IRQ_PRI_DMA21); diff --git a/ports/stm32/omv_portconfig.mk b/ports/stm32/omv_portconfig.mk index cc6a8ea07..2d4f56a5a 100644 --- a/ports/stm32/omv_portconfig.mk +++ b/ports/stm32/omv_portconfig.mk @@ -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:]') diff --git a/ports/stm32/omv_spi.c b/ports/stm32/omv_spi.c index 2eb644d9f..cd0bc397f 100644 --- a/ports/stm32/omv_spi.c +++ b/ports/stm32/omv_spi.c @@ -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" @@ -397,10 +397,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); diff --git a/ports/stm32/stm32.ld.S b/ports/stm32/stm.ld.S similarity index 100% rename from ports/stm32/stm32.ld.S rename to ports/stm32/stm.ld.S diff --git a/ports/stm32/dma_utils.c b/ports/stm32/stm_dma.c similarity index 84% rename from ports/stm32/dma_utils.c rename to ports/stm32/stm_dma.c index 1eb770c2c..66f665729 100644 --- a/ports/stm32/dma_utils.c +++ b/ports/stm32/stm_dma.c @@ -29,7 +29,7 @@ #include "omv_boardconfig.h" #include "omv_common.h" -#include "dma_utils.h" +#include "stm_dma.h" #if defined(GPDMA1) static DMA_HandleTypeDef *dma_handle[32]; @@ -38,7 +38,7 @@ static DMA_HandleTypeDef *dma_handle[32]; extern DMA_HandleTypeDef *dma_handle[16]; #endif -uint8_t dma_utils_channel_to_irqn(void *dma_channel) { +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) { @@ -63,7 +63,7 @@ uint8_t dma_utils_channel_to_irqn(void *dma_channel) { // 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) { +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) { @@ -85,8 +85,8 @@ uint8_t dma_utils_channel_to_id(void *dma_channel) { 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); +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; @@ -94,7 +94,7 @@ int dma_utils_set_irq_descr(void *dma_channel, DMA_HandleTypeDef *dma_descr) { return -1; } -uint8_t dma_utils_mpu_region_size(uint32_t size) { +uint8_t stm_dma_mpu_region_size(uint32_t size) { #if (__ARM_ARCH <= 7) switch (size) { case 0x00000020U: { @@ -187,137 +187,137 @@ uint8_t dma_utils_mpu_region_size(uint32_t size) { } #if defined(GPDMA1) -static inline void dma_utils_irq_handler(size_t irqn) { +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) { - dma_utils_irq_handler(0); + stm_dma_irq_handler(0); } void GPDMA1_Channel1_IRQHandler(void) { - dma_utils_irq_handler(1); + stm_dma_irq_handler(1); } void GPDMA1_Channel2_IRQHandler(void) { - dma_utils_irq_handler(2); + stm_dma_irq_handler(2); } void GPDMA1_Channel3_IRQHandler(void) { - dma_utils_irq_handler(3); + stm_dma_irq_handler(3); } void GPDMA1_Channel4_IRQHandler(void) { - dma_utils_irq_handler(4); + stm_dma_irq_handler(4); } void GPDMA1_Channel5_IRQHandler(void) { - dma_utils_irq_handler(5); + stm_dma_irq_handler(5); } void GPDMA1_Channel6_IRQHandler(void) { - dma_utils_irq_handler(6); + stm_dma_irq_handler(6); } void GPDMA1_Channel7_IRQHandler(void) { - dma_utils_irq_handler(7); + stm_dma_irq_handler(7); } void GPDMA1_Channel8_IRQHandler(void) { - dma_utils_irq_handler(8); + stm_dma_irq_handler(8); } void GPDMA1_Channel9_IRQHandler(void) { - dma_utils_irq_handler(9); + stm_dma_irq_handler(9); } void GPDMA1_Channel10_IRQHandler(void) { - dma_utils_irq_handler(10); + stm_dma_irq_handler(10); } void GPDMA1_Channel11_IRQHandler(void) { - dma_utils_irq_handler(11); + stm_dma_irq_handler(11); } void GPDMA1_Channel12_IRQHandler(void) { - dma_utils_irq_handler(12); + stm_dma_irq_handler(12); } void GPDMA1_Channel13_IRQHandler(void) { - dma_utils_irq_handler(13); + stm_dma_irq_handler(13); } void GPDMA1_Channel14_IRQHandler(void) { - dma_utils_irq_handler(14); + stm_dma_irq_handler(14); } void GPDMA1_Channel15_IRQHandler(void) { - dma_utils_irq_handler(15); + stm_dma_irq_handler(15); } void HPDMA1_Channel0_IRQHandler(void) { - dma_utils_irq_handler(16); + stm_dma_irq_handler(16); } void HPDMA1_Channel1_IRQHandler(void) { - dma_utils_irq_handler(17); + stm_dma_irq_handler(17); } void HPDMA1_Channel2_IRQHandler(void) { - dma_utils_irq_handler(18); + stm_dma_irq_handler(18); } void HPDMA1_Channel3_IRQHandler(void) { - dma_utils_irq_handler(19); + stm_dma_irq_handler(19); } void HPDMA1_Channel4_IRQHandler(void) { - dma_utils_irq_handler(20); + stm_dma_irq_handler(20); } void HPDMA1_Channel5_IRQHandler(void) { - dma_utils_irq_handler(21); + stm_dma_irq_handler(21); } void HPDMA1_Channel6_IRQHandler(void) { - dma_utils_irq_handler(22); + stm_dma_irq_handler(22); } void HPDMA1_Channel7_IRQHandler(void) { - dma_utils_irq_handler(23); + stm_dma_irq_handler(23); } void HPDMA1_Channel8_IRQHandler(void) { - dma_utils_irq_handler(24); + stm_dma_irq_handler(24); } void HPDMA1_Channel9_IRQHandler(void) { - dma_utils_irq_handler(25); + stm_dma_irq_handler(25); } void HPDMA1_Channel10_IRQHandler(void) { - dma_utils_irq_handler(26); + stm_dma_irq_handler(26); } void HPDMA1_Channel11_IRQHandler(void) { - dma_utils_irq_handler(27); + stm_dma_irq_handler(27); } void HPDMA1_Channel12_IRQHandler(void) { - dma_utils_irq_handler(28); + stm_dma_irq_handler(28); } void HPDMA1_Channel13_IRQHandler(void) { - dma_utils_irq_handler(29); + stm_dma_irq_handler(29); } void HPDMA1_Channel14_IRQHandler(void) { - dma_utils_irq_handler(30); + stm_dma_irq_handler(30); } void HPDMA1_Channel15_IRQHandler(void) { - dma_utils_irq_handler(31); + stm_dma_irq_handler(31); } #endif diff --git a/ports/stm32/dma_utils.h b/ports/stm32/stm_dma.h similarity index 71% rename from ports/stm32/dma_utils.h rename to ports/stm32/stm_dma.h index a30f6c849..bf8671a49 100644 --- a/ports/stm32/dma_utils.h +++ b/ports/stm32/stm_dma.h @@ -23,16 +23,10 @@ * * 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__ +#ifndef __STM_DMA_H__ +#define __STM_DMA_H__ +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); +#endif // __STM_DMA_H__ diff --git a/ports/stm32/stm32_hal_msp.c b/ports/stm32/stm_hal.c similarity index 99% rename from ports/stm32/stm32_hal_msp.c rename to ports/stm32/stm_hal.c index 45f3c4b6f..45fc19603 100644 --- a/ports/stm32/stm32_hal_msp.c +++ b/ports/stm32/stm_hal.c @@ -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); } } diff --git a/ports/stm32/jpeg.c b/ports/stm32/stm_jpeg.c similarity index 99% rename from ports/stm32/jpeg.c rename to ports/stm32/stm_jpeg.c index b7b6fe6f0..ff675518a 100644 --- a/ports/stm32/jpeg.c +++ b/ports/stm32/stm_jpeg.c @@ -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) diff --git a/ports/stm32/stm32_nema.c b/ports/stm32/stm_nema.c similarity index 100% rename from ports/stm32/stm32_nema.c rename to ports/stm32/stm_nema.c diff --git a/ports/stm32/xspi_psram.c b/ports/stm32/stm_xspi.c similarity index 98% rename from ports/stm32/xspi_psram.c rename to ports/stm32/stm_xspi.c index 33b5716e0..27835897d 100644 --- a/ports/stm32/xspi_psram.c +++ b/ports/stm32/stm_xspi.c @@ -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; diff --git a/ports/stm32/xspi_psram.h b/ports/stm32/stm_xspi.h similarity index 93% rename from ports/stm32/xspi_psram.h rename to ports/stm32/stm_xspi.h index 2fd93bf42..3910d8350 100644 --- a/ports/stm32/xspi_psram.h +++ b/ports/stm32/stm_xspi.h @@ -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__ From 2b4d66ba238475595af8fc492c6fe960bc678daa Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 3 Jul 2025 20:43:16 +0200 Subject: [PATCH 2/5] ports/stm32: Refactor MDMA code. Moved to separate file to make omv_csi more readable. Signed-off-by: iabdalkader --- ports/stm32/omv_csi.c | 151 +++--------------------------------------- ports/stm32/stm_dma.c | 134 +++++++++++++++++++++++++++++++++++++ ports/stm32/stm_dma.h | 13 ++++ 3 files changed, 158 insertions(+), 140 deletions(-) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index c0c1ae17d..9e24c0e69 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -50,7 +50,6 @@ #if defined(OMV_MDMA_CHANNEL_DCMI_0) #define USE_MDMA (1) -#define MDMA_BUFFER_SIZE (64) #endif #if !defined(DCMIPP) @@ -590,22 +589,12 @@ 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); + stm_mdma_start(csi, (uint32_t) src, (uint32_t) dst, fb->u * bytes_per_pixel, fb->v); HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn); return; } @@ -618,7 +607,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 +615,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,9 +735,11 @@ 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 @@ -889,10 +761,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 diff --git a/ports/stm32/stm_dma.c b/ports/stm32/stm_dma.c index 66f665729..6bfd4f599 100644 --- a/ports/stm32/stm_dma.c +++ b/ports/stm32/stm_dma.c @@ -186,6 +186,140 @@ uint8_t stm_dma_mpu_region_size(uint32_t size) { return -1; } +#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. +static 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) { diff --git a/ports/stm32/stm_dma.h b/ports/stm32/stm_dma.h index bf8671a49..81892ae75 100644 --- a/ports/stm32/stm_dma.h +++ b/ports/stm32/stm_dma.h @@ -29,4 +29,17 @@ 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); + +#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__ From cf0a58372d603864519ee863b709c683ad03f7c8 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 4 Jul 2025 17:48:38 +0200 Subject: [PATCH 3/5] boards/all: Define CSI DMA channel, request and ports. Define CSI DMA channel (instance), request and DMA transfer and linked-list ports for N6. Signed-off-by: iabdalkader --- boards/ARDUINO_GIGA/omv_boardconfig.h | 2 ++ boards/ARDUINO_NICLA_VISION/omv_boardconfig.h | 2 ++ boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h | 2 ++ boards/OPENMV2/omv_boardconfig.h | 2 ++ boards/OPENMV3/omv_boardconfig.h | 2 ++ boards/OPENMV4/omv_boardconfig.h | 2 ++ boards/OPENMV4P/omv_boardconfig.h | 2 ++ boards/OPENMVPT/omv_boardconfig.h | 2 ++ boards/OPENMV_N6/omv_boardconfig.h | 6 ++++++ 9 files changed, 22 insertions(+) diff --git a/boards/ARDUINO_GIGA/omv_boardconfig.h b/boards/ARDUINO_GIGA/omv_boardconfig.h index 1df679c13..3fae54b68 100644 --- a/boards/ARDUINO_GIGA/omv_boardconfig.h +++ b/boards/ARDUINO_GIGA/omv_boardconfig.h @@ -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) diff --git a/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h b/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h index 1f7ecde85..f7037a70c 100644 --- a/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h +++ b/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h @@ -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) diff --git a/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h b/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h index 1b0c6f476..5dd69930a 100644 --- a/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h +++ b/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h @@ -228,6 +228,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) diff --git a/boards/OPENMV2/omv_boardconfig.h b/boards/OPENMV2/omv_boardconfig.h index c63d94dd9..f745ddd77 100644 --- a/boards/OPENMV2/omv_boardconfig.h +++ b/boards/OPENMV2/omv_boardconfig.h @@ -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) diff --git a/boards/OPENMV3/omv_boardconfig.h b/boards/OPENMV3/omv_boardconfig.h index c767242c3..26fa29a78 100644 --- a/boards/OPENMV3/omv_boardconfig.h +++ b/boards/OPENMV3/omv_boardconfig.h @@ -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) diff --git a/boards/OPENMV4/omv_boardconfig.h b/boards/OPENMV4/omv_boardconfig.h index c35f103fa..41c088028 100644 --- a/boards/OPENMV4/omv_boardconfig.h +++ b/boards/OPENMV4/omv_boardconfig.h @@ -218,6 +218,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) diff --git a/boards/OPENMV4P/omv_boardconfig.h b/boards/OPENMV4P/omv_boardconfig.h index e6acb55c3..af6e7a535 100644 --- a/boards/OPENMV4P/omv_boardconfig.h +++ b/boards/OPENMV4P/omv_boardconfig.h @@ -218,6 +218,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) diff --git a/boards/OPENMVPT/omv_boardconfig.h b/boards/OPENMVPT/omv_boardconfig.h index d1f5f1b82..0247ecf70 100644 --- a/boards/OPENMVPT/omv_boardconfig.h +++ b/boards/OPENMVPT/omv_boardconfig.h @@ -210,6 +210,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) diff --git a/boards/OPENMV_N6/omv_boardconfig.h b/boards/OPENMV_N6/omv_boardconfig.h index 9a8d06fb8..c85b82069 100644 --- a/boards/OPENMV_N6/omv_boardconfig.h +++ b/boards/OPENMV_N6/omv_boardconfig.h @@ -229,6 +229,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) @@ -262,6 +264,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) @@ -277,6 +281,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) From 6406e719ce404202d0d1c65e25decd4c84841353 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 4 Jul 2025 17:57:19 +0200 Subject: [PATCH 4/5] ports/stm32: Add DMA init functions. Signed-off-by: iabdalkader --- ports/stm32/stm_dma.c | 246 +++++++++++++++++++++++++++++++++++++++++- ports/stm32/stm_dma.h | 18 ++++ 2 files changed, 262 insertions(+), 2 deletions(-) diff --git a/ports/stm32/stm_dma.c b/ports/stm32/stm_dma.c index 6bfd4f599..be2f6a6c0 100644 --- a/ports/stm32/stm_dma.c +++ b/ports/stm32/stm_dma.c @@ -24,6 +24,8 @@ * STM32 DMA helper functions. */ #include +#include +#include #include STM32_HAL_H #include "py/mphal.h" @@ -38,6 +40,86 @@ static DMA_HandleTypeDef *dma_handle[32]; 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) @@ -186,6 +268,165 @@ uint8_t stm_dma_mpu_region_size(uint32_t size) { 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; iInitLinkedList.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; @@ -210,8 +451,7 @@ void stm_mdma_init(omv_csi_t *csi, uint32_t bytes_per_pixel, uint32_t x_crop) { } // Configures an MDMA channel to completely offload the CPU in copying one line of pixels. -static void stm_mdma_init_channel(omv_csi_t *csi, - MDMA_InitTypeDef *init, 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) { framebuffer_t *fb = csi->fb; init->Request = MDMA_REQUEST_SW; @@ -390,7 +630,9 @@ void GPDMA1_Channel14_IRQHandler(void) { 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); } diff --git a/ports/stm32/stm_dma.h b/ports/stm32/stm_dma.h index 81892ae75..fd3119433 100644 --- a/ports/stm32/stm_dma.h +++ b/ports/stm32/stm_dma.h @@ -25,10 +25,28 @@ */ #ifndef __STM_DMA_H__ #define __STM_DMA_H__ +#include +#include +#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" From 7934ea0a1ec7244f951940f43e83c43e4165858b Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 4 Jul 2025 17:58:02 +0200 Subject: [PATCH 5/5] ports/stm32: Refactor DMA code. Signed-off-by: iabdalkader --- ports/stm32/modules/py_audio.c | 100 +++++++------------------ ports/stm32/omv_csi.c | 50 +++++-------- ports/stm32/omv_portconfig.h | 1 + ports/stm32/omv_spi.c | 129 +++++++++++---------------------- 4 files changed, 87 insertions(+), 193 deletions(-) diff --git a/ports/stm32/modules/py_audio.c b/ports/stm32/modules/py_audio.c index 8c5e24a7e..b76ed9f1f 100644 --- a/ports/stm32/modules/py_audio.c +++ b/ports/stm32/modules/py_audio.c @@ -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,28 +354,18 @@ 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 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. NVIC_SetPriority(OMV_DFSDM_FLT0_DMA_IRQ, IRQ_PRI_DMA21); HAL_NVIC_EnableIRQ(OMV_DFSDM_FLT0_DMA_IRQ); @@ -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,54 +408,21 @@ 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 stm_dma_set_irq_descr(OMV_MDF_FLT0_DMA_STREAM, &hdma_filter[0]); diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 9e24c0e69..69f843c9c 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -62,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; @@ -102,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 - stm_dma_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); @@ -341,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 @@ -493,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. @@ -540,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; @@ -595,7 +583,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) { // maximize the time before the frame has to be dropped. if (!csi->transpose) { stm_mdma_start(csi, (uint32_t) src, (uint32_t) dst, fb->u * bytes_per_pixel, fb->v); - HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn); + HAL_NVIC_DisableIRQ(csi->dma_irqn); return; } #endif @@ -745,7 +733,7 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // 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. diff --git a/ports/stm32/omv_portconfig.h b/ports/stm32/omv_portconfig.h index 9de33ab42..14680062f 100644 --- a/ports/stm32/omv_portconfig.h +++ b/ports/stm32/omv_portconfig.h @@ -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 \ }; diff --git a/ports/stm32/omv_spi.c b/ports/stm32/omv_spi.c index cd0bc397f..ed4c5b14d 100644 --- a/ports/stm32/omv_spi.c +++ b/ports/stm32/omv_spi.c @@ -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) {