From 908d612ace9293a9aeff5d08cf9e14b3248e08c6 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Fri, 15 Aug 2025 13:36:16 -0700 Subject: [PATCH 01/10] ports/stm32: Fix SystemClock_config() hard faulting. --- ports/stm32/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 1bfda089e..a3f631aaf 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -137,6 +137,9 @@ int main(void) { #endif bool first_soft_reset = true; + // Initialize SysTick. + HAL_InitTick(TICK_INT_PRIORITY); + // Configure PLLs, oscillators, and system/peripheral clocks SystemClock_Config(); From cc137456dc1b37e44afe852a62ff0654ee0e461b Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Thu, 14 Aug 2025 19:06:43 -0700 Subject: [PATCH 02/10] ports/stm32: Only enable frame int manually in jpeg mode. The frame interrupt was being enabled for non-JPEG transfers, causing massive image corruption and out-of-sync issues on the M4 and M7. This interrupt only needs to be enabled for JPEG mode where the size of the image is not known. --- ports/stm32/omv_csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 822311e89..4fc9410fb 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -626,7 +626,7 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } // In JPEG mode, enable the end of frame interrupt. - if (!csi->mipi_if && csi->pixformat != PIXFORMAT_JPEG) { + if (!csi->mipi_if && csi->pixformat == PIXFORMAT_JPEG) { __HAL_DCMI_ENABLE_IT(&csi->dcmi, DCMI_IT_FRAME); } From 080641c8270372dd3ce553cc4c9dcd3439a8cf12 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Thu, 14 Aug 2025 19:07:22 -0700 Subject: [PATCH 03/10] ports/stm32: Fix DMA line size error calculation. The error calculation needs to be on the raw byte size. --- ports/stm32/omv_csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 4fc9410fb..475288751 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -572,7 +572,7 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { #if defined(OMV_LINE_BUF_SIZE) (line_width > (OMV_LINE_BUF_SIZE / 2)) || #endif - (!csi->dma_size) || (csi->dma_size % OMV_CSI_LINE_ALIGNMENT)) { + (!csi->dma_size) || ((line_width * fb->v) % OMV_CSI_LINE_ALIGNMENT)) { return OMV_CSI_ERROR_INVALID_FRAMESIZE; } From 2c14af97e74fe8c7934170d64d2c3b5ec11ed4d4 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Thu, 14 Aug 2025 19:07:59 -0700 Subject: [PATCH 04/10] ports/stm32: Fix JPEG mode 3 support. --- ports/stm32/omv_csi.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 475288751..78b103cc0 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -264,8 +264,6 @@ static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) { // Stop the DCMI from generating more DMA requests, and disable the DMA. static int stm_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) { - csi->dma_size = 0; - if (!stm_csi_is_active(csi)) { return 0; } @@ -652,7 +650,7 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // In JPEG 3 mode, the transfer must be aborted as it waits for data indefinitely. if (!csi->mipi_if && (csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3)) { - omv_csi_abort(csi, true, false); + omv_csi_abort(csi, false, false); } // The JPEG in the framebuffer is actually invalid. @@ -689,12 +687,17 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // Offset is the total frame size. size = buffer->offset; } else { + // HAL_DCMI_Start_DMA splits bigger transfers. + if (csi->dma_size > (OMV_CSI_DMA_MAX_SIZE / 4)) { + csi->dma_size /= 2; + } + // Offset is the number of length-size transfers performed. - size = buffer->offset * csi->dma_size / 2; + size = buffer->offset * csi->dma_size * 4; // The DMA counter holds the number of bytes per transfer. if (!csi->mipi_if && __HAL_DMA_GET_COUNTER(&csi->dma)) { // Add in the uncompleted transfer length. - size += ((csi->dma_size / 2) - __HAL_DMA_GET_COUNTER(&csi->dma)) * 4; + size += (csi->dma_size - __HAL_DMA_GET_COUNTER(&csi->dma)) * 4; } } // Clean trailing data after 0xFFD9 at the end of the jpeg byte stream. From 76fc074c0f12ecfedae3337ff0dd572d3f81d815 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Thu, 14 Aug 2025 19:29:02 -0700 Subject: [PATCH 05/10] ports/stm32: Set oneshot on MDMA snapshot to invalidate. --- ports/stm32/omv_csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 78b103cc0..298d15310 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -609,6 +609,7 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // 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; + csi->one_shot = true; HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_CONTINUOUS, (uint32_t) &_line_buf, line_width / 4); #endif // USE_MDMA } else { From 2e525c504b253c036855772ed28c1db4c1164a09 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Fri, 15 Aug 2025 18:08:56 -0700 Subject: [PATCH 06/10] ports/stm32: Fix N6 csi camera config settings. Fixes support for mono bpp == 2 and rgb/yuv swap. --- ports/stm32/omv_csi.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 298d15310..42f516902 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -244,6 +244,24 @@ static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) { if (!csi->mipi_if) { DCMI->CR &= ~(DCMI_CR_JPEG_Msk << DCMI_CR_JPEG_Pos); DCMI->CR |= (csi->pixformat == PIXFORMAT_JPEG) ? DCMI_JPEG_ENABLE : DCMI_JPEG_DISABLE; + #if defined(STM32N6) + // Handle YUV422 Source -> Y Destination using DCMI byte drop. + if (csi->pixformat == PIXFORMAT_GRAYSCALE && csi->mono_bpp == 2) { + DCMI->CR |= DCMI_CR_BSM_0; + } else { + DCMI->CR &= ~DCMI_CR_BSM_0; + } + + // Turn on/off byte swapping for RGB/YUV formats. + for (size_t i = 0; i < OMV_ARRAY_SIZE(dma_nodes); i++) { + if ((csi->pixformat == PIXFORMAT_RGB565 && csi->rgb_swap) || + (csi->pixformat == PIXFORMAT_YUV422 && csi->yuv_swap)) { + dma_nodes[i].LinkRegisters[NODE_CTR1_DEFAULT_OFFSET] |= DMA_CTR1_DBX; + } else { + dma_nodes[i].LinkRegisters[NODE_CTR1_DEFAULT_OFFSET] &= ~DMA_CTR1_DBX; + } + } + #endif } else { #if USE_DCMIPP csi->dcmipp.State = HAL_DCMIPP_STATE_READY; @@ -617,6 +635,11 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // Start a multibuffer (line by line) transfer. HAL_DCMI_Start_DMA_MB(&csi->dcmi, DCMI_MODE_CONTINUOUS, (uint32_t) &_line_buf, csi->dma_size, fb->v); #else + // Handle YUV422 Source -> Y Destination using DCMI byte drop. + if (csi->pixformat == PIXFORMAT_GRAYSCALE && csi->mono_bpp == 2) { + csi->dma_size /= 2; + } + csi->one_shot = true; HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_SNAPSHOT, (uint32_t) buffer->data, csi->dma_size); #endif From feaca6af12e58b0f12d55f2863aff1ca891d0e44 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Fri, 15 Aug 2025 17:23:49 -0700 Subject: [PATCH 07/10] ports/stm32: Fix DMA transfer of small buffer sizes. HAL_DCMI_Start_DMA() only sets up one node in the circular linked-list when the transfer size is < 64KB. This causes a user setting transfer error which locks up the DMA hardware. The fix is to dynamically make the link list linear on smaller sizes and circular on larger sizes. --- ports/stm32/omv_csi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 42f516902..92c06d80e 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -640,6 +640,13 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { csi->dma_size /= 2; } + // Disable circular mode for transfer sizes less than 64KB. + if (csi->dma_size * 4 <= OMV_CSI_DMA_MAX_SIZE / 4) { + HAL_DMAEx_List_ClearCircularMode(&dma_queue); + } else { + HAL_DMAEx_List_SetCircularMode(&dma_queue); + } + csi->one_shot = true; HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_SNAPSHOT, (uint32_t) buffer->data, csi->dma_size); #endif From 9778028f40b46e255eea2eb469f52d7638dcd487 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Fri, 15 Aug 2025 17:45:50 -0700 Subject: [PATCH 08/10] ports/stm32: Add N6 error message for unsupported modes. --- ports/stm32/omv_csi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 92c06d80e..c39cebd55 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -592,6 +592,14 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { return OMV_CSI_ERROR_INVALID_FRAMESIZE; } + #if defined(STM32N6) + // The N6 DCMI driver currently does not support any of these modes. + if (csi->pixformat == PIXFORMAT_JPEG || csi->transpose || + fb->x != 0 || fb->u != csi->resolution[csi->framesize][0]) { + return OMV_CSI_ERROR_CAPTURE_FAILED; + } + #endif + HAL_DCMI_DisableCrop(&csi->dcmi); if (csi->pixformat != PIXFORMAT_JPEG) { // Vertically crop the image. Horizontal cropping is done in software. From 170a7eb107b45196cd3f29f9ef9833ddfdd58502 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Sat, 16 Aug 2025 18:23:52 -0700 Subject: [PATCH 09/10] ports/stm32: Move dma_queue to port bits. --- ports/stm32/omv_csi.c | 9 ++++----- ports/stm32/omv_portconfig.h | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index c39cebd55..c82b3eec6 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -83,8 +83,7 @@ extern uint8_t _line_buf; static omv_csi_t *stm_csi_all[2] = { 0 }; #if defined(STM32N6) -static DMA_QListTypeDef dma_queue; -// Nodes can't be places in CSI state because the need to be uncacheable. +// Nodes can't be placed in CSI state because they need to be uncacheable. static DMA_NodeTypeDef OMV_ATTR_SECTION(dma_nodes[2], ".dma_buffer"); #endif @@ -139,7 +138,7 @@ static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) { #if defined(STM32N6) // Initialize DMA in circular mode. - if (stm_dma_ll_init(&csi->dma, &dma_queue, dma_nodes, + if (stm_dma_ll_init(&csi->dma, &csi->dma_queue, dma_nodes, OMV_ARRAY_SIZE(dma_nodes), OMV_CSI_DMA_LIST_PORTS)) { return OMV_CSI_ERROR_CSI_INIT_FAILED; } @@ -650,9 +649,9 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // Disable circular mode for transfer sizes less than 64KB. if (csi->dma_size * 4 <= OMV_CSI_DMA_MAX_SIZE / 4) { - HAL_DMAEx_List_ClearCircularMode(&dma_queue); + HAL_DMAEx_List_ClearCircularMode(&csi->dma_queue); } else { - HAL_DMAEx_List_SetCircularMode(&dma_queue); + HAL_DMAEx_List_SetCircularMode(&csi->dma_queue); } csi->one_shot = true; diff --git a/ports/stm32/omv_portconfig.h b/ports/stm32/omv_portconfig.h index eb3f77d47..80f34e12a 100644 --- a/ports/stm32/omv_portconfig.h +++ b/ports/stm32/omv_portconfig.h @@ -152,6 +152,7 @@ typedef I2C_HandleTypeDef *omv_i2c_dev_t; #define OMV_CSI_PORT_BITS_DCMIPP \ struct { \ DCMIPP_HandleTypeDef dcmipp; \ + DMA_QListTypeDef dma_queue; \ }; #else #define OMV_CSI_PORT_BITS_DCMIPP From c601c706fe82be0c7a2e2c089a89c2eceb1e7c47 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Sat, 16 Aug 2025 18:50:28 -0700 Subject: [PATCH 10/10] ports/stm32: Simplify CSI_DMA_MAX_SIZE logic. --- ports/stm32/omv_csi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index c82b3eec6..e3cc7b4e5 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -66,7 +66,7 @@ #endif #ifndef OMV_CSI_DMA_MAX_SIZE -#define OMV_CSI_DMA_MAX_SIZE (0xFFFFU * 4U) +#define OMV_CSI_DMA_MAX_SIZE (0xFFFFU) #endif #ifndef OMV_CSI_LINE_ALIGNMENT @@ -625,8 +625,8 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // differing only in size, with an interrupt after every half of the transfer. if ((csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3)) { // Start a one-shot transfer to the framebuffer, used only for JPEG mode 3. - uint32_t size = framebuffer_get_buffer_size(fb); - csi->dma_size = IM_MIN(size, (OMV_CSI_DMA_MAX_SIZE * 2U)) / 4; + uint32_t size = framebuffer_get_buffer_size(fb) / 4; + csi->dma_size = IM_MIN(size, OMV_CSI_DMA_MAX_SIZE * 2U); csi->one_shot = true; HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_SNAPSHOT, (uint32_t) buffer->data, csi->dma_size); #if USE_MDMA @@ -648,7 +648,7 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } // Disable circular mode for transfer sizes less than 64KB. - if (csi->dma_size * 4 <= OMV_CSI_DMA_MAX_SIZE / 4) { + if (csi->dma_size * 4 <= OMV_CSI_DMA_MAX_SIZE) { HAL_DMAEx_List_ClearCircularMode(&csi->dma_queue); } else { HAL_DMAEx_List_SetCircularMode(&csi->dma_queue); @@ -726,7 +726,7 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { size = buffer->offset; } else { // HAL_DCMI_Start_DMA splits bigger transfers. - if (csi->dma_size > (OMV_CSI_DMA_MAX_SIZE / 4)) { + if (csi->dma_size > OMV_CSI_DMA_MAX_SIZE) { csi->dma_size /= 2; }