Merge pull request #2603 from openmv/n6_port
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: Add N6 support.
This commit is contained in:
Ibrahim Abdelkader 2025-06-19 17:47:33 +03:00 committed by GitHub
commit 5534b34880
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 2297 additions and 205 deletions

View File

@ -42,6 +42,7 @@ jobs:
- OPENMVPT
- OPENMV_RT1060
- OPENMV_AE3
- OPENMV_N6
- ARDUINO_PORTENTA_H7
- ARDUINO_GIGA
- ARDUINO_NICLA_VISION

View File

@ -198,6 +198,11 @@ MPY_CFLAGS += -DMICROPY_PY_ML_TFLM=1
MPY_MKARGS += MICROPY_PY_ML_TFLM=1
endif
ifeq ($(MICROPY_PY_ML_STAI), 1)
MPY_CFLAGS += -DMICROPY_PY_ML_STAI=1
MICROPY_ARGS += MICROPY_PY_ML_STAI=1
endif
MPY_PENDSV_ENTRIES := $(shell echo $(MPY_PENDSV_ENTRIES) | tr -d '[:space:]')
MPY_CFLAGS += -DMICROPY_BOARD_PENDSV_ENTRIES="$(MPY_PENDSV_ENTRIES)"
MPY_CFLAGS += -DMP_CONFIGFILE=\<$(OMV_PORT_DIR)/omv_mpconfigport.h\>

View File

@ -0,0 +1,149 @@
/*
* This file is part of the OpenMV project.
*
* Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
* Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
*
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Image library configuration.
*/
#ifndef __IMLIB_CONFIG_H__
#define __IMLIB_CONFIG_H__
// Enable Image I/O
#define IMLIB_ENABLE_IMAGE_IO
// Enable Image File I/O
#define IMLIB_ENABLE_IMAGE_FILE_IO
// Enable LAB LUT
#define IMLIB_ENABLE_LAB_LUT
// Enable YUV LUT
//#define IMLIB_ENABLE_YUV_LUT
// Enable ISP ops
#define IMLIB_ENABLE_ISP_OPS
// Enable binary ops
#define IMLIB_ENABLE_BINARY_OPS
// Enable math ops
#define IMLIB_ENABLE_MATH_OPS
// Enable flood_fill()
#define IMLIB_ENABLE_FLOOD_FILL
// Enable mean()
#define IMLIB_ENABLE_MEAN
// Enable median()
#define IMLIB_ENABLE_MEDIAN
// Enable mode()
#define IMLIB_ENABLE_MODE
// Enable midpoint()
#define IMLIB_ENABLE_MIDPOINT
// Enable morph()
#define IMLIB_ENABLE_MORPH
// Enable Gaussian
#define IMLIB_ENABLE_GAUSSIAN
// Enable Laplacian
#define IMLIB_ENABLE_LAPLACIAN
// Enable bilateral()
#define IMLIB_ENABLE_BILATERAL
// Enable linpolar()
#define IMLIB_ENABLE_LINPOLAR
// Enable logpolar()
#define IMLIB_ENABLE_LOGPOLAR
// Enable lens_corr()
#define IMLIB_ENABLE_LENS_CORR
// Enable rotation_corr()
#define IMLIB_ENABLE_ROTATION_CORR
// Enable phasecorrelate()
#if defined(IMLIB_ENABLE_ROTATION_CORR)
#define IMLIB_ENABLE_FIND_DISPLACEMENT
#endif
// Enable get_similarity()
#define IMLIB_ENABLE_GET_SIMILARITY
// Enable find_lines()
#define IMLIB_ENABLE_FIND_LINES
// Enable find_line_segments()
#define IMLIB_ENABLE_FIND_LINE_SEGMENTS
// Enable find_circles()
#define IMLIB_ENABLE_FIND_CIRCLES
// Enable find_rects()
#define IMLIB_ENABLE_FIND_RECTS
// Enable find_qrcodes() (14 KB)
#define IMLIB_ENABLE_QRCODES
// Enable find_apriltags() (64 KB)
#define IMLIB_ENABLE_APRILTAGS
#define IMLIB_ENABLE_APRILTAGS_TAG36H11
// Enable fine find_apriltags() - (8-way connectivity versus 4-way connectivity)
// #define IMLIB_ENABLE_FINE_APRILTAGS
// Enable high res find_apriltags() - uses more RAM
#define IMLIB_ENABLE_HIGH_RES_APRILTAGS
// Enable find_datamatrices() (26 KB)
#define IMLIB_ENABLE_DATAMATRICES
// Enable find_barcodes() (42 KB)
#define IMLIB_ENABLE_BARCODES
// Enable find_features() and Haar cascades.
#define IMLIB_ENABLE_FEATURES
// Enable Tensor Flow
#define IMLIB_ENABLE_STAI
// Enable AGAST.
#define IMLIB_ENABLE_AGAST
// Enable find_template()
#define IMLIB_FIND_TEMPLATE
// Enable find_lbp()
#define IMLIB_ENABLE_FIND_LBP
// Enable find_keypoints()
#if defined(IMLIB_ENABLE_FAST) || defined(IMLIB_ENABLE_AGAST)
#define IMLIB_ENABLE_FIND_KEYPOINTS
#endif
// Enable load, save and match descriptor
#define IMLIB_ENABLE_DESCRIPTOR
// Enable find_hog()
// #define IMLIB_ENABLE_HOG
// Enable selective_search()
// #define IMLIB_ENABLE_SELECTIVE_SEARCH
// Enable PNG encoder/decoder
#define IMLIB_ENABLE_PNG_ENCODER
#define IMLIB_ENABLE_PNG_DECODER
// Stereo Imaging
// #define IMLIB_ENABLE_STEREO_DISPARITY
#endif //__IMLIB_CONFIG_H__

View File

@ -0,0 +1,33 @@
include("$(MPY_DIR)/extmod/asyncio")
# Filesystem
freeze ("$(OMV_LIB_DIR)/", "_boot.py")
# Drivers
freeze ("$(OMV_LIB_DIR)/", "modbus.py")
freeze ("$(OMV_LIB_DIR)/", "pid.py")
freeze ("$(OMV_LIB_DIR)/", "bno055.py")
freeze ("$(OMV_LIB_DIR)/", "ssd1306.py")
freeze ("$(OMV_LIB_DIR)/", "tb6612.py")
freeze ("$(OMV_LIB_DIR)/", "vl53l1x.py")
freeze ("$(OMV_LIB_DIR)/", "machine.py")
freeze ("$(OMV_LIB_DIR)/", "display.py")
freeze ("$(OMV_LIB_DIR)/ml")
# Networking
require("ssl")
require("ntptime")
require("webrepl")
freeze ("$(OMV_LIB_DIR)/", "rpc.py")
freeze ("$(OMV_LIB_DIR)/", "rtsp.py")
freeze ("$(OMV_LIB_DIR)/", "mqtt.py")
freeze ("$(OMV_LIB_DIR)/", "requests.py")
# Utils
require("time")
require("logging")
require("collections-defaultdict")
require("types")
# Bluetooth
require("aioble")

View File

@ -0,0 +1,322 @@
/*
* This file is part of the OpenMV project.
*
* Copyright (c) 2013-2024 Ibrahim Abdelkader <iabdalkader@openmv.io>
* Copyright (c) 2013-2024 Kwabena W. Agyeman <kwagyeman@openmv.io>
*
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Board configuration and pin definitions.
*/
#ifndef __OMV_BOARDCONFIG_H__
#define __OMV_BOARDCONFIG_H__
// Architecture info
#define OMV_BOARD_ARCH "OpenMV N6" // 33 chars max
#define OMV_BOARD_TYPE "N6"
#define OMV_BOARD_UID_ADDR 0x46009014 // Unique ID address.
#define OMV_BOARD_UID_SIZE 3 // Unique ID size in words.
#define OMV_BOARD_UID_OFFSET 4 // Bytes offset for multi-word UIDs.
// JPEG compression settings.
#define OMV_JPEG_CODEC_ENABLE (1)
#define OMV_JPEG_QUALITY_LOW (50)
#define OMV_JPEG_QUALITY_HIGH (90)
#define OMV_JPEG_QUALITY_THRESHOLD (800 * 600 * 2)
// Enable RAW preview.
#define OMV_RAW_PREVIEW_ENABLE (1)
#define OMV_RAW_PREVIEW_WIDTH (512)
#define OMV_RAW_PREVIEW_HEIGHT (512)
// GPU Configuration
#define OMV_GPU_ENABLE (1)
#define OMV_GPU_NEMA (1)
#define OMV_GPU_NEMA_BUFFER_SIZE (32 * 1024)
#define OMV_OV7725_ENABLE (1)
#define OMV_OV7725_PLL_CONFIG (0x41) // x4
#define OMV_OV7725_BANDING (0x7F)
#define OMV_OV5640_ENABLE (1)
#define OMV_OV5640_AF_ENABLE (1)
#define OMV_OV5640_CLK_FREQ (24000000)
#define OMV_OV5640_PLL_CTRL2 (0x64)
#define OMV_OV5640_PLL_CTRL3 (0x13)
#define OMV_OV5640_REV_Y_CHECK (1)
#define OMV_OV5640_REV_Y_FREQ (25000000)
#define OMV_OV5640_REV_Y_CTRL2 (0x54)
#define OMV_OV5640_REV_Y_CTRL3 (0x13)
#define OMV_MT9V0XX_ENABLE (1)
#define OMV_LEPTON_ENABLE (1)
#define OMV_PAG7936_ENABLE (1)
#define OMV_PAG7936_MIPI_CSI2 (1)
#define OMV_PS5520_ENABLE (1)
#define OMV_GENX320_EHC_ENABLE (1)
#define OMV_GENX320_HSYNC_VALUE (0x1)
// FIR drivers configuration.
#define OMV_FIR_MLX90621_ENABLE (1)
#define OMV_FIR_MLX90640_ENABLE (1)
#define OMV_FIR_MLX90641_ENABLE (1)
#define OMV_FIR_AMG8833_ENABLE (1)
#define OMV_FIR_LEPTON_ENABLE (0)
// UMM heap block size
#define OMV_UMM_BLOCK_SIZE 256
// USB IRQn.
#define OMV_USB_IRQN (USB1_OTG_HS_IRQn)
//PLL1 800MHz
#define OMV_OSC_PLL1M (3)
#define OMV_OSC_PLL1N (50)
#define OMV_OSC_PLL1P1 (1)
#define OMV_OSC_PLL1P2 (1)
#define OMV_OSC_PLL1FRAC (0)
#define OMV_OSC_PLL1SOURCE RCC_PLLSOURCE_HSE
//PLL2 1000MHz
#define OMV_OSC_PLL2M (6)
#define OMV_OSC_PLL2N (125)
#define OMV_OSC_PLL2P1 (1)
#define OMV_OSC_PLL2P2 (1)
#define OMV_OSC_PLL2FRAC (0)
#define OMV_OSC_PLL2SOURCE RCC_PLLSOURCE_HSE
//PLL3 1200MHz
#define OMV_OSC_PLL3M (1)
#define OMV_OSC_PLL3N (25)
#define OMV_OSC_PLL3P1 (1)
#define OMV_OSC_PLL3P2 (1)
#define OMV_OSC_PLL3FRAC (0)
#define OMV_OSC_PLL3SOURCE RCC_PLLSOURCE_HSE
//PLL4 1200MHz
#define OMV_OSC_PLL4M (1)
#define OMV_OSC_PLL4N (25)
#define OMV_OSC_PLL4P1 (1)
#define OMV_OSC_PLL4P2 (1)
#define OMV_OSC_PLL4FRAC (0)
#define OMV_OSC_PLL4SOURCE RCC_PLLSOURCE_HSE
// Clock Sources
#define OMV_RCC_IC8_SOURCE (RCC_ICCLKSOURCE_PLL3)
#define OMV_RCC_IC8_CLKDIV (25)
#define OMV_RCC_IC9_SOURCE (RCC_ICCLKSOURCE_PLL1)
#define OMV_RCC_IC9_CLKDIV (10)
#define OMV_RCC_IC10_SOURCE (RCC_ICCLKSOURCE_PLL1)
#define OMV_RCC_IC10_CLKDIV (8)
#define OMV_RCC_IC14_SOURCE (RCC_ICCLKSOURCE_PLL1)
#define OMV_RCC_IC14_CLKDIV (10)
#define OMV_RCC_IC15_SOURCE (RCC_ICCLKSOURCE_PLL1)
#define OMV_RCC_IC15_CLKDIV (16)
#define OMV_RCC_IC17_SOURCE (RCC_ICCLKSOURCE_PLL3)
#define OMV_RCC_IC17_CLKDIV (4)
#define OMV_RCC_IC18_SOURCE (RCC_ICCLKSOURCE_PLL3)
#define OMV_RCC_IC18_CLKDIV (60)
#define OMV_OSC_I2C3_SOURCE (RCC_I2C3CLKSOURCE_IC10)
#define OMV_OSC_SPI5_SOURCE (RCC_SPI5CLKSOURCE_IC9)
#define OMV_OSC_DCMIPP_SOURCE (RCC_DCMIPPCLKSOURCE_IC17)
#define OMV_OSC_CSI_SOURCE (0) // has one clock source IC18
#define OMV_OSC_ADF1_SOURCE (RCC_ADF1CLKSOURCE_IC8)
// HSE/HSI/CSI State
#define OMV_OSC_HSE_STATE (RCC_HSE_ON)
#define OMV_OSC_HSI_STATE (RCC_HSI_ON)
#define OMV_OSC_HSI_DIV (RCC_HSI_DIV1)
#define OMV_OSC_HSI_CAL (RCC_HSICALIBRATION_DEFAULT)
// Power supply configuration
#define OMV_PWR_SUPPLY (PWR_SMPS_SUPPLY)
// Linker script constants (see the linker script template stm32fxxx.ld.S).
// Note: fb_alloc is a stack-based, dynamically allocated memory on FB.
// The maximum available fb_alloc memory = FB_ALLOC_SIZE + FB_SIZE - (w*h*bpp).
#define OMV_MAIN_MEMORY SRAM1 // Data/BSS memory
#define OMV_STACK_MEMORY SRAM1 // stack memory
#define OMV_RAMFUNC_MEMORY ITCM
#define OMV_STACK_SIZE (64K)
#define OMV_HEAP_MEMORY SRAM1 // libc/sbrk heap memory
#define OMV_HEAP_SIZE (256K)
#define OMV_FB_MEMORY DRAM // Framebuffer, fb_alloc
#define OMV_FB_SIZE (20M) // FB memory.
#define OMV_FB_ALLOC_SIZE (11M) // minimum fb_alloc size
#define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer.
#define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data).
#define OMV_VOSPI_MEMORY SRAM1 // VoSPI buffer memory.
#define OMV_VOSPI_SIZE (64K)
#define OMV_DMA_MEMORY SRAM1 // Misc DMA buffers memory.
#define OMV_GC_BLOCK0_MEMORY SRAM2 // Main GC block
#define OMV_GC_BLOCK0_SIZE (1M)
#define OMV_GC_BLOCK1_MEMORY DRAM // Main GC block
#define OMV_GC_BLOCK1_SIZE (32M)
#define OMV_MSC_BUF_SIZE (2K) // USB MSC bot data
// Memory map.
#define OMV_DTCM_ORIGIN 0x20000000
#define OMV_DTCM_LENGTH 128K
#define OMV_ITCM_ORIGIN 0x00000000
#define OMV_ITCM_LENGTH 64K
#define OMV_SRAM1_ORIGIN 0x24000000 // AXISRAM1
#define OMV_SRAM1_LENGTH 1M // 1MB
#define OMV_SRAM2_ORIGIN 0x24100000 // AXISRAM2
#define OMV_SRAM2_LENGTH 1M // 1MB
#define OMV_SRAM3_ORIGIN 0x24200000 // AXISRAM3
#define OMV_SRAM3_LENGTH 448K // 448KB
#define OMV_SRAM4_ORIGIN 0x24270000 // AXISRAM4
#define OMV_SRAM4_LENGTH 448K // 448KB
#define OMV_SRAM5_ORIGIN 0x242E0000 // AXISRAM5
#define OMV_SRAM5_LENGTH 448K // 448KB
#define OMV_SRAM6_ORIGIN 0x24350000 // AXISRAM6
#define OMV_SRAM6_LENGTH 448K // 448KB
#define OMV_SRAM7_ORIGIN 0x28000000 // AHBSRAM1 + AHBSRAM2 combined
#define OMV_SRAM7_LENGTH 32K // 16KB + 16KB = 32KB
#define OMV_DRAM_ORIGIN 0x90000000 // XSPI1
#define OMV_DRAM_LENGTH 64M // 512 Mbits (64 MBytes)
// Flash configuration.
#define OMV_FLASH_BOOT_ORIGIN 0x34180400
#define OMV_FLASH_BOOT_LENGTH 512K
#define OMV_FLASH_TXT_ORIGIN 0x70080000
#define OMV_FLASH_TXT_LENGTH 3584K
#define OMV_ROMFS_PART0_ORIGIN 0x70800000
#define OMV_ROMFS_PART0_LENGTH 0x01800000
// Enable additional GPIO ports.
#define OMV_GPIO_PORT_F_ENABLE (1)
#define OMV_GPIO_PORT_G_ENABLE (1)
#define OMV_GPIO_PORT_H_ENABLE (1)
#define OMV_GPIO_PORT_N_ENABLE (1)
#define OMV_GPIO_PORT_O_ENABLE (1)
#define OMV_GPIO_PORT_P_ENABLE (1)
#define OMV_GPIO_PORT_Q_ENABLE (1)
// Physical I2C buses.
// I2C bus 3
#define OMV_I2C2_ID (2)
#define OMV_I2C2_SCL_PIN (&omv_pin_B10_I2C2)
#define OMV_I2C2_SDA_PIN (&omv_pin_B11_I2C2)
// I2C bus 3
#define OMV_I2C3_ID (3)
#define OMV_I2C3_SCL_PIN (&omv_pin_A8_I2C3)
#define OMV_I2C3_SDA_PIN (&omv_pin_A9_I2C3)
// I2C bus 4
#define OMV_I2C4_ID (4)
#define OMV_I2C4_SCL_PIN (&omv_pin_E13_I2C4)
#define OMV_I2C4_SDA_PIN (&omv_pin_E14_I2C4)
// Physical SPI buses.
// SPI bus 5
#define OMV_SPI5_ID (5)
#define OMV_SPI5_SCLK_PIN (&omv_pin_E15_SPI5)
#define OMV_SPI5_MISO_PIN (&omv_pin_D4_SPI5)
#define OMV_SPI5_MOSI_PIN (&omv_pin_A4_SPI5)
#define OMV_SPI5_SSEL_PIN (&omv_pin_A3_SPI5)
#define OMV_SPI5_DMA_TX_CHANNEL (GPDMA1_Channel0)
#define OMV_SPI5_DMA_TX_REQUEST (GPDMA1_REQUEST_SPI5_TX)
#define OMV_SPI5_DMA_RX_CHANNEL (GPDMA1_Channel1)
#define OMV_SPI5_DMA_RX_REQUEST (GPDMA1_REQUEST_SPI5_RX)
// CSI SPI bus
#define OMV_CSI_SPI_ID (OMV_SPI5_ID)
// CSI I2C bus
#define OMV_CSI_I2C_ID (OMV_I2C3_ID)
#define OMV_CSI_I2C_SPEED (OMV_I2C_SPEED_STANDARD)
// FIR I2C bus
#define OMV_FIR_I2C_ID (OMV_I2C2_ID)
#define OMV_FIR_I2C_SPEED (OMV_I2C_SPEED_FULL)
// IMU SPI bus
#define OMV_IMU_I2C_ID (OMV_I2C4_ID)
#define OMV_IMU_I2C_SPEED (OMV_I2C_SPEED_FULL)
#define OMV_IMU_CHIP_LSM6DSM (1)
#define OMV_IMU_X_Y_ROTATION_DEGREES 90
#define OMV_IMU_MOUNTING_Z_DIRECTION -1
// MDF1
#define OMV_MDF (ADF1_Filter0)
#define OMV_MDF_PROC_CLKDIV (2) // 48MHz / 2 = 24MHz
#define OMV_MDF_CCKY_CLKDIV (12) // 24MHz / 12 = 2 MHz
#define OMV_AUDIO_MAX_CHANNELS (2)
#define OMV_MDF_CK_PIN (&omv_pin_E2_ADF1)
#define OMV_MDF_D1_PIN (&omv_pin_B2_ADF1)
#define OMV_MDF_FLT0_IRQ ADF1_FLT0_IRQn
#define OMV_MDF_FLT0_IRQHandler ADF1_FLT0_IRQHandler
#define OMV_MDF_FLT0_DMA_STREAM GPDMA1_Channel6
#define OMV_MDF_FLT0_DMA_REQUEST GPDMA1_REQUEST_ADF1_FLT0
#define OMV_MDF_FLT0_DMA_IRQ GPDMA1_Channel6_IRQn
// Camera Interface
#define OMV_CSI_CLK_SOURCE (OMV_CSI_CLK_SOURCE_TIM)
#define OMV_CSI_CLK_FREQUENCY (12000000)
#define OMV_CSI_TIM (TIM1)
#define OMV_CSI_TIM_PIN (&omv_pin_E9_TIM1)
#define OMV_CSI_TIM_CHANNEL (TIM_CHANNEL_1)
#define OMV_CSI_TIM_CLK_ENABLE() __TIM1_CLK_ENABLE()
#define OMV_CSI_TIM_CLK_DISABLE() __TIM1_CLK_DISABLE()
#define OMV_CSI_TIM_CLK_SLEEP_ENABLE() __TIM1_CLK_SLEEP_ENABLE()
#define OMV_CSI_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq()
#define OMV_CSI_DMA_CHANNEL (HPDMA1_Channel12)
#define OMV_CSI_DMA_REQUEST (HPDMA1_REQUEST_DCMI_PSSI)
#define OMV_CSI_DMA_MEMCPY_ENABLE (0)
#define OMV_CSI_HW_CROP_ENABLE (1)
#define OMV_CSI_D0_PIN (&omv_pin_A1_DCMI)
#define OMV_CSI_D1_PIN (&omv_pin_A10_DCMI)
#define OMV_CSI_D2_PIN (&omv_pin_E0_DCMI)
#define OMV_CSI_D3_PIN (&omv_pin_E10_DCMI)
#define OMV_CSI_D4_PIN (&omv_pin_B0_DCMI)
#define OMV_CSI_D5_PIN (&omv_pin_E5_DCMI)
#define OMV_CSI_D6_PIN (&omv_pin_G2_DCMI)
#define OMV_CSI_D7_PIN (&omv_pin_F1_DCMI)
#define OMV_CSI_HSYNC_PIN (&omv_pin_D0_DCMI)
#define OMV_CSI_VSYNC_PIN (&omv_pin_E6_DCMI)
#define OMV_CSI_PXCLK_PIN (&omv_pin_G1_DCMI)
#define OMV_CSI_RESET_PIN (&omv_pin_E3_GPIO)
#define OMV_CSI_POWER_PIN (&omv_pin_E1_GPIO)
//#define OMV_CSI_FSYNC_PIN (&omv_pin_B4_GPIO)
#define OMV_XSPI1_IO00_PIN (&omv_pin_P0_XSPIM_P1)
#define OMV_XSPI1_IO01_PIN (&omv_pin_P1_XSPIM_P1)
#define OMV_XSPI1_IO02_PIN (&omv_pin_P2_XSPIM_P1)
#define OMV_XSPI1_IO03_PIN (&omv_pin_P3_XSPIM_P1)
#define OMV_XSPI1_IO04_PIN (&omv_pin_P4_XSPIM_P1)
#define OMV_XSPI1_IO05_PIN (&omv_pin_P5_XSPIM_P1)
#define OMV_XSPI1_IO06_PIN (&omv_pin_P6_XSPIM_P1)
#define OMV_XSPI1_IO07_PIN (&omv_pin_P7_XSPIM_P1)
#define OMV_XSPI1_IO08_PIN (&omv_pin_P8_XSPIM_P1)
#define OMV_XSPI1_IO09_PIN (&omv_pin_P9_XSPIM_P1)
#define OMV_XSPI1_IO10_PIN (&omv_pin_P10_XSPIM_P1)
#define OMV_XSPI1_IO11_PIN (&omv_pin_P11_XSPIM_P1)
#define OMV_XSPI1_IO12_PIN (&omv_pin_P12_XSPIM_P1)
#define OMV_XSPI1_IO13_PIN (&omv_pin_P13_XSPIM_P1)
#define OMV_XSPI1_IO14_PIN (&omv_pin_P14_XSPIM_P1)
#define OMV_XSPI1_IO15_PIN (&omv_pin_P15_XSPIM_P1)
#define OMV_XSPI1_NCS1_PIN (&omv_pin_O0_XSPIM_P1)
#define OMV_XSPI1_DQS0_PIN (&omv_pin_O2_XSPIM_P1)
#define OMV_XSPI1_DQS1_PIN (&omv_pin_O3_XSPIM_P1)
#define OMV_XSPI1_CLKP_PIN (&omv_pin_O4_XSPIM_P1)
#define OMV_XSPI_PSRAM_ID (1)
#define OMV_XSPI_PSRAM_SIZE (0x4000000)
#define OMV_XSPI_PSRAM_FREQUENCY (200000000)
#endif //__OMV_BOARDCONFIG_H__

View File

@ -0,0 +1,42 @@
MCU=STM32N657xx
CPU=cortex-m55
FPU=fpv5-d16
PORT=stm32
SYSTEM=st/system_stm32n6
DFU_DEVICE=0x37C5:0x9206
JLINK_DEVICE=STM32N657L0
OMV_FIRM_BASE=0x70000000
OMV_FIRM_ADDR=0x70080000
OMV_BOOT_ADDR=0x34180400
OMV_BOOT_CFLAGS=-mcmse \
-DOMV_BOOTLOADER \
-DUSER_TZ_SAU_SETUP \
-DCFG_TUD_MAX_SPEED=OPT_MODE_HIGH_SPEED \
-DCFG_TUSB_RHPORT0_MODE=OPT_MODE_DEVICE
OMV_BOARD_CFLAGS=-mcmse \
-DOMV_NOSYS_STUBS_ENABLE=1 \
-DMICROPY_HW_RUNS_FROM_EXT_FLASH=1
OMV_RAMFUNC_OBJS = bdev.o xspi.o spiflash.o
OMV_SIGN_BOOT=1
OMV_SIGN_HDRV=2.3
OMV_SIGN_FLAGS=0x80000000
OMV_PROG_STLDR=MX25UM51245G_STM32N6570-NUCLEO.stldr
OMV_HSE_VALUE=48000000
OMV_ENABLE_BL=1
OMV_BOSON_ENABLE=1
OMV_GENX320_ENABLE=1
OMV_LEPTON_SDK_ENABLE=1
MICROPY_PY_CSI = 1
MICROPY_PY_FIR = 1
MICROPY_PY_IMU = 1
MICROPY_PY_ULAB = 1
MICROPY_PY_AUDIO = 1
MICROPY_PY_LWIP = 1
MICROPY_PY_SSL = 1
MICROPY_PY_SSL_ECDSA_SIGN_ALT = 0
MICROPY_SSL_MBEDTLS = 1
MICROPY_PY_NETWORK_CYW43 = 1
MICROPY_PY_BLUETOOTH = 1
MICROPY_BLUETOOTH_NIMBLE = 1
MICROPY_PY_ML = 1
MICROPY_PY_ML_STAI = 1

View File

@ -0,0 +1,89 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2013-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.
*
* Board configuration and pin definitions.
*/
// *INDENT-OFF*
#ifndef __OMV_BOOTCONFIG_H__
#define __OMV_BOOTCONFIG_H__
#include STM32_HAL_H
#include "Legacy/stm32_hal_legacy.h"
#include "port.h"
// Misc config.
#define OMV_BOOT_VID (0x37C5)
#define OMV_BOOT_PID (0x9206)
#define OMV_BOOT_DFU_TIMEOUT (1500)
#define OMV_BOOT_MAGIC_ADDR (0x3401FFFCU)
#define OMV_BOOT_LED_PIN (0) // index in omv_boot_pins
// Flash config.
#define OMV_BOOT_AXI_FLASH_ENABLE (0)
#define OMV_BOOT_SPI_FLASH_ENABLE (1)
#define OMV_BOOT_SPI_FLASH_MMAP (1) // Memory-map the flash on exit
#define OMV_BOOT_SPI_FLASH_MMAP_DTR (1)
// OSPI/XSPI config.
#define OMV_BOOT_XSPI_INSTANCE (2)
#define OMV_BOOT_XSPI_FREQUENCY (200000000)
#define OMV_BOOT_XSPI_FLASH_SIZE (0x2000000) // Must be a power of 2.
#define OMV_BOOT_XSPI_FLASH_RST_PIN (13) // index in omv_boot_pins
#define GPIO_SPEED_LOW GPIO_SPEED_FREQ_LOW
#define GPIO_SPEED_HIGH GPIO_SPEED_FREQ_VERY_HIGH
// Boot I/O pins.
static const pin_t omv_boot_pins[] = {
{ .gpio = GPIOA, .pin = GPIO_PIN_7, .speed = GPIO_SPEED_LOW, .mode = GPIO_MODE_OUTPUT_PP, .pull = GPIO_PULLUP },
{ .gpio = GPION, .pin = GPIO_PIN_0, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_1, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_2, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_3, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_4, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_5, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_6, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_7, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_8, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_9, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_10, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_11, .speed = GPIO_SPEED_HIGH, .mode = GPIO_MODE_AF_PP, .alt = GPIO_AF9_XSPIM_P2 },
{ .gpio = GPION, .pin = GPIO_PIN_12, .speed = GPIO_SPEED_LOW, .mode = GPIO_MODE_OUTPUT_PP, .pull = GPIO_PULLUP },
};
#define OMV_BOOT_PINS_COUNT (sizeof(omv_boot_pins) / sizeof(omv_boot_pins[0]))
// Boot partitions.
static const partition_t OMV_BOOT_DFU_PARTITIONS[] = {
{ .type = PTYPE_SPI_FLASH, .region = -1, .rdonly = 1, .start = 0x00000000, .limit = 0x00080000, .attr = 0 }, // Boot
{ .type = PTYPE_SPI_FLASH, .region = -1, .rdonly = 0, .start = 0x00080000, .limit = 0x00400000, .attr = 0 }, // FIRMWARE
{ .type = PTYPE_SPI_FLASH, .region = -1, .rdonly = 0, .start = 0x00400000, .limit = 0x00800000, .attr = 0 }, // FILESYSTEM
{ .type = PTYPE_SPI_FLASH, .region = -1, .rdonly = 0, .start = 0x00800000, .limit = 0x02000000, .attr = 0 }, // ROMFS0
};
#define OMV_BOOT_DFU_PARTITIONS_COUNT 4 // Must be a literal
#define OMV_BOOT_DFU_PARTITIONS_STR "BOOTLOADER", "FIRMWARE", "FILESYSTEM", "ROMFS0"
// XIP flash, not used for DFU.
static const partition_t OMV_BOOT_XIP_PARTITIONS[] = {
{ .type = PTYPE_XIP_FLASH, .region = 0, .rdonly = 1, .start = 0x70000000, .limit = 0x78000000, .attr = MEMATTR_NORMAL_WB_RA_WA },
};
#define OMV_BOOT_XIP_PARTITIONS_COUNT (sizeof(OMV_BOOT_XIP_PARTITIONS) / sizeof(OMV_BOOT_XIP_PARTITIONS[0]))
#endif //__OMV_BOOTCONFIG_H__

View File

@ -0,0 +1,53 @@
OMV_GPIO_DEFINE(E, 15, AF5, SPI5) // SCLK
OMV_GPIO_DEFINE(D, 4, AF5, SPI5) // MISO
OMV_GPIO_DEFINE(A, 4, AF5, SPI5) // MOSI
OMV_GPIO_DEFINE(A, 3, AF5, SPI5) // SSEL
OMV_GPIO_DEFINE(B, 10, AF4, I2C2) // I2C2_SCL
OMV_GPIO_DEFINE(B, 11, AF4, I2C2) // I2C2_SDA
OMV_GPIO_DEFINE(A, 8, AF4, I2C3) // I2C3_SCL
OMV_GPIO_DEFINE(A, 9, AF4, I2C3) // I2C3_SDA
OMV_GPIO_DEFINE(E, 13, AF4, I2C4) // I2C4_SCL
OMV_GPIO_DEFINE(E, 14, AF4, I2C4) // I2C4_SDA
OMV_GPIO_DEFINE(E, 2, AF3, ADF1) // ADF1_CCK0
OMV_GPIO_DEFINE(B, 2, AF3, ADF1) // ADF1_SDI0
OMV_GPIO_DEFINE(A, 1, AF9, DCMI) // CSI_D0
OMV_GPIO_DEFINE(A, 10, AF9, DCMI) // CSI_D1
OMV_GPIO_DEFINE(E, 0, AF9, DCMI) // CSI_D2
OMV_GPIO_DEFINE(E, 10, AF9, DCMI) // CSI_D3
OMV_GPIO_DEFINE(B, 0, AF9, DCMI) // CSI_D4
OMV_GPIO_DEFINE(E, 5, AF9, DCMI) // CSI_D5
OMV_GPIO_DEFINE(G, 2, AF9, DCMI) // CSI_D6
OMV_GPIO_DEFINE(F, 1, AF9, DCMI) // CSI_D7
OMV_GPIO_DEFINE(E, 9, AF1, TIM1) // CSI_CLK
OMV_GPIO_DEFINE(D, 0, AF9, DCMI) // CSI_HSYNC
OMV_GPIO_DEFINE(E, 6, AF9, DCMI) // CSI_VSYNC
OMV_GPIO_DEFINE(G, 1, AF9, DCMI) // CSI_PXCLK
OMV_GPIO_DEFINE(E, 3, NONE, GPIO) // CSI_RESET
OMV_GPIO_DEFINE(E, 1, NONE, GPIO) // CSI_POWER
OMV_GPIO_DEFINE(P, 0, AF9, XSPIM_P1) // XSPIM_IO0
OMV_GPIO_DEFINE(P, 1, AF9, XSPIM_P1) // XSPIM_IO1
OMV_GPIO_DEFINE(P, 2, AF9, XSPIM_P1) // XSPIM_IO2
OMV_GPIO_DEFINE(P, 3, AF9, XSPIM_P1) // XSPIM_IO3
OMV_GPIO_DEFINE(P, 4, AF9, XSPIM_P1) // XSPIM_IO4
OMV_GPIO_DEFINE(P, 5, AF9, XSPIM_P1) // XSPIM_IO5
OMV_GPIO_DEFINE(P, 6, AF9, XSPIM_P1) // XSPIM_IO6
OMV_GPIO_DEFINE(P, 7, AF9, XSPIM_P1) // XSPIM_IO7
OMV_GPIO_DEFINE(P, 8, AF9, XSPIM_P1) // XSPIM_IO8
OMV_GPIO_DEFINE(P, 9, AF9, XSPIM_P1) // XSPIM_IO9
OMV_GPIO_DEFINE(P, 10, AF9, XSPIM_P1) // XSPIM_IO10
OMV_GPIO_DEFINE(P, 11, AF9, XSPIM_P1) // XSPIM_IO11
OMV_GPIO_DEFINE(P, 12, AF9, XSPIM_P1) // XSPIM_IO12
OMV_GPIO_DEFINE(P, 13, AF9, XSPIM_P1) // XSPIM_IO13
OMV_GPIO_DEFINE(P, 14, AF9, XSPIM_P1) // XSPIM_IO14
OMV_GPIO_DEFINE(P, 15, AF9, XSPIM_P1) // XSPIM_IO15
OMV_GPIO_DEFINE(O, 0, AF9, XSPIM_P1) // XSPIM_NCS1
OMV_GPIO_DEFINE(O, 2, AF9, XSPIM_P1) // XSPIM_DQS0
OMV_GPIO_DEFINE(O, 3, AF9, XSPIM_P1) // XSPIM_DQS1
OMV_GPIO_DEFINE(O, 4, AF9, XSPIM_P1) // XSPIM_CLKP

View File

@ -0,0 +1,25 @@
{
"0": {
"size": "0x1800000",
"entries": [
{
"type": "tflite",
"path": "{TOP}/lib/models/fomo_face_detection.tflite",
"alignment": 32,
"profile": "default"
},
{
"type": "tflite",
"path": "{TOP}/lib/models/yolo_v2_224_small.tflite",
"alignment": 32,
"profile": "default"
},
{
"type": "tflite",
"path": "{TOP}/lib/models/yolo_v5_224_nano.tflite",
"alignment": 32,
"profile": "default"
}
]
}
}

View File

@ -0,0 +1,17 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013-2016 Kwabena W. Agyeman <kwagyeman@openmv.io>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Ulab config file.
*
*/
#ifndef __ULAB_CONFIG_H__
#define __ULAB_CONFIG_H__
// Override ulab defaults here.
#define ULAB_MAX_DIMS (4)
#define ULAB_SUPPORTS_COMPLEX (0)
#define ULAB_SCIPY_HAS_OPTIMIZE_MODULE (0)
#define ULAB_SCIPY_HAS_SPECIAL_MODULE (0)
#define ULAB_FFT_IS_NUMPY_COMPATIBLE (0)
#endif //__ULAB_CONFIG_H__

View File

@ -70,6 +70,23 @@ Reset_Handler:
/* Call the clock system initialization function.*/
bl SystemInit
/* Copy RAM functions from flash to ITCM */
ldr r0, =_ram_function_start // Destination start (RAM)
ldr r1, =_ram_function_end // Destination end (RAM)
ldr r2, =_ram_function_flash // Source start (Flash)
movs r3, #0
b LoopCopyRamFunc
CopyRamFunc:
ldr r4, [r2, r3]
str r4, [r0, r3]
adds r3, r3, #4
LoopCopyRamFunc:
adds r4, r0, r3
cmp r4, r1
bcc CopyRamFunc
/* Copy the data segment initializers from flash to SRAM */
ldr r0, =_sdata
ldr r1, =_edata

View File

@ -147,9 +147,25 @@ void SystemClock_Config(void) {
//PeriphClkInit.CsiClockSelection = OMV_OSC_DCMIPP_SOURCE;
#endif
#if defined(OMV_OSC_MDF1_SOURCE)
PeriphClkInit.PeriphClockSelection |= RCC_PERIPHCLK_MDF1;
PeriphClkInit.Mdf1ClockSelection = OMV_OSC_MDF1_SOURCE;
#endif
#if defined(OMV_OSC_ADF1_SOURCE)
PeriphClkInit.PeriphClockSelection |= RCC_PERIPHCLK_ADF1;
PeriphClkInit.Adf1ClockSelection = OMV_OSC_ADF1_SOURCE;
#endif
PeriphClkInit.PeriphClockSelection |= RCC_PERIPHCLK_XSPI1;
PeriphClkInit.Xspi1ClockSelection = RCC_XSPI1CLKSOURCE_HCLK;
PeriphClkInit.PeriphClockSelection |= RCC_PERIPHCLK_SDMMC1;
PeriphClkInit.Sdmmc1ClockSelection = RCC_SDMMC1CLKSOURCE_HCLK;
PeriphClkInit.PeriphClockSelection |= RCC_PERIPHCLK_SDMMC2;
PeriphClkInit.Sdmmc2ClockSelection = RCC_SDMMC2CLKSOURCE_HCLK;
#if defined(OMV_RCC_IC0_SOURCE)
PeriphClkInit.ICSelection[RCC_IC0].ClockSelection = OMV_RCC_IC0_SOURCE;
PeriphClkInit.ICSelection[RCC_IC0].ClockDivider = OMV_RCC_IC0_CLKDIV;

View File

@ -58,7 +58,7 @@
#define HAL_LPTIM_MODULE_ENABLED
/*#define HAL_LTDC_MODULE_ENABLED */
/*#define HAL_MCE_MODULE_ENABLED */
/*#define HAL_MDF_MODULE_ENABLED */
#define HAL_MDF_MODULE_ENABLED
/*#define HAL_MMC_MODULE_ENABLED */
/*#define HAL_NAND_MODULE_ENABLED */
/*#define HAL_NOR_MODULE_ENABLED */
@ -70,7 +70,7 @@
#define HAL_RNG_MODULE_ENABLED
#define HAL_RTC_MODULE_ENABLED
/*#define HAL_SAI_MODULE_ENABLED */
/*#define HAL_SD_MODULE_ENABLED */
#define HAL_SD_MODULE_ENABLED
/*#define HAL_SDRAM_MODULE_ENABLED */
/*#define HAL_SMARTCARD_MODULE_ENABLED */
/*#define HAL_SMBUS_MODULE_ENABLED */
@ -416,6 +416,7 @@
#ifdef HAL_RTC_MODULE_ENABLED
#include "stm32n6xx_hal_rtc.h"
#include "stm32n6xx_ll_rtc.h"
#endif /* HAL_RTC_MODULE_ENABLED */
#ifdef HAL_SAI_MODULE_ENABLED

View File

@ -84,6 +84,7 @@
#include "usbdbg.h"
#include "sdram.h"
#include "xspi_psram.h"
#include "fb_alloc.h"
#include "dma_alloc.h"
#include "file_utils.h"
@ -162,6 +163,18 @@ int main(void) {
storage_init();
#endif
#if OMV_XSPI_PSRAM_SIZE
if (xspi_psram_init() != 0) {
__fatal_error("Failed to init XSPI PSRAM!");
}
#endif
#if OMV_XSPI_PSRAM_STARTUP_TEST
if (xspi_psram_test(true) == 0) {
__fatal_error("XSPI PSRAM test failed");
}
#endif
// Basic sub-system init
led_init();
pendsv_init();
@ -208,7 +221,9 @@ soft_reset:
#if MICROPY_HW_ENABLE_CAN
pyb_can_init0();
#endif
#if MICROPY_PY_PYB_LEGACY && MICROPY_HW_ENABLE_HW_I2C
i2c_init0();
#endif
spi_init0();
uart_init0();
fb_alloc_init0();
@ -263,7 +278,10 @@ soft_reset:
#if MICROPY_PY_CSI
// Initialize the csi.
if (first_soft_reset) {
omv_csi_init();
int ret = omv_csi_init();
if (ret != 0 && ret != OMV_CSI_ERROR_ISC_UNDETECTED) {
__fatal_error("Failed to init the CSI");
}
}
#endif
@ -289,13 +307,17 @@ soft_reset:
// Init USB device to default setting if it was not already configured
if (!(pyb_usb_flags & PYB_USB_FLAG_USB_MODE_CALLED)) {
uint8_t usb_mode = USBD_MODE_CDC_MSC;
#if MICROPY_HW_USB_HS
usb_mode |= USBD_MODE_HIGH_SPEED;
#endif
pyb_usb_dev_init(pyb_usb_dev_detect(), MICROPY_HW_USB_VID,
MICROPY_HW_USB_PID_CDC_MSC, USBD_MODE_CDC_MSC, 0, NULL, NULL);
MICROPY_HW_USB_PID, usb_mode, 0, NULL, NULL);
}
// report if SDRAM failed
#if MICROPY_HW_SDRAM_SIZE
if (first_soft_reset && (!sdram_ok)) {
if (first_soft_reset && !sdram_ok) {
__fatal_error("Failed to init sdram!");
}
#endif
@ -364,7 +386,9 @@ soft_reset_exit:
cyw43_deinit(&cyw43_state);
#endif
timer_deinit();
#if MICROPY_PY_PYB_LEGACY && MICROPY_HW_ENABLE_HW_I2C
pyb_i2c_deinit_all();
#endif
spi_deinit_all();
uart_deinit_all();
#if MICROPY_HW_ENABLE_CAN

View File

@ -47,21 +47,38 @@
static CRC_HandleTypeDef hcrc;
static SAI_HandleTypeDef hsai;
static DMA_HandleTypeDef hdma_sai_rx;
static PDM_Filter_Config_t PDM_FilterConfig[OMV_AUDIO_MAX_CHANNELS];
static PDM_Filter_Handler_t PDM_FilterHandler[OMV_AUDIO_MAX_CHANNELS];
// NOTE: BDMA can only access D3 SRAM4 memory.
#define PDM_BUFFER_SIZE (16384)
uint8_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(PDM_BUFFER[PDM_BUFFER_SIZE], 32), ".d3_dma_buffer");
#elif defined(OMV_DFSDM)
static DFSDM_Channel_HandleTypeDef hdfsdm;
// NOTE: Only 1 filter is supported right now.
static DFSDM_Filter_HandleTypeDef hdfsdm_filter[OMV_AUDIO_MAX_CHANNELS];
static DMA_HandleTypeDef hdma_filter[OMV_AUDIO_MAX_CHANNELS];
// NOTE: placed in D2 memory.
#define PDM_BUFFER_SIZE (512 * 2)
int32_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(PDM_BUFFER[PDM_BUFFER_SIZE], 32), ".d2_dma_buffer");
#define DFSDM_GAIN_FRAC_BITS (3)
static int32_t dfsdm_gain = 1;
#elif defined(OMV_MDF)
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_HandleTypeDef hdma_filter[OMV_AUDIO_MAX_CHANNELS];
#define PDM_BUFFER_SIZE (512 * 2)
int32_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(PDM_BUFFER[PDM_BUFFER_SIZE], 32), ".dma_buffer");
#else
#error "No audio driver defined for this board"
#endif
@ -84,20 +101,24 @@ void OMV_SAI_DMA_IRQHandler(void) {
HAL_DMA_IRQHandler(hsai.hdmarx);
}
#elif defined(OMV_DFSDM)
void OMV_DFSDM_FLT0_IRQHandler() {
void OMV_DFSDM_FLT0_IRQHandler(void) {
HAL_DFSDM_IRQHandler(&hdfsdm_filter[0]);
}
#elif defined(OMV_MDF)
void OMV_MDF_FLT0_IRQHandler(void) {
HAL_MDF_IRQHandler(&hmdf);
}
#endif // defined(OMV_SAI)
#if defined(OMV_SAI)
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
#elif defined(OMV_DFSDM)
void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter)
#elif defined(OMV_MDF)
void HAL_MDF_AcqHalfCpltCallback(MDF_HandleTypeDef *hmdf)
#endif
{
xfer_status |= DMA_XFER_HALF;
uint32_t pdm_buffer_size_bytes = (sizeof(PDM_BUFFER[0]) * g_pdm_buffer_size);
SCB_InvalidateDCache_by_Addr((uint32_t *) (&PDM_BUFFER[0]), pdm_buffer_size_bytes);
if (MP_STATE_PORT(audio_callback) != mp_const_none) {
mp_sched_schedule_node(&audio_task_sched_node, audio_task_callback);
}
@ -107,11 +128,11 @@ void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
#elif defined(OMV_DFSDM)
void HAL_DFSDM_FilterRegConvCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter)
#elif defined(OMV_MDF)
void HAL_MDF_AcqCpltCallback(MDF_HandleTypeDef *hmdf)
#endif
{
xfer_status |= DMA_XFER_FULL;
uint32_t pdm_buffer_size_bytes = (sizeof(PDM_BUFFER[0]) * g_pdm_buffer_size);
SCB_InvalidateDCache_by_Addr((uint32_t *) (&PDM_BUFFER[g_pdm_buffer_size]), pdm_buffer_size_bytes / 2);
if (MP_STATE_PORT(audio_callback) != mp_const_none) {
mp_sched_schedule_node(&audio_task_sched_node, audio_task_callback);
}
@ -166,7 +187,9 @@ static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *k
// Default/max PDM buffer size;
g_pdm_buffer_size = PDM_BUFFER_SIZE;
#if defined(OMV_DFSDM)
#if defined(OMV_MDF)
uint32_t samples_per_channel = PDM_BUFFER_SIZE / 2; // Half a transfer
#elif defined(OMV_DFSDM)
uint32_t samples_per_channel = PDM_BUFFER_SIZE / 2; // Half a transfer
dfsdm_gain = __USAT(fast_roundf(expf((gain_db / 20.0f) * M_LN10) * (1 << DFSDM_GAIN_FRAC_BITS)), 15);
#else
@ -189,7 +212,7 @@ static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *k
}
samples_per_channel = args[ARG_samples].u_int;
// Recalculate the PDM buffer size for the requested samples.
#if defined(OMV_DFSDM)
#if defined(OMV_DFSDM) || defined(OMV_MDF)
g_pdm_buffer_size = samples_per_channel * 2;
#else
g_pdm_buffer_size = (samples_per_channel * decimation_factor * g_channels) / 4;
@ -369,6 +392,96 @@ static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *k
NVIC_SetPriority(OMV_DFSDM_FLT0_IRQ, IRQ_PRI_DMA21);
HAL_NVIC_EnableIRQ(OMV_DFSDM_FLT0_IRQ);
#elif defined(OMV_MDF)
hmdf.Instance = OMV_MDF;
hmdf.Init.CommonParam.InterleavedFilters = 0;
hmdf.Init.CommonParam.ProcClockDivider = OMV_MDF_PROC_CLKDIV;
hmdf.Init.CommonParam.OutputClock.Activation = ENABLE;
hmdf.Init.CommonParam.OutputClock.Pins = MDF_OUTPUT_CLOCK_0;
hmdf.Init.CommonParam.OutputClock.Divider = OMV_MDF_CCKY_CLKDIV;
hmdf.Init.CommonParam.OutputClock.Trigger.Activation = DISABLE;
hmdf.Init.SerialInterface.Activation = ENABLE;
hmdf.Init.SerialInterface.Mode = MDF_SITF_LF_MASTER_SPI_MODE;
hmdf.Init.SerialInterface.ClockSource = MDF_SITF_CCK0_SOURCE;
hmdf.Init.SerialInterface.Threshold = 31;
hmdf.Init.FilterBistream = MDF_BITSTREAM0_FALLING;
if (HAL_MDF_Init(&hmdf) != HAL_OK) {
RAISE_OS_EXCEPTION("MDF init failed!");
}
hmdf_filter[0].DataSource = MDF_DATA_SOURCE_BSMX;
hmdf_filter[0].Delay = 0;
hmdf_filter[0].CicMode = MDF_ONE_FILTER_SINC4;
hmdf_filter[0].DecimationRatio = 32;
hmdf_filter[0].Offset = 0;
hmdf_filter[0].Gain = gain_db / 3; // gain in steps of 3db
hmdf_filter[0].ReshapeFilter.Activation = ENABLE;
hmdf_filter[0].ReshapeFilter.DecimationRatio = MDF_RSF_DECIMATION_RATIO_4;
hmdf_filter[0].HighPassFilter.Activation = DISABLE; // Disabled for now.
hmdf_filter[0].HighPassFilter.CutOffFrequency = MDF_HPF_CUTOFF_0_000625FPCM;
hmdf_filter[0].Integrator.Activation = DISABLE;
hmdf_filter[0].SoundActivity.Activation = DISABLE;
hmdf_filter[0].AcquisitionMode = MDF_MODE_ASYNC_CONT;
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) {
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) {
RAISE_OS_EXCEPTION("MDF DMA init failed!");
}
__HAL_LINKDMA(&hmdf, hdma, hdma_filter[0]);
if (HAL_DMA_ConfigChannelAttributes(&hdma_filter[0],
DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC |
DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC) != HAL_OK) {
RAISE_OS_EXCEPTION("MDF DMA init failed!");
}
// Set DMA IRQ handle
dma_utils_set_irq_descr(OMV_MDF_FLT0_DMA_STREAM, &hdma_filter[0]);
// Configure and enable MDF Filter 0 DMA IRQ.
NVIC_SetPriority(OMV_MDF_FLT0_IRQ, IRQ_PRI_DMA21);
HAL_NVIC_EnableIRQ(OMV_MDF_FLT0_IRQ);
NVIC_SetPriority(OMV_MDF_FLT0_DMA_IRQ, IRQ_PRI_DMA21);
HAL_NVIC_EnableIRQ(OMV_MDF_FLT0_DMA_IRQ);
#endif // defined(OMV_SAI)
// Allocate global PCM buffer.
@ -399,6 +512,15 @@ void py_audio_deinit() {
HAL_DMA_DeInit(&hdma_sai_rx);
hdma_sai_rx.Instance = NULL;
}
#elif defined(OMV_MDF)
if (hmdf.Instance != NULL) {
HAL_MDF_AcqStop_DMA(&hmdf);
HAL_MDF_DeInit(&hmdf);
}
// Disable IRQs
HAL_NVIC_DisableIRQ(OMV_MDF_FLT0_IRQ);
HAL_NVIC_DisableIRQ(OMV_MDF_FLT0_DMA_IRQ);
#elif defined(OMV_DFSDM)
if (hdma_filter[0].Instance != NULL) {
HAL_DFSDM_FilterRegularStop_DMA(&hdfsdm_filter[0]);
@ -438,6 +560,10 @@ static void audio_task_callback(mp_sched_node_t *node) {
for (int i = 0; i < g_channels; i++) {
PDM_Filter(&((uint8_t *) PDM_BUFFER)[i], &pcmbuf[i], &PDM_FilterHandler[i]);
}
#elif defined(OMV_MDF)
for (int i = 0; i < g_pdm_buffer_size / 2; i++) {
pcmbuf[i] = PDM_BUFFER[i] >> 16;
}
#elif defined(OMV_DFSDM)
for (int i = 0; i < g_pdm_buffer_size / 2; i++) {
pcmbuf[i] = __SSAT_ASR((PDM_BUFFER[i] >> 8) * dfsdm_gain, 16, DFSDM_GAIN_FRAC_BITS);
@ -453,6 +579,10 @@ static void audio_task_callback(mp_sched_node_t *node) {
for (int i = 0; i < g_channels; i++) {
PDM_Filter(&((uint8_t *) PDM_BUFFER)[g_pdm_buffer_size / 2 + i], &pcmbuf[i], &PDM_FilterHandler[i]);
}
#elif defined(OMV_MDF)
for (int i = 0; i < g_pdm_buffer_size / 2; i++) {
pcmbuf[i] = PDM_BUFFER[g_pdm_buffer_size / 2 + i] >> 16;
}
#elif defined(OMV_DFSDM)
for (int i = 0; i < g_pdm_buffer_size / 2; i++) {
pcmbuf[i] = __SSAT_ASR((PDM_BUFFER[g_pdm_buffer_size / 2 + i] >> 8) * dfsdm_gain, 16, DFSDM_GAIN_FRAC_BITS);
@ -480,6 +610,15 @@ static mp_obj_t py_audio_start_streaming(mp_obj_t callback_obj) {
MP_STATE_PORT(audio_callback) = mp_const_none;
RAISE_OS_EXCEPTION("SAI DMA transfer failed!");
}
#elif defined(OMV_MDF)
MDF_DmaConfigTypeDef dma_config = {
.Address = (uint32_t) PDM_BUFFER,
.DataLength = sizeof(PDM_BUFFER[0]) * g_pdm_buffer_size, // in bytes
.MsbOnly = DISABLE,
};
if (HAL_MDF_AcqStart_DMA(&hmdf, &hmdf_filter[0], &dma_config) != HAL_OK) {
RAISE_OS_EXCEPTION("MDF DMA transfer failed!");
}
#elif defined(OMV_DFSDM)
// Start DMA transfer
if (HAL_DFSDM_FilterRegularStart_DMA(&hdfsdm_filter[0], PDM_BUFFER, g_pdm_buffer_size) != HAL_OK) {

View File

@ -33,6 +33,7 @@
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "py/mphal.h"
#include "irq.h"
#include "omv_boardconfig.h"
@ -42,18 +43,48 @@
#include "omv_csi.h"
#include "dma_utils.h"
#if defined(DMA2)
#define USE_DMA (1)
#define DMA_MAX_TRANSFER (0xFFFFU * 4U)
#endif
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#define USE_MDMA (1)
#define MDMA_BUFFER_SIZE (64)
#define DMA_MAX_XFER_SIZE (0xFFFFU * 4U)
#define DMA_LENGTH_ALIGNMENT (16)
#endif
#if !defined(DCMIPP)
#define USE_DCMI (1)
#define DCMI_IS_ACTIVE() (DCMI->CR & DCMI_CR_ENABLE)
#else
#define USE_DCMIPP (1)
// NOTE using PIPE1.
#define DCMI_IS_ACTIVE() (DCMIPP->P1FCTCR & DCMIPP_P1FCTCR_CPTREQ)
#define DCMIPP_PIPE (DCMIPP_PIPE1)
#endif
#define LINE_WIDTH_ALIGNMENT (16)
extern uint8_t _line_buf;
extern uint32_t hal_get_exti_gpio(uint32_t line);
#if USE_DCMI
void DCMI_IRQHandler(void) {
HAL_DCMI_IRQHandler(&csi.dcmi);
}
#endif
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#if USE_DCMIPP
void CSI_IRQHandler(void) {
HAL_DCMIPP_CSI_IRQHandler(&csi.dcmi);
}
void DCMIPP_IRQHandler(void) {
HAL_DCMIPP_IRQHandler(&csi.dcmi);
}
#endif
#if USE_MDMA
void omv_csi_mdma_irq_handler(void) {
if (MDMA->GISR0 & (1 << OMV_MDMA_CHANNEL_DCMI_0)) {
HAL_MDMA_IRQHandler(&csi.mdma0);
@ -64,6 +95,7 @@ void omv_csi_mdma_irq_handler(void) {
}
#endif
#if USE_DMA
static int omv_csi_dma_config() {
// DMA Stream configuration
csi.dma.Instance = DMA2_Stream1;
@ -96,13 +128,14 @@ static int omv_csi_dma_config() {
// Configure the DMA IRQ Channel
NVIC_SetPriority(DMA2_Stream1_IRQn, IRQ_PRI_DMA21);
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#if USE_MDMA
csi.mdma0.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_0);
csi.mdma1.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_1);
#endif
return 0;
}
#endif
void omv_csi_init0() {
omv_csi_abort(&csi, true, false);
@ -145,7 +178,6 @@ int omv_csi_init() {
// Configure the csi external clock (XCLK).
if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) {
// Failed to initialize the csi clock.
return OMV_CSI_ERROR_TIM_INIT_FAILED;
}
@ -153,26 +185,24 @@ int omv_csi_init() {
for (uint32_t i = 0, n_buses = OMV_ARRAY_SIZE(buses); i < n_buses; i++) {
uint32_t id = buses[i][0], speed = buses[i][1];
if ((init_ret = omv_csi_probe_init(id, speed)) == 0) {
// Sensor was detected on the current bus.
break;
}
omv_i2c_deinit(&csi.i2c_bus);
// Scan the next bus or fail if this is the last one.
if ((i + 1) == n_buses) {
// Sensor probe/init failed.
return init_ret;
}
}
// Configure the DCMI DMA Stream
#if USE_DMA
if (omv_csi_dma_config() != 0) {
// DMA problem
return OMV_CSI_ERROR_DMA_INIT_FAILED;
}
#endif
// Configure the DCMI interface.
if (omv_csi_config(OMV_CSI_CONFIG_INIT) != 0) {
// DCMI config failed
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
@ -188,7 +218,22 @@ int omv_csi_init() {
int omv_csi_config(omv_csi_config_t config) {
if (config == OMV_CSI_CONFIG_INIT) {
// Configure DCMI.
// Configure DCMI/PP.
#if USE_DCMIPP
// Initialize the DCMIPP
csi.dcmi.Instance = DCMIPP;
if (HAL_DCMIPP_Init(&csi.dcmi) != HAL_OK) {
return -1;
}
// Configure and enable DCMI IRQ Channel
NVIC_SetPriority(DCMIPP_IRQn, IRQ_PRI_DCMI);
HAL_NVIC_EnableIRQ(DCMIPP_IRQn);
// Configure and enable CSI IRQ Channel
NVIC_SetPriority(CSI_IRQn, IRQ_PRI_DCMI);
HAL_NVIC_EnableIRQ(CSI_IRQn);
#else
csi.dcmi.Instance = DCMI;
csi.dcmi.Init.VSPolarity = csi.vsync_pol ? DCMI_VSPOLARITY_HIGH : DCMI_VSPOLARITY_LOW;
csi.dcmi.Init.HSPolarity = csi.hsync_pol ? DCMI_HSPOLARITY_HIGH : DCMI_HSPOLARITY_LOW;
@ -197,10 +242,6 @@ int omv_csi_config(omv_csi_config_t config) {
csi.dcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME;
csi.dcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B;
csi.dcmi.Init.JPEGMode = DCMI_JPEG_DISABLE;
#if defined(STM32F7) || defined(STM32H7)
csi.dcmi.Init.ByteSelectMode = DCMI_BSM_ALL;
csi.dcmi.Init.LineSelectMode = DCMI_LSM_ALL;
#endif
// Link the DMA handle to the DCMI handle
__HAL_LINKDMA(&csi.dcmi, DMA_Handle, csi.dma);
@ -214,26 +255,153 @@ int omv_csi_config(omv_csi_config_t config) {
// Configure and enable DCMI IRQ Channel
NVIC_SetPriority(DCMI_IRQn, IRQ_PRI_DCMI);
HAL_NVIC_EnableIRQ(DCMI_IRQn);
#endif
} else if (config == OMV_CSI_CONFIG_PIXFORMAT) {
#if USE_DCMI
DCMI->CR &= ~(DCMI_CR_JPEG_Msk << DCMI_CR_JPEG_Pos);
DCMI->CR |= (csi.pixformat == PIXFORMAT_JPEG) ? DCMI_JPEG_ENABLE : DCMI_JPEG_DISABLE;
#else
// Select and configure the DCMIPP source.
if (csi.mipi_if) {
DCMIPP_CSI_ConfTypeDef scfg = {
.NumberOfLanes = DCMIPP_CSI_TWO_DATA_LANES,
.DataLaneMapping = DCMIPP_CSI_PHYSICAL_DATA_LANES,
.PHYBitrate = (csi.mipi_brate == 850) ? DCMIPP_CSI_PHY_BT_850 : DCMIPP_CSI_PHY_BT_1200,
};
if (HAL_DCMIPP_CSI_SetConfig(&csi.dcmi, &scfg) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
// Configure CSI virtual channel and pipe.
DCMIPP_CSI_PIPE_ConfTypeDef pcfg = {
.DataTypeMode = DCMIPP_DTMODE_DTIDA,
.DataTypeIDA = DCMIPP_DT_RAW10,
.DataTypeIDB = DCMIPP_DT_RAW10,
};
if (HAL_DCMIPP_CSI_SetVCConfig(&csi.dcmi, DCMIPP_VIRTUAL_CHANNEL0,
DCMIPP_CSI_DT_BPP10) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
if (HAL_DCMIPP_CSI_PIPE_SetConfig(&csi.dcmi, DCMIPP_PIPE, &pcfg) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
} else {
DCMIPP_ParallelConfTypeDef scfg = {
.SynchroMode = DCMIPP_SYNCHRO_HARDWARE,
.ExtendedDataMode = DCMIPP_INTERFACE_8BITS,
.VSPolarity = csi.vsync_pol ? DCMIPP_VSPOLARITY_HIGH : DCMIPP_VSPOLARITY_LOW,
.HSPolarity = csi.hsync_pol ? DCMIPP_HSPOLARITY_HIGH : DCMIPP_HSPOLARITY_LOW,
.PCKPolarity = csi.pixck_pol ? DCMIPP_PCKPOLARITY_RISING : DCMIPP_PCKPOLARITY_FALLING,
};
if (csi.raw_output) {
scfg.Format = DCMIPP_FORMAT_RAW8;
} else if (csi.pixformat == PIXFORMAT_RGB565) {
scfg.Format = DCMIPP_FORMAT_RGB565;
scfg.SwapCycles = DCMIPP_SWAPCYCLES_ENABLE;
} else if (csi.pixformat == PIXFORMAT_GRAYSCALE) {
scfg.Format = (csi.mono_bpp == 1) ? DCMIPP_FORMAT_MONOCHROME_8B : DCMIPP_FORMAT_YUV422;
} else {
return OMV_CSI_ERROR_PIXFORMAT_UNSUPPORTED;
}
if (HAL_DCMIPP_PARALLEL_SetConfig(&csi.dcmi, &scfg) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
}
// Configure the pixel processing pipeline.
DCMIPP_PipeConfTypeDef pcfg = { .FrameRate = DCMIPP_FRAME_RATE_ALL };
if (csi.pixformat == PIXFORMAT_RGB565) {
pcfg.PixelPackerFormat = DCMIPP_PIXEL_PACKER_FORMAT_RGB565_1;
} else if (csi.pixformat == PIXFORMAT_GRAYSCALE) {
pcfg.PixelPackerFormat = DCMIPP_PIXEL_PACKER_FORMAT_MONO_Y8_G8_1;
} else {
return OMV_CSI_ERROR_PIXFORMAT_UNSUPPORTED;
}
if (HAL_DCMIPP_PIPE_SetConfig(&csi.dcmi, DCMIPP_PIPE, &pcfg) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
// Swap RGB enabled.
if (csi.yuv_swap) {
HAL_DCMIPP_PIPE_EnableYUVSwap(&csi.dcmi, DCMIPP_PIPE);
}
// Swap YUV if enabled.
if (csi.rgb_swap) {
HAL_DCMIPP_PIPE_EnableRedBlueSwap(&csi.dcmi, DCMIPP_PIPE);
}
// Configure debayer.
if (csi.raw_output && csi.pixformat != PIXFORMAT_BAYER) {
DCMIPP_RawBayer2RGBConfTypeDef rawcfg = {
.RawBayerType = DCMIPP_RAWBAYER_BGGR,
.VLineStrength = DCMIPP_RAWBAYER_ALGO_NONE,
.HLineStrength = DCMIPP_RAWBAYER_ALGO_NONE,
.PeakStrength = DCMIPP_RAWBAYER_ALGO_NONE,
.EdgeStrength = DCMIPP_RAWBAYER_ALGO_NONE,
};
if (HAL_DCMIPP_PIPE_SetISPRawBayer2RGBConfig(&csi.dcmi, DCMIPP_PIPE, &rawcfg) != HAL_OK ||
HAL_DCMIPP_PIPE_EnableISPRawBayer2RGB(&csi.dcmi, DCMIPP_PIPE) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
DCMIPP_ExposureConfTypeDef expcfg = {
.ShiftRed = 0,
.MultiplierRed = 128,
.ShiftGreen = 0,
.MultiplierGreen = 128,
.ShiftBlue = 0,
.MultiplierBlue = 128,
};
if (HAL_DCMIPP_PIPE_SetISPExposureConfig(&csi.dcmi, DCMIPP_PIPE, &expcfg) != HAL_OK ||
HAL_DCMIPP_PIPE_EnableISPExposure(&csi.dcmi, DCMIPP_PIPE) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
const uint32_t statsrc[] = {
DCMIPP_STAT_EXT_SOURCE_PRE_BLKLVL_R,
DCMIPP_STAT_EXT_SOURCE_PRE_BLKLVL_G,
DCMIPP_STAT_EXT_SOURCE_PRE_BLKLVL_B
};
DCMIPP_StatisticExtractionConfTypeDef statcfg[3];
for (size_t i = 0; i < 3; i++) {
statcfg[i].Source = statsrc[i];
statcfg[i].Mode = DCMIPP_STAT_EXT_MODE_AVERAGE;
statcfg[i].Bins = DCMIPP_STAT_EXT_AVER_MODE_ALL_PIXELS; //NOEXT16;
}
for (size_t i = DCMIPP_STATEXT_MODULE1; i <= DCMIPP_STATEXT_MODULE3; i++) {
if (HAL_DCMIPP_PIPE_SetISPStatisticExtractionConfig(&csi.dcmi,
DCMIPP_PIPE, i,
&statcfg[i - DCMIPP_STATEXT_MODULE1]) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
if (HAL_DCMIPP_PIPE_EnableISPStatisticExtraction(&csi.dcmi, DCMIPP_PIPE, i) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
}
}
#endif
}
return 0;
}
// Stop the DCMI from generating more DMA requests, and disable the DMA.
int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
if (DCMI->CR & DCMI_CR_ENABLE) {
if (DCMI_IS_ACTIVE()) {
#if USE_DCMI
DCMI->CR &= ~DCMI_CR_ENABLE;
#endif
#if USE_DMA
if (in_irq) {
HAL_DMA_Abort_IT(&csi->dma);
} else {
HAL_DMA_Abort(&csi->dma);
}
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#endif
#if USE_MDMA
if (!in_irq) {
HAL_MDMA_Abort(&csi->mdma0);
HAL_MDMA_Abort(&csi->mdma1);
@ -241,10 +409,12 @@ int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
HAL_MDMA_DeInit(&csi->mdma0);
HAL_MDMA_DeInit(&csi->mdma1);
#endif
#if USE_DCMI
__HAL_DCMI_DISABLE_IT(&csi->dcmi, DCMI_IT_FRAME);
__HAL_DCMI_CLEAR_FLAG(&csi->dcmi, DCMI_FLAG_FRAMERI);
#else
HAL_DCMIPP_PIPE_Stop(&csi->dcmi, DCMIPP_PIPE);
#endif
csi->first_line = false;
csi->drop_frame = false;
csi->last_frame_ms = 0;
@ -341,8 +511,10 @@ int omv_csi_shutdown(int enable) {
omv_gpio_write(OMV_CSI_POWER_PIN, 0);
}
#endif
#if USE_DCMI
HAL_NVIC_DisableIRQ(DCMI_IRQn);
HAL_DCMI_DeInit(&csi.dcmi);
#endif
} else {
#if defined(OMV_CSI_POWER_PIN)
if (csi.power_pol == OMV_CSI_ACTIVE_HIGH) {
@ -379,6 +551,7 @@ int omv_csi_set_vsync_callback(vsync_cb_t vsync_cb) {
return 0;
}
#if USE_DCMI
// If the image is cropped by more than 1 word in width, align the line start to a word
// address to improve copy performance. Do not crop by more than 1 word as this will
// result in less time between DMA transfers complete interrupts on 16-byte boundaries.
@ -394,11 +567,15 @@ static uint32_t get_dcmi_hw_crop(uint32_t bytes_per_pixel) {
return x_crop;
}
#endif
#if USE_DCMI
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) {
#else
void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t pipe) {
#endif
framebuffer_t *fb = csi.fb;
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#if USE_MDMA
// 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.
@ -422,31 +599,23 @@ void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) {
if (csi.frame_callback) {
csi.frame_callback();
}
}
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
int omv_csi_dma_memcpy(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 USE_DCMIPP
// Get the destination buffer address.
vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
if (buffer == NULL) {
if (!csi.mipi_if) {
HAL_DCMIPP_PIPE_Stop(dcmipp, pipe);
} else {
HAL_DCMIPP_CSI_PIPE_Stop(dcmipp, pipe, DCMIPP_VIRTUAL_CHANNEL0);
}
// 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;
} else {
HAL_DCMIPP_PIPE_SetMemoryAddress(dcmipp, pipe, DCMIPP_MEMORY_ADDRESS_0, (uint32_t) buffer->data);
}
#endif
}
#endif
#if USE_DCMI
// This function is called after each line transfer is complete, with a pointer to the
// buffer that was used. At this point, the DMA transfers the next line to the next buffer.
// Using line buffers allows performing post-processing before writing the frame to the
@ -458,7 +627,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
omv_csi_throttle_framerate();
if (csi.drop_frame) {
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#if USE_MDMA
if (!csi.transpose) {
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
}
@ -493,7 +662,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
return;
}
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#if USE_MDMA
// DCMI_DMAConvCpltUser is called with the other MAR register.
// So, we have to fix the address in full MDMA offload mode.
if (!csi.transpose) {
@ -510,7 +679,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
}
// For all non-JPEG and non-transposed modes image capture can be completely offload to MDMA.
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#if USE_MDMA
if (!csi.transpose) {
// NOTE: MDMA is started here, not in FRAME/VSYNC callbacks, to maximize the time before
// the frame has to be dropped.
@ -536,17 +705,18 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
dst += bytes_per_pixel * buffer->offset++;
}
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#if USE_MDMA
// Two MDMA channels are used to maximize the time available for each channel to finish the transfer.
omv_csi_copy_line((buffer->offset % 2) ? &csi.mdma1 : &csi.mdma0, src, dst);
#else
omv_csi_copy_line(NULL, src, dst);
#endif
}
#endif
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#if USE_MDMA
// Configures an MDMA channel to completely offload the CPU in copying one line of pixels.
static void mdma_config(MDMA_InitTypeDef *init, omv_csi_t *csi, uint32_t bytes_per_pixel) {
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;
@ -618,6 +788,97 @@ static void mdma_config(MDMA_InitTypeDef *init, omv_csi_t *csi, uint32_t bytes_p
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(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];
uint32_t shift[3];
uint32_t multi[3];
for (int i = 0; i < 3; i++) {
// DCMIPP_STATEXT_MODULE1
HAL_DCMIPP_PIPE_GetISPAccumulatedStatisticsCounter(&csi->dcmi, DCMIPP_PIPE, i + 1, &avg[i]);
}
// Averages are collected from bayer components (4R 2G 4B).
avg[0] = OMV_MAX((avg[0] * 256 * 4) / n_pixels, 1);
avg[1] = OMV_MAX((avg[1] * 256 * 2) / n_pixels, 1);
avg[2] = OMV_MAX((avg[2] * 256 * 4) / n_pixels, 1);
// Compute global luminance
float luminance = avg[0] * 0.299 + avg[1] * 0.587 + avg[2] * 0.114;
//printf("Luminance: %f AVG_R: %lu, AVG_G: %lu, AVG_B: %lu\n", (double) luminance, avg[0], avg[1], avg[2]);
if (csi->ioctl) {
omv_csi_ioctl(OMV_CSI_IOCTL_UPDATE_AGC_AEC, fast_floorf(luminance));
}
// Calculate average and exposure factors for each channel (R, G, B)
for (int i = 0; i < 3; i++) {
shift[i] = 0;
multi[i] = roundf((luminance * 128.0f / avg[i]));
while (multi[i] >= 255.0f && shift[i] < 7) {
multi[i] /= 2;
shift[i]++;
}
//printf("Channel %d: Expf: %lu Shift: %lu, Multi: %lu\n", i, expf, shift[i], multi[i]);
}
// Configure RGB exposure settings.
DCMIPP_ExposureConfTypeDef expcfg = {
.ShiftRed = shift[0],
.MultiplierRed = multi[0],
.ShiftGreen = shift[1],
.MultiplierGreen = multi[1],
.ShiftBlue = shift[2],
.MultiplierBlue = multi[2],
};
HAL_DCMIPP_PIPE_SetISPExposureConfig(&csi->dcmi, DCMIPP_PIPE, &expcfg);
}
#endif
// This is the default snapshot function, which can be replaced in omv_csi_init functions.
@ -625,6 +886,14 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
uint32_t length = 0;
framebuffer_t *fb = csi->fb;
if (csi->pixformat == PIXFORMAT_INVALID) {
return OMV_CSI_ERROR_INVALID_PIXFORMAT;
}
if (csi->framesize == OMV_CSI_FRAMESIZE_INVALID) {
return OMV_CSI_ERROR_INVALID_FRAMESIZE;
}
// Compress the framebuffer for the IDE preview, if not the first frame, the
// framebuffer is enabled, and the image sensor doesn't support JPEG encoding.
framebuffer_update_jpeg_buffer(fb);
@ -638,83 +907,59 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
uint32_t w = fb->u;
uint32_t h = fb->v;
// TODO
// If DCMI_DMAConvCpltUser() happens before framebuffer_free_current_buffer(); below then the
// transfer is stopped and it will be re-enabled again right afterwards in the single vbuffer
// case. We know the transfer was stopped by checking DCMI_CR_ENABLE.
// case.
framebuffer_free_current_buffer(fb);
// Configure and start the capture.
if (!(DCMI->CR & DCMI_CR_ENABLE)) {
if (!DCMI_IS_ACTIVE()) {
framebuffer_setup_buffers(fb);
// Setup the size and address of the transfer
uint32_t bytes_per_pixel = omv_csi_get_src_bpp();
// Error out if the pixformat is not set.
if (!bytes_per_pixel) {
return OMV_CSI_ERROR_INVALID_PIXFORMAT;
}
uint32_t x_crop = get_dcmi_hw_crop(bytes_per_pixel);
uint32_t dma_line_width_bytes = resolution[csi->framesize][0] * bytes_per_pixel;
// Shrink the captured pixel count by one word to allow cropping to fix alignment.
if (x_crop) {
dma_line_width_bytes -= sizeof(uint32_t);
}
length = dma_line_width_bytes * h;
// Error out if the transfer size is not compatible with DMA transfer restrictions.
if ((!dma_line_width_bytes)
|| (dma_line_width_bytes % sizeof(uint32_t))
|| (dma_line_width_bytes > (OMV_LINE_BUF_SIZE / 2))
|| (!length)
|| (length % DMA_LENGTH_ALIGNMENT)) {
return OMV_CSI_ERROR_INVALID_FRAMESIZE;
}
// Get the destination buffer address.
vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
if ((csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3) && (!buffer)) {
if (buffer == NULL) {
return OMV_CSI_ERROR_FRAMEBUFFER_ERROR;
}
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
// The code below will enable MDMA data transfer from the DCMI line buffer for non-JPEG modes.
if (csi->pixformat != PIXFORMAT_JPEG) {
mdma_config(&csi->mdma0.Init, csi, bytes_per_pixel);
memcpy(&csi->mdma1.Init, &csi->mdma0.Init, sizeof(MDMA_InitTypeDef));
HAL_MDMA_Init(&csi->mdma0);
#if USE_DCMI
// Setup the size and address of the transfer
uint32_t bytes_per_pixel = omv_csi_get_src_bpp();
uint32_t x_crop = get_dcmi_hw_crop(bytes_per_pixel);
uint32_t line_width_bytes = resolution[csi->framesize][0] * bytes_per_pixel;
// 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);
// Shrink the captured pixel count by one word to allow cropping to fix alignment.
if (x_crop) {
line_width_bytes -= sizeof(uint32_t);
}
HAL_MDMA_Init(&csi->mdma1);
HAL_MDMA_ConfigPostRequestMask(&csi->mdma1, (uint32_t) &DMA2->LIFCR, DMA_FLAG_TCIF1_5);
} else {
HAL_MDMA_Init(&csi->mdma1);
length = line_width_bytes * h;
// Error out if the transfer size is not compatible with DMA transfer restrictions.
if ((!line_width_bytes) ||
(line_width_bytes % sizeof(uint32_t)) ||
(line_width_bytes > (OMV_LINE_BUF_SIZE / 2)) ||
(!length) || (length % LINE_WIDTH_ALIGNMENT)) {
return OMV_CSI_ERROR_INVALID_FRAMESIZE;
}
}
#endif
HAL_DCMI_DisableCrop(&csi->dcmi);
if (csi->pixformat != PIXFORMAT_JPEG) {
// Vertically crop the image. Horizontal cropping is done in software.
HAL_DCMI_ConfigCrop(&csi->dcmi, x_crop, fb->y, dma_line_width_bytes - 1, h - 1);
HAL_DCMI_ConfigCrop(&csi->dcmi, x_crop, fb->y, line_width_bytes - 1, h - 1);
HAL_DCMI_EnableCrop(&csi->dcmi);
}
// Reset the circular, current target, and double buffer mode flags which get set by the below calls.
((DMA_Stream_TypeDef *) csi->dma.Instance)->CR &= ~(DMA_SxCR_CIRC | DMA_SxCR_CT | DMA_SxCR_DBM);
#if USE_MDMA
// Enable MDMA transfer from the DCMI line buffer for non-JPEG modes.
if (csi->pixformat != PIXFORMAT_JPEG) {
omv_csi_mdma_enable(csi, bytes_per_pixel);
}
#endif
// Enable the DMA IRQ before starting the transfer.
// 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_DCMI_Start_DMA and HAL_DCMI_Start_DMA_MB both perform circular transfers,
@ -722,27 +967,66 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
if ((csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3)) {
// Start a one-shot transfer to the framebuffer, used only for JPEG mode 3.
uint32_t size = framebuffer_get_buffer_size(fb);
length = IM_MIN(size, (DMA_MAX_XFER_SIZE * 2U));
length = IM_MIN(size, (DMA_MAX_TRANSFER * 2U));
HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_SNAPSHOT,
(uint32_t) buffer->data, length / sizeof(uint32_t));
// HAL_DCMI_Start_DMA splits bigger transfers.
if (length > DMA_MAX_XFER_SIZE) {
if (length > DMA_MAX_TRANSFER) {
length /= 2;
}
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#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.
((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, dma_line_width_bytes / sizeof(uint32_t));
#endif
(uint32_t) &_line_buf, line_width_bytes / sizeof(uint32_t));
#endif // USE_MDMA
} else {
// Start a multibuffer (line by line) transfer.
HAL_DCMI_Start_DMA_MB(&csi->dcmi, DCMI_MODE_CONTINUOUS,
(uint32_t) &_line_buf, length / sizeof(uint32_t), h);
}
#else
uint32_t bytes_per_pixel = omv_csi_get_dst_bpp();
uint32_t line_width_bytes = fb->u * bytes_per_pixel;
if (!line_width_bytes ||
line_width_bytes % LINE_WIDTH_ALIGNMENT) {
return OMV_CSI_ERROR_INVALID_FRAMESIZE;
}
// Configure crop
DCMIPP_CropConfTypeDef ccfg = {
.HStart = fb->x,
.VStart = fb->y,
.HSize = fb->u,
.VSize = fb->v,
};
if (HAL_DCMIPP_PIPE_SetCropConfig(&csi->dcmi, DCMIPP_PIPE, &ccfg) != HAL_OK ||
HAL_DCMIPP_PIPE_EnableCrop(&csi->dcmi, DCMIPP_PIPE) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
// Set output pitch
if (HAL_DCMIPP_PIPE_SetPitch(&csi->dcmi, DCMIPP_PIPE, line_width_bytes) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
// Start the DCMIPP
if (!csi->mipi_if) {
if (HAL_DCMIPP_PIPE_Start(&csi->dcmi, DCMIPP_PIPE, (uint32_t) buffer->data,
DCMIPP_MODE_CONTINUOUS) != HAL_OK) {
return OMV_CSI_ERROR_CAPTURE_FAILED;
}
} else {
if (HAL_DCMIPP_CSI_PIPE_Start(&csi->dcmi, DCMIPP_PIPE, DCMIPP_VIRTUAL_CHANNEL0,
(uint32_t) buffer->data, DCMIPP_MODE_CONTINUOUS) != HAL_OK) {
return OMV_CSI_ERROR_CAPTURE_FAILED;
}
}
#endif // USE_DCMI
}
// Trigger the camera if FSYNC is enabled.
@ -752,14 +1036,16 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
}
#endif
#if USE_DCMI
// In JPEG mode, enable the end of frame interrupt.
if (DCMI->CR & DCMI_JPEG_ENABLE) {
__HAL_DCMI_ENABLE_IT(&csi->dcmi, DCMI_IT_FRAME);
}
#endif
framebuffer_flags_t fb_flags = FB_NO_FLAGS;
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
#if USE_MDMA
// csi->mdma0.State will be HAL_MDMA_STATE_RESET if the MDMA is not initialized.
if (csi->mdma0.State != HAL_MDMA_STATE_RESET) {
fb_flags = FB_INVALIDATE;
@ -772,21 +1058,21 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
__WFI();
if ((HAL_GetTick() - tick_start) > OMV_CSI_TIMEOUT_MS) {
omv_csi_abort(csi, true, false);
#if defined(OMV_CSI_FSYNC_PIN)
if (csi->frame_sync) {
omv_gpio_write(OMV_CSI_FSYNC_PIN, 0);
}
#endif
return OMV_CSI_ERROR_CAPTURE_TIMEOUT;
}
}
#if USE_DMA
// In JPEG 3 mode, the transfer must be aborted as it waits for data indefinitely.
if ((csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3)) {
omv_csi_abort(csi, true, false);
}
#endif
// We're done receiving data.
#if defined(OMV_CSI_FSYNC_PIN)
@ -837,10 +1123,12 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
// Offset is the number of length-size transfers performed.
size = buffer->offset * length;
// The DMA counter holds the number of bytes per transfer.
#if USE_DMA
if (__HAL_DMA_GET_COUNTER(&csi->dma)) {
// Add in the uncompleted transfer length.
size += ((length / sizeof(uint32_t)) - __HAL_DMA_GET_COUNTER(&csi->dma)) * sizeof(uint32_t);
}
#endif
}
// Clean trailing data after 0xFFD9 at the end of the jpeg byte stream.
fb->pixfmt = PIXFORMAT_JPEG;
@ -853,5 +1141,10 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
// Set the user image.
framebuffer_init_image(fb, image);
#if USE_DCMIPP
if (csi->raw_output) {
omv_csi_update_awb(csi, w * h);
}
#endif
return 0;
}

View File

@ -34,11 +34,23 @@
#include "omv_boardconfig.h"
#include "omv_gpio.h"
#if defined(STM32N6)
// EXTI lines 0 to 15 have single IRQs.
#define MAX_SINGLE_IRQ_LINE (16)
#else
// EXTI lines 0 to 4 have single IRQs.
#define MAX_SINGLE_IRQ_LINE (5)
#endif
static omv_gpio_irq_descr_t gpio_irq_descr_table[16] = {{0}};
// Returns the port number configured in EXTI_CR for this line.
static uint32_t omv_gpio_irq_get_gpio(uint32_t line) {
#if defined(STM32N6)
return (EXTI->EXTICR[line >> 2] >> (8 * (line & 3))) & 0x0F;
#else
return (SYSCFG->EXTICR[line >> 2] >> (4 * (line & 3))) & 0x0F;
#endif
}
// Returns pin number.
@ -67,12 +79,48 @@ static uint32_t omv_gpio_irq_get_irqn(omv_gpio_t pin) {
case 4:
exti_irqn = EXTI4_IRQn;
break;
#if defined(STM32N6)
case 5:
exti_irqn = EXTI5_IRQn;
break;
case 6:
exti_irqn = EXTI6_IRQn;
break;
case 7:
exti_irqn = EXTI7_IRQn;
break;
case 8:
exti_irqn = EXTI8_IRQn;
break;
case 9:
exti_irqn = EXTI9_IRQn;
break;
case 10:
exti_irqn = EXTI10_IRQn;
break;
case 11:
exti_irqn = EXTI11_IRQn;
break;
case 12:
exti_irqn = EXTI12_IRQn;
break;
case 13:
exti_irqn = EXTI13_IRQn;
break;
case 14:
exti_irqn = EXTI14_IRQn;
break;
case 15:
exti_irqn = EXTI15_IRQn;
break;
#else
case 5 ... 9:
exti_irqn = EXTI9_5_IRQn;
break;
case 10 ... 15:
exti_irqn = EXTI15_10_IRQn;
break;
#endif
default:
break;
}
@ -87,8 +135,7 @@ static inline bool omv_gpio_irq_enabled(omv_gpio_irq_descr_t *gpio_irq_descr, ui
}
void omv_gpio_irq_handler(uint32_t line) {
if (line < 5) {
// EXTI lines 0 to 4 have single IRQs.
if (line < MAX_SINGLE_IRQ_LINE) {
omv_gpio_irq_descr_t *gpio_irq_descr = &gpio_irq_descr_table[line];
if (omv_gpio_irq_enabled(gpio_irq_descr, line)) {
__HAL_GPIO_EXTI_CLEAR_FLAG(1 << line);

View File

@ -39,7 +39,7 @@
// If an I2C handle is already defined in MicroPython, reuse that handle to allow
// MicroPython to process I2C IRQs, otherwise define a new handle and handle IRQs here.
#if defined(I2C1)
#if defined(MICROPY_HW_I2C1_SCL)
#if MICROPY_PY_PYB_LEGACY && defined(MICROPY_HW_I2C1_SCL)
extern I2C_HandleTypeDef I2CHandle1;
#else
static I2C_HandleTypeDef I2CHandle1;
@ -53,7 +53,7 @@ void I2C1_ER_IRQHandler(void) {
#endif // I2C1
#if defined(I2C2)
#if defined(MICROPY_HW_I2C2_SCL)
#if MICROPY_PY_PYB_LEGACY && defined(MICROPY_HW_I2C2_SCL)
extern I2C_HandleTypeDef I2CHandle2;
#else
static I2C_HandleTypeDef I2CHandle2;
@ -67,7 +67,7 @@ void I2C2_ER_IRQHandler(void) {
#endif // I2C2
#if defined(I2C3)
#if defined(MICROPY_HW_I2C3_SCL)
#if MICROPY_PY_PYB_LEGACY && defined(MICROPY_HW_I2C3_SCL)
extern I2C_HandleTypeDef I2CHandle3;
#else
static I2C_HandleTypeDef I2CHandle3;
@ -81,7 +81,7 @@ void I2C3_ER_IRQHandler(void) {
#endif // I2C3
#if defined(I2C4)
#if defined(MICROPY_HW_I2C4_SCL)
#if MICROPY_PY_PYB_LEGACY && defined(MICROPY_HW_I2C4_SCL)
extern I2C_HandleTypeDef I2CHandle4;
#else
static I2C_HandleTypeDef I2CHandle4;
@ -101,6 +101,8 @@ static const uint32_t omv_i2c_timing[OMV_I2C_SPEED_MAX] = {
0x1090699B, 0x70330309, 0x50100103,
#elif defined(STM32H7)
0x20D09DE7, 0x40900C22, 0x4030040B,
#elif defined(STM32N6)
0x10C0ECFF, 0x00602173, 0x00300B29,
#else
#error "no I2C timings for this MCU"
#endif
@ -188,6 +190,9 @@ int omv_i2c_init(omv_i2c_t *i2c, uint32_t bus_id, uint32_t speed) {
assert_param(speed != OMV_I2C_SPEED_FAST);
#else
if (speed == OMV_I2C_SPEED_FAST) {
#if defined(STM32N6)
HAL_I2CEx_ConfigFastModePlus(i2c->inst, I2C_FASTMODEPLUS_ENABLE);
#else
// Enable FAST mode plus.
switch (bus_id) {
#if defined(I2C_FASTMODEPLUS_I2C1)
@ -211,6 +216,7 @@ int omv_i2c_init(omv_i2c_t *i2c, uint32_t bus_id, uint32_t speed) {
break;
#endif
}
#endif // defined(STM32N6)
}
#endif // !defined(IS_I2C_FASTMODEPLUS)
@ -301,7 +307,8 @@ int omv_i2c_scan(omv_i2c_t *i2c, uint8_t *list, uint8_t size) {
}
}
}
#if defined(STM32H7)
#if defined(STM32H7) || defined(STM32N6)
// After a failed scan the bus can get stuck. Re-initializing the bus fixes
// it, but it seems disabling and re-enabling the bus is all that's needed.
if (idx == 0) {

View File

@ -108,6 +108,17 @@ typedef I2C_HandleTypeDef *omv_i2c_dev_t;
#define OMV_SPI_MAX_16BIT_XFER (65536U - 8U)
#define OMV_SPI_MAX_TIMEOUT (HAL_MAX_DELAY)
#if defined(STM32N6)
#define OMV_SPI_PORT_BITS \
struct { \
IRQn_Type irqn; \
SPI_HandleTypeDef *descr; \
DMA_QListTypeDef dma_queue_tx; \
DMA_QListTypeDef dma_queue_rx; \
DMA_HandleTypeDef dma_descr_tx; \
DMA_HandleTypeDef dma_descr_rx; \
};
#else
#define OMV_SPI_PORT_BITS \
struct { \
IRQn_Type irqn; \
@ -115,9 +126,9 @@ typedef I2C_HandleTypeDef *omv_i2c_dev_t;
DMA_HandleTypeDef dma_descr_tx; \
DMA_HandleTypeDef dma_descr_rx; \
};
#endif
// omv_csi_t port-specific fields.
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
#if defined(STM32H7)
#define OMV_CSI_PORT_BITS_MDMA \
MDMA_HandleTypeDef mdma0; \
@ -125,6 +136,14 @@ typedef I2C_HandleTypeDef *omv_i2c_dev_t;
#else
#define OMV_CSI_PORT_BITS_MDMA
#endif
#if defined(STM32N6)
#define OMV_CSI_PORT_BITS \
struct { \
TIM_HandleTypeDef tim; \
DCMIPP_HandleTypeDef dcmi; \
};
#else
#define OMV_CSI_PORT_BITS \
struct { \
TIM_HandleTypeDef tim; \

View File

@ -28,16 +28,23 @@ MCU_SERIES := $(shell echo $(MCU) | cut -c6-7 | tr '[:upper:]' '[:lower:]')
MCU_LOWER := $(shell echo $(MCU) | tr '[:upper:]' '[:lower:]')
HAL_DIR := lib/stm32/$(MCU_SERIES)
CMSIS_INC := st
CUBE_DIR := $(TOOLS_DIR)/st/cubeprog/bin/
SIGN_TOOL = $(TOOLS_DIR)/st/cubeprog/bin/STM32MP_SigningTool_CLI
PROG_TOOL = $(TOOLS_DIR)/st/cubeprog/bin/STM32_Programmer.sh
STLDR_DIR = $(TOOLS_DIR)/st/cubeprog/bin/ExternalLoader/
SIGN_TOOL = $(CUBE_DIR)STM32MP_SigningTool_CLI
PROG_TOOL = $(CUBE_DIR)STM32_Programmer_CLI
STLDR_DIR = $(CUBE_DIR)ExternalLoader/
ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),n6))
STEDGE_ARGS ?= --stedge-args "--target stm32n6"
STEDGE_DIR = tools/st/stedgeai
STEDGE_TOOLS = $(STEDGE_DIR)/stedgeai.stamp
endif
ROMFS_IMAGE := $(FW_DIR)/romfs.stamp
ROMFS_CONFIG := $(OMV_BOARD_CONFIG_DIR)/romfs.json
# Compiler Flags
CFLAGS += -std=gnu99 \
CFLAGS += -std=gnu11 \
-Wall \
-Werror \
-Warray-bounds \
@ -76,6 +83,15 @@ LDFLAGS = -mthumb \
-Wl,-T$(BUILD)/$(LDSCRIPT).lds \
-Wl,-Map=$(BUILD)/$(FIRMWARE).map
LDSCRIPT_FLAGS += -I$(COMMON_DIR) \
-I$(OMV_BOARD_CONFIG_DIR)
ifneq ($(OMV_RAMFUNC_OBJS),)
LDSCRIPT_FLAGS += -DOMV_RAMFUNC_EXC="$(addprefix *,$(OMV_RAMFUNC_OBJS))"
LDSCRIPT_FLAGS += -DOMV_RAMFUNC_INC="$(foreach obj,$(OMV_RAMFUNC_OBJS),*$(obj)(.text.* .rodata.*);)"
endif
OMV_CFLAGS += -I$(TOP_DIR)
OMV_CFLAGS += -I$(TOP_DIR)/modules
OMV_CFLAGS += -I$(TOP_DIR)/$(LIBPDM_DIR)
@ -127,6 +143,7 @@ include common/common.mk
include drivers/drivers.mk
include lib/imlib/imlib.mk
include lib/tflm/tflm.mk
include lib/stai/stai.mk
include ports/ports.mk
include common/micropy.mk
@ -176,6 +193,7 @@ MPY_FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/,\
sdcard.o \
sdram.o \
sdio.o \
xspi.o \
vfs_rom_ioctl.o \
fatfs_port.o \
extint.o \
@ -191,11 +209,6 @@ MPY_FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/,\
usbdev/**/src/*.o \
)
# CubeAI objects
ifeq ($(CUBEAI), 1)
include $(TOP_DIR)/$(CUBEAI_DIR)/cube.mk
endif
# Libraries
ifeq ($(MICROPY_PY_AUDIO), 1)
LIBS += $(TOP_DIR)/$(LIBPDM_DIR)/libPDMFilter_CM7_GCC_wc32.a
@ -209,6 +222,10 @@ ifeq ($(MICROPY_PY_ML_TFLM), 1)
LIBS += $(TOP_DIR)/$(TENSORFLOW_DIR)/libtflm/lib/libtflm-$(CPU)+fp-release.a
endif
ifeq ($(CUBEAI), 1)
include $(TOP_DIR)/$(CUBEAI_DIR)/cube.mk
endif
###################################################
all: $(ROMFS_IMAGE)
ifeq ($(OMV_ENABLE_BL), 1)
@ -216,16 +233,18 @@ ifeq ($(OMV_ENABLE_BL), 1)
endif
$(SIZE) $(FW_DIR)/$(FIRMWARE).elf
$(STEDGE_TOOLS):
@bash -c "source tools/ci.sh && ci_install_stedgeai $(STEDGE_DIR)"
# This target builds MicroPython.
MICROPYTHON: | FIRM_DIRS
MICROPYTHON: $(STEDGE_TOOLS) | FIRM_DIRS
$(MAKE) -C $(MICROPY_DIR)/ports/$(PORT) BUILD=$(BUILD)/$(MICROPY_DIR) $(MPY_MKARGS)
$(OMV_FIRM_OBJ): | MICROPYTHON
# This target builds the firmware.
$(FIRMWARE): $(OMV_FIRM_OBJ)
$(CPP) -P -E -I$(COMMON_DIR) -I$(OMV_BOARD_CONFIG_DIR) \
ports/$(PORT)/$(LDSCRIPT).ld.S > $(BUILD)/$(LDSCRIPT).lds
$(CPP) -P -E $(LDSCRIPT_FLAGS) ports/$(PORT)/$(LDSCRIPT).ld.S > $(BUILD)/$(LDSCRIPT).lds
$(CC) $(LDFLAGS) $(OMV_FIRM_OBJ) $(MPY_FIRM_OBJ) -o $(FW_DIR)/$(FIRMWARE).elf $(LIBS) -lm
$(OBJCOPY) -Obinary $(FW_DIR)/$(FIRMWARE).elf $(FW_DIR)/$(FIRMWARE).bin
$(PYTHON) $(MKDFU) -D $(DFU_DEVICE) \
@ -238,7 +257,7 @@ ifeq ($(OMV_ENABLE_BL), 1)
endif
# This target builds the bootloader.
$(BOOTLOADER): | FIRM_DIRS
$(BOOTLOADER): $(STEDGE_TOOLS) | FIRM_DIRS
ifeq ($(OMV_ENABLE_BL), 1)
$(MAKE) -C $(TOP_DIR)/$(BOOT_DIR) BUILD=$(BUILD)/$(BOOT_DIR)
$(OBJCOPY) -Obinary $(FW_DIR)/$(BOOTLOADER).elf $(FW_DIR)/$(BOOTLOADER).bin
@ -254,8 +273,10 @@ endif
$(ROMFS_IMAGE): $(ROMFS_CONFIG) | $(FIRMWARE) $(BOOTLOADER)
$(ECHO) "GEN romfs image"
$(PYTHON) $(TOOLS_DIR)/$(MKROMFS) \
--top-dir $(TOP_DIR) --out-dir $(FW_DIR) \
--build-dir $(BUILD) --config $(ROMFS_CONFIG)
--top-dir $(TOP_DIR) \
--out-dir $(FW_DIR) \
--build-dir $(BUILD)/lib/models \
$(STEDGE_ARGS) --config $(ROMFS_CONFIG)
touch $@
# Flash the bootloader
@ -274,7 +295,7 @@ flash_boot_dfu_util::
flash_image_dfu_util::
dfu-util -a 0 -d $(DFU_DEVICE) -D $(FW_DIR)/$(FIRMWARE).dfu
deploy: $(FIRMWARE)
deploy: $(ROMFS_IMAGE)
$(PROG_TOOL) -c port=SWD mode=HOTPLUG ap=1 \
-el $(STLDR_DIR)/$(OMV_PROG_STLDR) -w $(FW_DIR)/openmv.bin $(OMV_FIRM_BASE) -hardRst

View File

@ -92,18 +92,14 @@ DEFINE_SPI_INSTANCE(6)
{ OMV_SPI##spi_number##_DMA_RX_CHANNEL, { OMV_SPI##spi_number##_DMA_RX_REQUEST } }; \
} while (0)
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), ".dma_buffer");
#endif
static uint32_t omv_spi_clocksource(SPI_TypeDef *spi) {
#if defined(STM32H7)
if (spi == SPI1 || spi == SPI2 || spi == SPI3) {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI123);
} else if (spi == SPI4 || spi == SPI5) {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI45);
} else {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI6);
}
#else
#if defined(STM32F4) || defined(STM32F7)
if (spi == SPI2) {
// SPI2 is on APB1
return HAL_RCC_GetPCLK1Freq();
@ -114,6 +110,28 @@ static uint32_t omv_spi_clocksource(SPI_TypeDef *spi) {
// SPI1, SPI4, SPI5 and SPI6 are on APB2
return HAL_RCC_GetPCLK2Freq();
}
#elif defined(STM32H7)
if (spi == SPI1 || spi == SPI2 || spi == SPI3) {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI123);
} else if (spi == SPI4 || spi == SPI5) {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI45);
} else {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI6);
}
#elif defined(STM32N6)
if (spi == SPI1) {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI1);
} else if (spi == SPI2) {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI2);
} else if (spi == SPI3) {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI3);
} else if (spi == SPI4) {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI4);
} else if (spi == SPI5) {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI5);
} else {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI6);
}
#endif
}
@ -183,7 +201,7 @@ static void omv_spi_callback(SPI_HandleTypeDef *hspi) {
}
if (spi->callback) {
uint8_t *buf = spi->descr->pRxBuffPtr ? spi->descr->pRxBuffPtr : spi->descr->pTxBuffPtr;
uint8_t *buf = (uint8_t *) (spi->descr->pRxBuffPtr ? spi->descr->pRxBuffPtr : spi->descr->pTxBuffPtr);
if (spi->dma_flags & OMV_SPI_DMA_DOUBLE) {
if (spi->xfer_flags & OMV_SPI_XFER_HALF) {
uint32_t size = spi->descr->RxXferSize ? spi->descr->RxXferSize : spi->descr->TxXferSize;
@ -197,7 +215,7 @@ static void omv_spi_callback(SPI_HandleTypeDef *hspi) {
int omv_spi_transfer_start(omv_spi_t *spi, omv_spi_transfer_t *xfer) {
// No TX transfers in circular or double buffer mode.
if ((spi->dma_flags & (OMV_SPI_DMA_CIRCULAR | OMV_SPI_DMA_DOUBLE)) && xfer->txbuf) {
if (xfer->txbuf && (spi->dma_flags & (OMV_SPI_DMA_CIRCULAR | OMV_SPI_DMA_DOUBLE))) {
return -1;
}
@ -270,40 +288,104 @@ 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;
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_descr->Init.Mode = (config->dma_flags & OMV_SPI_DMA_CIRCULAR) ? DMA_CIRCULAR : DMA_NORMAL;
dma_descr->Init.Priority = DMA_PRIORITY_HIGH;
dma_descr->Init.Direction = direction;
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_descr->Init.FIFOMode = DMA_FIFOMODE_DISABLE;
dma_descr->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
dma_init->FIFOMode = DMA_FIFOMODE_DISABLE;
dma_init->FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
// Note MBURST and PBURST are ignored.
dma_descr->Init.MemBurst = DMA_MBURST_SINGLE;
dma_descr->Init.PeriphBurst = DMA_PBURST_SINGLE;
dma_descr->Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
dma_init->MemBurst = DMA_MBURST_SINGLE;
dma_init->PeriphBurst = DMA_PBURST_SINGLE;
dma_init->MemDataAlignment = DMA_MDATAALIGN_WORD;
#if defined(STM32H7)
dma_descr->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
dma_init->PeriphDataAlignment = DMA_PDATAALIGN_WORD;
#else
dma_descr->Init.PeriphDataAlignment = (config->datasize == 8) ? DMA_PDATAALIGN_BYTE : DMA_PDATAALIGN_HALFWORD;
dma_init->PeriphDataAlignment = (config->datasize == 8) ? DMA_PDATAALIGN_BYTE : DMA_PDATAALIGN_HALFWORD;
#endif
dma_descr->Init.MemInc = DMA_MINC_ENABLE;
dma_descr->Init.PeriphInc = DMA_PINC_DISABLE;
dma_init->MemInc = DMA_MINC_ENABLE;
dma_init->PeriphInc = DMA_PINC_DISABLE;
// Initialize the DMA stream
HAL_DMA_DeInit(dma_descr);
if (HAL_DMA_Init(dma_descr) != HAL_OK) {
// Initialization Error
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 = (config->datasize == 8) ? DMA_SRC_DATAWIDTH_BYTE : DMA_SRC_DATAWIDTH_WORD;
dma_init->DestDataWidth = (config->datasize == 8) ? DMA_DEST_DATAWIDTH_BYTE : 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 {
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_LOW_PRIORITY_LOW_WEIGHT;
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;
}
}
if (HAL_DMA_ConfigChannelAttributes(dma_descr,
DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC |
DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC) != HAL_OK) {
return -1;
}
#endif // STM32N6
// Link the DMA handle to SPI handle.
if (direction == DMA_MEMORY_TO_PERIPH) {
@ -311,6 +393,7 @@ static int omv_spi_dma_init(omv_spi_t *spi, uint32_t direction, omv_spi_config_t
} else {
__HAL_LINKDMA(spi->descr, hdmarx, spi->dma_descr_rx);
}
// Set the SPI handle used by the DMA channel's IRQ handler.
dma_utils_set_irq_descr(dma_descr->Instance, dma_descr);
@ -336,9 +419,9 @@ static int omv_spi_bus_init(omv_spi_t *spi, omv_spi_config_t *config) {
spi_descr->Init.CLKPhase = config->clk_pha;
spi_descr->Init.CLKPolarity = config->clk_pol;
spi_descr->Init.BaudRatePrescaler = omv_spi_prescaler(spi_descr->Instance, config->baudrate);
#if defined(STM32F7) || defined(STM32H7)
#if defined(STM32F7) || defined(STM32H7) || defined(STM32N6)
spi_descr->Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
#if defined(STM32H7)
#if defined(STM32H7) || defined(STM32N6)
spi_descr->Init.NSSPolarity = (config->nss_pol == 0) ? SPI_NSS_POLARITY_LOW : SPI_NSS_POLARITY_HIGH;
spi_descr->Init.FifoThreshold = SPI_FIFO_THRESHOLD_04DATA;
spi_descr->Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
@ -356,7 +439,7 @@ static int omv_spi_bus_init(omv_spi_t *spi, omv_spi_config_t *config) {
} else if (config->bus_mode == OMV_SPI_BUS_RX) {
spi_descr->Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
} else {
#if defined(STM32H7)
#if defined(STM32H7) || defined(STM32N6)
spi_descr->Init.Direction = SPI_DIRECTION_2LINES_TXONLY;
#else
spi_descr->Init.Direction = SPI_DIRECTION_1LINE;
@ -407,11 +490,15 @@ int omv_spi_init(omv_spi_t *spi, omv_spi_config_t *config) {
}
if (config->dma_flags & (OMV_SPI_DMA_NORMAL | OMV_SPI_DMA_CIRCULAR)) {
int ret = 0;
if (config->bus_mode & OMV_SPI_BUS_TX) {
omv_spi_dma_init(spi, DMA_MEMORY_TO_PERIPH, config);
ret |= omv_spi_dma_init(spi, DMA_MEMORY_TO_PERIPH, config);
}
if (config->bus_mode & OMV_SPI_BUS_RX) {
omv_spi_dma_init(spi, DMA_PERIPH_TO_MEMORY, config);
ret |= omv_spi_dma_init(spi, DMA_PERIPH_TO_MEMORY, config);
}
if (ret != 0) {
return -1;
}
}
// Configure and enable SPI IRQ channel.

View File

@ -99,16 +99,28 @@ SECTIONS
/* The program code and other data goes into FLASH */
.text : ALIGN(4)
{
KEEP(*(.isr_vector))// ISR table
KEEP(*(.isr_vector))
. = ALIGN(4);
*(.text) // .text sections (code)
#ifdef OMV_RAMFUNC_EXC
*(EXCLUDE_FILE(OMV_RAMFUNC_EXC) .text*)
. = ALIGN(4);
*(EXCLUDE_FILE(OMV_RAMFUNC_EXC) .rodata*)
. = ALIGN(4);
#else
*(.text*) // .text* sections (code)
. = ALIGN(4);
*(.rodata) // .rodata sections (constants, strings, etc.)
. = ALIGN(4);
*(.rodata*) // .rodata* sections (constants, strings, etc.)
. = ALIGN(4);
#endif
} >FLASH_TEXT
.gnu.sgstubs :
{
. = ALIGN(4);
_start_sg = .;
*(.gnu*)
. = ALIGN(4);
_end_sg = .;
} >FLASH_TEXT
/* The address used as the source for copying the initialized data section. */
@ -122,8 +134,6 @@ SECTIONS
. = ALIGN(4);
*(.data*) // .data* sections
. = ALIGN(4);
*(.ram_function*)
. = ALIGN(4);
_edata = .; // Define a global symbol at data end
} >OMV_MAIN_MEMORY AT> FLASH_TEXT
@ -150,6 +160,22 @@ SECTIONS
} >OMV_CM4_BOOT_MEMORY
#endif
#ifdef OMV_RAMFUNC_MEMORY
.ram_function : ALIGN(4)
{
_ram_function_start = .;
. = ALIGN(4);
*(.ram_function*)
#ifdef OMV_RAMFUNC_INC
OMV_RAMFUNC_INC
#endif
. = ALIGN(4);
_ram_function_end = .;
} >OMV_RAMFUNC_MEMORY AT> FLASH_TEXT
_ram_function_flash = LOADADDR(.ram_function);
#endif
#include "common.ld.S"
.mp_etext :

View File

@ -40,7 +40,8 @@
#define MPU_REGION_NUMBER_MAX (MPU_REGION_NUMBER7)
#endif
#define MEM_ATTR_NORMAL_NON_CACHEABLE 0
#define MEMATTR_NORMAL_NCACHE 0
#define MEMATTR_NORMAL_WB_RA_WA 1
extern void SystemClock_Config(void);
extern uint32_t omv_exti_get_gpio(uint32_t line);
@ -68,17 +69,25 @@ void HAL_MspInit(void) {
ARM_MPU_ClrRegion(i);
}
ARM_MPU_SetMemAttr(MEM_ATTR_NORMAL_NON_CACHEABLE, ARM_MPU_ATTR(
ARM_MPU_SetMemAttr(MEMATTR_NORMAL_NCACHE, ARM_MPU_ATTR(
ARM_MPU_ATTR_NON_CACHEABLE,
ARM_MPU_ATTR_NON_CACHEABLE));
ARM_MPU_SetMemAttr(MEMATTR_NORMAL_WB_RA_WA, ARM_MPU_ATTR(
ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1),
ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1)));
MPU->RNR = 0;
MPU->RBAR = ARM_MPU_RBAR(0x90000000, ARM_MPU_SH_NON, 0, 1, 0); // RO-0, NP-1, XN-0
MPU->RLAR = ARM_MPU_RLAR(0x9FFFFFFF, MEMATTR_NORMAL_WB_RA_WA);
for (dma_memory_table_t const *buf = &_dma_memory_table_start; buf < &_dma_memory_table_end; buf++) {
uint32_t region_base = buf->addr;
uint32_t region_size = buf->size;
if (region_size) {
MPU->RNR = region_number--;
MPU->RBAR = ARM_MPU_RBAR(region_base, ARM_MPU_SH_NON, 0, 1, 0); // RO-0, NP-1, XN-0
MPU->RLAR = ARM_MPU_RLAR(region_base + region_size - 1, MEM_ATTR_NORMAL_NON_CACHEABLE);
MPU->RLAR = ARM_MPU_RLAR(region_base + region_size - 1, MEMATTR_NORMAL_NCACHE);
}
}
ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk);
@ -131,6 +140,7 @@ void HAL_MspInit(void) {
}
if (!(SCB->CCR & (uint32_t) SCB_CCR_DC_Msk)) {
// See ST Errata ES0620 - Rev 0.2 section 2.1.2
SCB_EnableDCache();
} else {
SCB_CleanInvalidateDCache();
@ -178,6 +188,19 @@ void HAL_MspInit(void) {
__HAL_RCC_GPIOQ_CLK_ENABLE();
#endif
// Enable low-power GPIO clocks.
#if defined(STM32N6)
__HAL_RCC_GPIOA_CLK_SLEEP_ENABLE();
__HAL_RCC_GPIOB_CLK_SLEEP_ENABLE();
__HAL_RCC_GPIOC_CLK_SLEEP_ENABLE();
__HAL_RCC_GPIOD_CLK_SLEEP_ENABLE();
__HAL_RCC_GPIOE_CLK_SLEEP_ENABLE();
__HAL_RCC_GPIOF_CLK_SLEEP_ENABLE();
__HAL_RCC_GPIOG_CLK_SLEEP_ENABLE();
__HAL_RCC_GPIOO_CLK_SLEEP_ENABLE();
__HAL_RCC_GPIOP_CLK_SLEEP_ENABLE();
#endif
// Enable DMA clocks.
#if defined(__HAL_RCC_DMA1_CLK_ENABLE)
__HAL_RCC_DMA1_CLK_ENABLE();
@ -199,6 +222,15 @@ void HAL_MspInit(void) {
HAL_NVIC_EnableIRQ(MDMA_IRQn);
#endif
// Enable SDMMCx clocks.
#if defined(STM32N6)
__HAL_RCC_SDMMC1_CLK_ENABLE();
__HAL_RCC_SDMMC1_CLK_SLEEP_ENABLE();
__HAL_RCC_SDMMC2_CLK_ENABLE();
__HAL_RCC_SDMMC2_CLK_SLEEP_ENABLE();
#endif
// Setup AXI QoS
#if defined(OMV_AXI_QOS_D2_AHB_R_PRI)
OMV_AXI_QOS_D2_AHB_R_SET(OMV_AXI_QOS_D2_AHB_R_PRI);
@ -269,6 +301,33 @@ void HAL_MspInit(void) {
HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PC2, SYSCFG_SWITCH_PC2_CLOSE);
HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PC3, SYSCFG_SWITCH_PC3_CLOSE);
#endif
#if defined(STM32N6)
__HAL_RCC_RIFSC_CLK_ENABLE();
RIMC_MasterConfig_t RIMC_master = {
.MasterCID = RIF_CID_1,
.SecPriv = RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV,
};
// Array of master/peripheral index pairs [master_index, periph_index]
static const uint32_t rif_config_pairs[][2] = {
{ RIF_MASTER_INDEX_NPU, RIF_RISC_PERIPH_INDEX_NPU },
{ RIF_MASTER_INDEX_OTG1, RIF_RISC_PERIPH_INDEX_OTG1HS },
{ RIF_MASTER_INDEX_DMA2D, RIF_RISC_PERIPH_INDEX_DMA2D },
{ RIF_MASTER_INDEX_GPU2D, RIF_RISC_PERIPH_INDEX_GPU2D },
{ RIF_MASTER_INDEX_VENC, RIF_RISC_PERIPH_INDEX_VENC },
{ RIF_MASTER_INDEX_SDMMC1, RIF_RISC_PERIPH_INDEX_SDMMC1 },
{ RIF_MASTER_INDEX_SDMMC2, RIF_RISC_PERIPH_INDEX_SDMMC2 },
{ RIF_MASTER_INDEX_DCMIPP, RIF_RISC_PERIPH_INDEX_DCMIPP },
{ RIF_MASTER_INDEX_DCMIPP, RIF_RISC_PERIPH_INDEX_CSI },
};
for (int i = 0; i < sizeof(rif_config_pairs) / sizeof(rif_config_pairs[0]); i++) {
HAL_RIF_RIMC_ConfigMasterAttributes(rif_config_pairs[i][0], &RIMC_master);
HAL_RIF_RISC_SetSlaveSecureAttributes(rif_config_pairs[i][1], RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV);
}
#endif
}
void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c) {
@ -278,27 +337,31 @@ void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c) {
if (0) {
#if defined(OMV_I2C1_ID)
} else if (hi2c->Instance == I2C1) {
__HAL_RCC_I2C1_CLK_ENABLE();
scl_pin = OMV_I2C1_SCL_PIN;
sda_pin = OMV_I2C1_SDA_PIN;
__HAL_RCC_I2C1_CLK_ENABLE();
__HAL_RCC_I2C1_CLK_SLEEP_ENABLE();
#endif
#if defined(OMV_I2C2_ID)
} else if (hi2c->Instance == I2C2) {
__HAL_RCC_I2C2_CLK_ENABLE();
scl_pin = OMV_I2C2_SCL_PIN;
sda_pin = OMV_I2C2_SDA_PIN;
__HAL_RCC_I2C2_CLK_ENABLE();
__HAL_RCC_I2C2_CLK_SLEEP_ENABLE();
#endif
#if defined(OMV_I2C3_ID)
} else if (hi2c->Instance == I2C3) {
__HAL_RCC_I2C3_CLK_ENABLE();
scl_pin = OMV_I2C3_SCL_PIN;
sda_pin = OMV_I2C3_SDA_PIN;
__HAL_RCC_I2C3_CLK_ENABLE();
__HAL_RCC_I2C3_CLK_SLEEP_ENABLE();
#endif
#if defined(OMV_I2C4_ID)
} else if (hi2c->Instance == I2C4) {
__HAL_RCC_I2C4_CLK_ENABLE();
scl_pin = OMV_I2C4_SCL_PIN;
sda_pin = OMV_I2C4_SDA_PIN;
__HAL_RCC_I2C4_CLK_ENABLE();
__HAL_RCC_I2C4_CLK_SLEEP_ENABLE();
#endif
}
@ -342,6 +405,7 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) {
if (htim->Instance == OMV_CSI_TIM) {
// Enable DCMI timer clock.
OMV_CSI_TIM_CLK_ENABLE();
OMV_CSI_TIM_CLK_SLEEP_ENABLE();
// Timer GPIO configuration.
omv_gpio_config(OMV_CSI_TIM_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_HIGH, -1);
#if defined(OMV_CSI_TIM_EXT_PIN)
@ -369,6 +433,7 @@ void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef *htim) {
void HAL_DCMI_MspInit(DCMI_HandleTypeDef *hdcmi) {
const omv_gpio_t dcmi_pins[] = {
#ifdef OMV_CSI_D0_PIN
OMV_CSI_D0_PIN,
OMV_CSI_D1_PIN,
OMV_CSI_D2_PIN,
@ -380,18 +445,25 @@ void HAL_DCMI_MspInit(DCMI_HandleTypeDef *hdcmi) {
OMV_CSI_HSYNC_PIN,
OMV_CSI_VSYNC_PIN,
OMV_CSI_PXCLK_PIN,
#endif
};
// DCMI clock enable
__DCMI_CLK_ENABLE();
// Configure VSYNC EXTI.
#if (DCMI_VSYNC_EXTI_SHARED == 1)
if (exti_gpio == 0)
// Enable DCMI clock.
#if defined(PSSI)
__HAL_RCC_DCMI_PSSI_CLK_ENABLE();
__HAL_RCC_DCMI_PSSI_CLK_SLEEP_ENABLE();
#else
__HAL_RCC_DCMI_CLK_ENABLE();
__HAL_RCC_DCMI_CLK_SLEEP_ENABLE();
#endif
{
#ifdef OMV_CSI_VSYNC_PIN
// Configure VSYNC EXTI.
#if DCMI_VSYNC_EXTI_SHARED
if (exti_gpio == 0)
#endif // DCMI_VSYNC_EXTI_SHARED
omv_gpio_config(OMV_CSI_VSYNC_PIN, OMV_GPIO_MODE_IT_BOTH, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_MAX, -1);
}
#endif // OMV_CSI_VSYNC_PIN
// Configure DCMI pins.
for (int i = 0; i < OMV_ARRAY_SIZE(dcmi_pins); i++) {
@ -401,6 +473,7 @@ void HAL_DCMI_MspInit(DCMI_HandleTypeDef *hdcmi) {
void HAL_DCMI_MspDeInit(DCMI_HandleTypeDef *hdcmi) {
const omv_gpio_t dcmi_pins[] = {
#ifdef OMV_CSI_D0_PIN
OMV_CSI_D0_PIN,
OMV_CSI_D1_PIN,
OMV_CSI_D2_PIN,
@ -412,10 +485,17 @@ void HAL_DCMI_MspDeInit(DCMI_HandleTypeDef *hdcmi) {
OMV_CSI_HSYNC_PIN,
OMV_CSI_VSYNC_PIN,
OMV_CSI_PXCLK_PIN,
#endif
};
// Disable DCMI clock.
__DCMI_CLK_DISABLE();
#if defined(PSSI)
__HAL_RCC_DCMI_PSSI_CLK_DISABLE();
__HAL_RCC_DCMI_PSSI_CLK_SLEEP_DISABLE();
#else
__HAL_RCC_DCMI_CLK_DISABLE();
__HAL_RCC_DCMI_CLK_SLEEP_DISABLE();
#endif
// Deinit pins.
for (int i = 0; i < OMV_ARRAY_SIZE(dcmi_pins); i++) {
@ -423,6 +503,85 @@ void HAL_DCMI_MspDeInit(DCMI_HandleTypeDef *hdcmi) {
}
}
#if defined(DCMIPP)
void HAL_DCMIPP_MspInit(DCMIPP_HandleTypeDef *hdcmipp) {
const omv_gpio_t dcmi_pins[] = {
#ifdef OMV_CSI_D0_PIN
OMV_CSI_D0_PIN,
OMV_CSI_D1_PIN,
OMV_CSI_D2_PIN,
OMV_CSI_D3_PIN,
OMV_CSI_D4_PIN,
OMV_CSI_D5_PIN,
OMV_CSI_D6_PIN,
OMV_CSI_D7_PIN,
OMV_CSI_HSYNC_PIN,
OMV_CSI_VSYNC_PIN,
OMV_CSI_PXCLK_PIN,
#endif
};
// Enable DCMIPP clock.
__HAL_RCC_DCMIPP_CLK_ENABLE();
__HAL_RCC_DCMIPP_CLK_SLEEP_ENABLE();
__HAL_RCC_DCMIPP_FORCE_RESET();
__HAL_RCC_DCMIPP_RELEASE_RESET();
// Enable CSI clock
__HAL_RCC_CSI_CLK_ENABLE();
__HAL_RCC_CSI_CLK_SLEEP_ENABLE();
__HAL_RCC_CSI_FORCE_RESET();
__HAL_RCC_CSI_RELEASE_RESET();
#ifdef OMV_CSI_VSYNC_PIN
// Configure VSYNC EXTI.
#if DCMI_VSYNC_EXTI_SHARED
if (exti_gpio == 0)
#endif // DCMI_VSYNC_EXTI_SHARED
omv_gpio_config(OMV_CSI_VSYNC_PIN, OMV_GPIO_MODE_IT_BOTH, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_MAX, -1);
#endif // OMV_CSI_VSYNC_PIN
// Configure DCMI pins.
for (int i = 0; i < OMV_ARRAY_SIZE(dcmi_pins); i++) {
omv_gpio_config(dcmi_pins[i], OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_MAX, -1);
}
}
void HAL_DCMIPP_MspDeInit(DCMIPP_HandleTypeDef *hdcmipp) {
const omv_gpio_t dcmi_pins[] = {
#ifdef OMV_CSI_D0_PIN
OMV_CSI_D0_PIN,
OMV_CSI_D1_PIN,
OMV_CSI_D2_PIN,
OMV_CSI_D3_PIN,
OMV_CSI_D4_PIN,
OMV_CSI_D5_PIN,
OMV_CSI_D6_PIN,
OMV_CSI_D7_PIN,
OMV_CSI_HSYNC_PIN,
OMV_CSI_VSYNC_PIN,
OMV_CSI_PXCLK_PIN,
#endif
};
// Disable DCMI clock.
__HAL_RCC_DCMIPP_FORCE_RESET();
__HAL_RCC_DCMIPP_RELEASE_RESET();
__HAL_RCC_DCMIPP_CLK_DISABLE();
__HAL_RCC_DCMIPP_CLK_SLEEP_DISABLE();
__HAL_RCC_CSI_FORCE_RESET();
__HAL_RCC_CSI_RELEASE_RESET();
__HAL_RCC_CSI_CLK_DISABLE();
__HAL_RCC_CSI_CLK_SLEEP_DISABLE();
// Deinit pins.
for (int i = 0; i < OMV_ARRAY_SIZE(dcmi_pins); i++) {
omv_gpio_deinit(dcmi_pins[i]);
}
}
#endif
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
typedef struct {
omv_gpio_t sclk_pin;
@ -437,6 +596,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
#if defined(OMV_SPI1_ID)
} else if (hspi->Instance == SPI1) {
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_SPI1_CLK_SLEEP_ENABLE();
spi_pins = (spi_pins_t) {
OMV_SPI1_SCLK_PIN, OMV_SPI1_MISO_PIN, OMV_SPI1_MOSI_PIN, OMV_SPI1_SSEL_PIN
};
@ -444,6 +604,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
#if defined(OMV_SPI2_ID)
} else if (hspi->Instance == SPI2) {
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_SPI2_CLK_SLEEP_ENABLE();
spi_pins = (spi_pins_t) {
OMV_SPI2_SCLK_PIN, OMV_SPI2_MISO_PIN, OMV_SPI2_MOSI_PIN, OMV_SPI2_SSEL_PIN
};
@ -451,6 +612,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
#if defined(OMV_SPI3_ID)
} else if (hspi->Instance == SPI3) {
__HAL_RCC_SPI3_CLK_ENABLE();
__HAL_RCC_SPI3_CLK_SLEEP_ENABLE();
spi_pins = (spi_pins_t) {
OMV_SPI3_SCLK_PIN, OMV_SPI3_MISO_PIN, OMV_SPI3_MOSI_PIN, OMV_SPI3_SSEL_PIN
};
@ -458,6 +620,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
#if defined(OMV_SPI4_ID)
} else if (hspi->Instance == SPI4) {
__HAL_RCC_SPI4_CLK_ENABLE();
__HAL_RCC_SPI4_CLK_SLEEP_ENABLE();
spi_pins = (spi_pins_t) {
OMV_SPI4_SCLK_PIN, OMV_SPI4_MISO_PIN, OMV_SPI4_MOSI_PIN, OMV_SPI4_SSEL_PIN
};
@ -465,6 +628,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
#if defined(OMV_SPI5_ID)
} else if (hspi->Instance == SPI5) {
__HAL_RCC_SPI5_CLK_ENABLE();
__HAL_RCC_SPI5_CLK_SLEEP_ENABLE();
spi_pins = (spi_pins_t) {
OMV_SPI5_SCLK_PIN, OMV_SPI5_MISO_PIN, OMV_SPI5_MOSI_PIN, OMV_SPI5_SSEL_PIN
};
@ -472,6 +636,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
#if defined(SPI6_ID)
} else if (hspi->Instance == SPI6) {
__HAL_RCC_SPI6_CLK_ENABLE();
__HAL_RCC_SPI6_CLK_SLEEP_ENABLE();
spi_pins = (spi_pins_t) {
SPI6_SCLK_PIN, SPI6_MISO_PIN, SPI6_MOSI_PIN, SPI6_SSEL_PIN
};
@ -480,7 +645,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
return;
}
#if defined(STM32H7)
#if defined(STM32H7) || defined(STM32N6)
omv_gpio_config(spi_pins.sclk_pin, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
#else
// The STM32F4 and STM32F7 don't set the initial state of the clock line until transmitting the
@ -506,7 +671,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
} else {
if (hspi->Init.Mode == SPI_MODE_MASTER) {
omv_gpio_config(spi_pins.ssel_pin, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_HIGH, -1);
#if defined(STM32H7)
#if defined(STM32H7) || defined(STM32N6)
if (hspi->Init.NSSPolarity == SPI_NSS_POLARITY_LOW) {
omv_gpio_write(spi_pins.ssel_pin, 1);
} else {
@ -616,7 +781,7 @@ void HAL_SAI_MspInit(SAI_HandleTypeDef *hsai) {
}
void HAL_SAI_MspDeInit(SAI_HandleTypeDef *hsai) {
if (hsai->Instance == SAI4_Block_A) {
if (hsai->Instance == OMV_SAI) {
OMV_SAI_CLK_DISABLE();
omv_gpio_deinit(OMV_SAI_CK_PIN);
omv_gpio_deinit(OMV_SAI_D1_PIN);
@ -638,23 +803,57 @@ void HAL_DFSDM_ChannelMspDeInit(DFSDM_Channel_HandleTypeDef *hdfsdm) {
omv_gpio_deinit(OMV_DFSDM_D1_PIN);
}
}
#elif defined(OMV_MDF)
void HAL_MDF_MspInit(MDF_HandleTypeDef *hmdf) {
if (hmdf->Instance == OMV_MDF) {
if (IS_ADF_INSTANCE(hmdf->Instance)) {
__HAL_RCC_ADF1_CLK_ENABLE();
__HAL_RCC_ADF1_CLK_SLEEP_ENABLE();
} else {
__HAL_RCC_MDF1_CLK_ENABLE();
__HAL_RCC_MDF1_CLK_SLEEP_ENABLE();
}
omv_gpio_config(OMV_MDF_CK_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
omv_gpio_config(OMV_MDF_D1_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
}
}
void HAL_MDF_MspDeInit(MDF_HandleTypeDef *hmdf) {
if (hmdf->Instance == OMV_MDF) {
if (IS_ADF_INSTANCE(hmdf->Instance)) {
__HAL_RCC_ADF1_CLK_DISABLE();
__HAL_RCC_ADF1_CLK_SLEEP_DISABLE();
} else {
__HAL_RCC_MDF1_CLK_DISABLE();
__HAL_RCC_MDF1_CLK_SLEEP_DISABLE();
}
omv_gpio_deinit(OMV_MDF_CK_PIN);
omv_gpio_deinit(OMV_MDF_D1_PIN);
}
}
#endif
void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc) {
__HAL_RCC_CRC_CLK_ENABLE();
__HAL_RCC_CRC_CLK_SLEEP_ENABLE();
}
void HAL_CRC_MspDeInit(CRC_HandleTypeDef *hcrc) {
__HAL_RCC_CRC_CLK_DISABLE();
__HAL_RCC_CRC_CLK_SLEEP_DISABLE();
}
void HAL_DMA2D_MspInit(DMA2D_HandleTypeDef *hdma2d) {
__HAL_RCC_DMA2D_CLK_ENABLE();
__HAL_RCC_DMA2D_CLK_SLEEP_ENABLE();
}
void HAL_DMA2D_MspDeInit(DMA2D_HandleTypeDef *hdma2d) {
__HAL_RCC_DMA2D_FORCE_RESET();
__HAL_RCC_DMA2D_RELEASE_RESET();
__HAL_RCC_DMA2D_CLK_DISABLE();
__HAL_RCC_DMA2D_CLK_SLEEP_DISABLE();
}
@ -689,6 +888,7 @@ void HAL_GFXMMU_MspDeInit(GFXMMU_HandleTypeDef *hgfxmmu) {
#if (OMV_JPEG_CODEC_ENABLE == 1)
void HAL_JPEG_MspInit(JPEG_HandleTypeDef *hjpeg) {
__HAL_RCC_JPEG_CLK_ENABLE();
__HAL_RCC_JPEG_CLK_SLEEP_ENABLE();
}
void HAL_JPEG_MspDeInit(JPEG_HandleTypeDef *hjpeg) {
@ -809,6 +1009,99 @@ void HAL_LTDC_MspDeInit(LTDC_HandleTypeDef *hltdc) {
}
#endif
#if defined(OMV_XSPI1_IO00_PIN)
void HAL_XSPI_MspInit(XSPI_HandleTypeDef *hxspi) {
const omv_gpio_t xspi_pins[] = {
OMV_XSPI1_IO00_PIN,
OMV_XSPI1_IO01_PIN,
OMV_XSPI1_IO02_PIN,
OMV_XSPI1_IO03_PIN,
OMV_XSPI1_IO04_PIN,
OMV_XSPI1_IO05_PIN,
OMV_XSPI1_IO06_PIN,
OMV_XSPI1_IO07_PIN,
OMV_XSPI1_IO08_PIN,
OMV_XSPI1_IO09_PIN,
OMV_XSPI1_IO10_PIN,
OMV_XSPI1_IO11_PIN,
OMV_XSPI1_IO12_PIN,
OMV_XSPI1_IO13_PIN,
OMV_XSPI1_IO14_PIN,
OMV_XSPI1_IO15_PIN,
OMV_XSPI1_NCS1_PIN,
OMV_XSPI1_DQS0_PIN,
OMV_XSPI1_DQS1_PIN,
OMV_XSPI1_CLKP_PIN,
};
// Reset and enable XSPI clock.
if (hxspi->Instance == XSPI1) {
__HAL_RCC_XSPI1_FORCE_RESET();
__HAL_RCC_XSPI1_RELEASE_RESET();
__HAL_RCC_XSPI1_CLK_ENABLE();
__HAL_RCC_XSPI1_CLK_SLEEP_ENABLE();
} else if (hxspi->Instance == XSPI2) {
__HAL_RCC_XSPI2_FORCE_RESET();
__HAL_RCC_XSPI2_RELEASE_RESET();
__HAL_RCC_XSPI2_CLK_ENABLE();
__HAL_RCC_XSPI2_CLK_SLEEP_ENABLE();
} else if (hxspi->Instance == XSPI3) {
__HAL_RCC_XSPI3_FORCE_RESET();
__HAL_RCC_XSPI3_RELEASE_RESET();
__HAL_RCC_XSPI3_CLK_ENABLE();
__HAL_RCC_XSPI3_CLK_SLEEP_ENABLE();
}
for (int i = 0; i < OMV_ARRAY_SIZE(xspi_pins); i++) {
omv_gpio_config(xspi_pins[i], OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_MAX, -1);
}
}
void HAL_XSPI_MspDeInit(XSPI_HandleTypeDef *hxspi) {
const omv_gpio_t xspi_pins[] = {
OMV_XSPI1_IO00_PIN,
OMV_XSPI1_IO01_PIN,
OMV_XSPI1_IO02_PIN,
OMV_XSPI1_IO03_PIN,
OMV_XSPI1_IO04_PIN,
OMV_XSPI1_IO05_PIN,
OMV_XSPI1_IO06_PIN,
OMV_XSPI1_IO07_PIN,
OMV_XSPI1_IO08_PIN,
OMV_XSPI1_IO09_PIN,
OMV_XSPI1_IO10_PIN,
OMV_XSPI1_IO11_PIN,
OMV_XSPI1_IO12_PIN,
OMV_XSPI1_IO13_PIN,
OMV_XSPI1_IO14_PIN,
OMV_XSPI1_IO15_PIN,
OMV_XSPI1_NCS1_PIN,
OMV_XSPI1_DQS0_PIN,
OMV_XSPI1_DQS1_PIN,
OMV_XSPI1_CLKP_PIN,
};
// Reset and enable XSPI clock.
if (hxspi->Instance == XSPI1) {
__HAL_RCC_XSPI1_FORCE_RESET();
__HAL_RCC_XSPI1_RELEASE_RESET();
__HAL_RCC_XSPI1_CLK_DISABLE();
} else if (hxspi->Instance == XSPI2) {
__HAL_RCC_XSPI2_FORCE_RESET();
__HAL_RCC_XSPI2_RELEASE_RESET();
__HAL_RCC_XSPI2_CLK_DISABLE();
} else if (hxspi->Instance == XSPI3) {
__HAL_RCC_XSPI3_FORCE_RESET();
__HAL_RCC_XSPI3_RELEASE_RESET();
__HAL_RCC_XSPI3_CLK_DISABLE();
}
for (int i = 0; i < OMV_ARRAY_SIZE(xspi_pins); i++) {
omv_gpio_deinit(xspi_pins[i]);
}
}
#endif
void HAL_MspDeInit(void) {
}

330
ports/stm32/xspi_psram.c Normal file
View File

@ -0,0 +1,330 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* STM32 XSPI PSRAM driver.
*/
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include STM32_HAL_H
#include "omv_boardconfig.h"
#if defined(OMV_XSPI_PSRAM_ID)
#define XSPI_CMD_READ (0x00)
#define XSPI_CMD_READ_LATENCY (7)
#define XSPI_CMD_READ_LINEAR_BURST (0x20)
#define XSPI_CMD_WRITE (0x80)
#define XSPI_CMD_WRITE_LATENCY (7)
#define XSPI_CMD_WRITE_LINEAR_BURST (0xA0)
#define XSPI_CMD_READ_REG (0x40)
#define XSPI_CMD_WRITE_REG (0xC0)
#define XSPI_COMMAND_TIMEOUT (1000)
static XSPI_HandleTypeDef xspi;
static int xspi_psram_read_reg(XSPI_HandleTypeDef *xspi, uint32_t addr, uint8_t *data) {
XSPI_RegularCmdTypeDef command = {
.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG,
.Instruction = XSPI_CMD_READ_REG,
.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES,
.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS,
.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE,
.Address = addr,
.AddressMode = HAL_XSPI_ADDRESS_8_LINES,
.AddressWidth = HAL_XSPI_ADDRESS_32_BITS,
.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE,
.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE,
.DataMode = HAL_XSPI_DATA_8_LINES,
.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE,
.DataLength = 2,
.DummyCycles = XSPI_CMD_READ_LATENCY - 1,
.DQSMode = HAL_XSPI_DQS_ENABLE,
};
uint16_t regval = 0;
if (HAL_XSPI_Command(xspi, &command, XSPI_COMMAND_TIMEOUT) != HAL_OK ||
HAL_XSPI_Receive(xspi, (uint8_t *) &regval, XSPI_COMMAND_TIMEOUT) != HAL_OK) {
return -1;
}
*data = (uint8_t) regval;
return 0;
}
static int xspi_psram_write_reg(XSPI_HandleTypeDef *xspi, uint32_t addr, uint8_t data) {
XSPI_RegularCmdTypeDef command = {
.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG,
.Instruction = XSPI_CMD_WRITE_REG,
.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES,
.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS,
.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE,
.Address = addr,
.AddressMode = HAL_XSPI_ADDRESS_8_LINES,
.AddressWidth = HAL_XSPI_ADDRESS_32_BITS,
.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE,
.DataMode = HAL_XSPI_DATA_8_LINES,
.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE,
.DataLength = 2,
.DummyCycles = 0,
.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE,
.DQSMode = HAL_XSPI_DQS_DISABLE,
};
uint16_t regval = (data << 8) | data;
if (HAL_XSPI_Command(xspi, &command, XSPI_COMMAND_TIMEOUT) != HAL_OK ||
HAL_XSPI_Transmit(xspi, (uint8_t *) &regval, XSPI_COMMAND_TIMEOUT) != HAL_OK) {
return -1;
}
return 0;
}
static int xspi_psram_write_verify(XSPI_HandleTypeDef *xspi, uint32_t addr, uint8_t data) {
uint8_t reg = 0;
if (xspi_psram_write_reg(xspi, addr, data) != 0 ||
xspi_psram_read_reg(xspi, addr, &reg) != 0) {
return -1;
}
return reg == data ? 0 : -1;
}
// Note: Linear burst commands force read/write commands to do 2KByte Wrap(X8)/1K Word(X16)
static int xspi_psram_memory_map(XSPI_HandleTypeDef *xspi, uint32_t burst_enable) {
XSPI_RegularCmdTypeDef command = {
.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES,
.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS,
.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE,
.AddressMode = HAL_XSPI_ADDRESS_8_LINES,
.AddressWidth = HAL_XSPI_ADDRESS_32_BITS,
.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE,
.DataMode = HAL_XSPI_DATA_16_LINES,
.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE,
.DataLength = 0,
.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE,
.DQSMode = HAL_XSPI_DQS_ENABLE,
};
// Initialize the write command
command.OperationType = HAL_XSPI_OPTYPE_WRITE_CFG;
command.Instruction = (burst_enable ? XSPI_CMD_WRITE_LINEAR_BURST : XSPI_CMD_WRITE);
command.DummyCycles = (XSPI_CMD_WRITE_LATENCY - 1U);
if (HAL_XSPI_Command(xspi, &command, XSPI_COMMAND_TIMEOUT) != HAL_OK) {
return -1;
}
// Initialize the read command
command.OperationType = HAL_XSPI_OPTYPE_READ_CFG;
command.Instruction = (burst_enable ? XSPI_CMD_READ_LINEAR_BURST : XSPI_CMD_READ);
command.DummyCycles = (XSPI_CMD_READ_LATENCY - 1U);
if (HAL_XSPI_Command(xspi, &command, XSPI_COMMAND_TIMEOUT) != HAL_OK) {
return -1;
}
XSPI_MemoryMappedTypeDef mmap_config = {
.TimeoutPeriodClock = 0x34,
.TimeOutActivation = HAL_XSPI_TIMEOUT_COUNTER_DISABLE,
};
if (HAL_XSPI_MemoryMapped(xspi, &mmap_config) != HAL_OK) {
return -1;
}
return 0;
}
int xspi_psram_init() {
uint32_t xspi_clk = 0;
// Reset and enable XSPI clock.
if (OMV_XSPI_PSRAM_ID == 1) {
xspi_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI1);
} else if (OMV_XSPI_PSRAM_ID == 2) {
xspi_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2);
} else if (OMV_XSPI_PSRAM_ID == 3) {
xspi_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI3);
} else {
return -1;
}
memset(&xspi, 0, sizeof(XSPI_HandleTypeDef));
xspi.Instance = XSPI1;
xspi.Init.FifoThresholdByte = 8;
xspi.Init.MemoryType = HAL_XSPI_MEMTYPE_APMEM_16BITS;
xspi.Init.MemoryMode = HAL_XSPI_SINGLE_MEM;
xspi.Init.MemorySize = __builtin_ctz(OMV_XSPI_PSRAM_SIZE) - 1;
xspi.Init.MemorySelect = HAL_XSPI_CSSEL_NCS1;
xspi.Init.ChipSelectHighTimeCycle = 1;
xspi.Init.ClockMode = HAL_XSPI_CLOCK_MODE_0;
xspi.Init.ClockPrescaler = (xspi_clk / OMV_XSPI_PSRAM_FREQUENCY) - 1;
xspi.Init.FreeRunningClock = HAL_XSPI_FREERUNCLK_DISABLE;
xspi.Init.SampleShifting = HAL_XSPI_SAMPLE_SHIFT_NONE;
xspi.Init.DelayHoldQuarterCycle = HAL_XSPI_DHQC_DISABLE;
xspi.Init.ChipSelectBoundary = HAL_XSPI_BONDARYOF_16KB;
xspi.Init.WrapSize = HAL_XSPI_WRAP_NOT_SUPPORTED;
if (HAL_XSPI_Init(&xspi) != HAL_OK) {
return -1;
}
// Read Latency=7 up to 200MHz
if (xspi_psram_write_verify(&xspi, 0, 0x30) != 0) {
return -1;
}
// Write Latency=7 up to 200MHz
if (xspi_psram_write_verify(&xspi, 4, 0x20) != 0) {
return -1;
}
// x16 | RBX | 2K Byte burst
if (xspi_psram_write_verify(&xspi, 8, 0x4B) != 0) {
return -1;
}
// Switch to memory-mapped mode.
if (xspi_psram_memory_map(&xspi, 1) != 0) {
return -1;
}
return 0;
}
extern void __fatal_error(const char *msg);
#if __GNUC__ >= 11
// Prevent array bounds warnings when accessing SDRAM_START_ADDRESS as a memory pointer.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
bool __attribute__((optimize("Os"))) xspi_psram_test(bool exhaustive) {
uint8_t const pattern = 0xaa;
uint8_t const antipattern = 0x55;
volatile uint8_t *const mem_base = (uint8_t *) 0x90000000;
char error_buffer[1024];
uint32_t mem_size = OMV_XSPI_PSRAM_SIZE;
#if (__DCACHE_PRESENT == 1)
bool i_cache_disabled = false;
bool d_cache_disabled = false;
// Disable caches for testing.
if (SCB->CCR & (uint32_t) SCB_CCR_IC_Msk) {
SCB_DisableICache();
i_cache_disabled = true;
}
if (SCB->CCR & (uint32_t) SCB_CCR_DC_Msk) {
SCB_DisableDCache();
d_cache_disabled = true;
}
#endif
// Test data bus
for (uint32_t i = 0; i < 16; i++) {
*((volatile uint32_t *) mem_base) = (1 << i);
__DSB();
if (*((volatile uint32_t *) mem_base) != (1 << i)) {
snprintf(error_buffer, sizeof(error_buffer),
"Data bus test failed at 0x%p expected 0x%x found 0x%lx",
&mem_base[0], (1 << i), ((volatile uint32_t *) mem_base)[0]);
__fatal_error(error_buffer);
return false;
}
}
// Test address bus
for (uint32_t i = 1; i < mem_size; i <<= 1) {
mem_base[i] = pattern;
__DSB();
if (mem_base[i] != pattern) {
snprintf(error_buffer, sizeof(error_buffer),
"Address bus test failed at 0x%p expected 0x%x found 0x%x",
&mem_base[i], pattern, mem_base[i]);
__fatal_error(error_buffer);
return false;
}
}
// Check for aliasing (overlapping addresses)
mem_base[0] = antipattern;
__DSB();
for (uint32_t i = 1; i < mem_size; i <<= 1) {
if (mem_base[i] != pattern) {
snprintf(error_buffer, sizeof(error_buffer),
"Address bus overlap at 0x%p expected 0x%x found 0x%x",
&mem_base[i], pattern, mem_base[i]);
__fatal_error(error_buffer);
return false;
}
}
mem_size = 512 * 1024;
// Test all RAM cells
if (exhaustive) {
// Write all memory first then compare, so even if the cache
// is enabled, it's not just writing and reading from cache.
// Note: This test should also detect refresh rate issues.
for (uint32_t i = 0; i < mem_size; i++) {
mem_base[i] = ((i % 2) ? pattern : antipattern);
}
for (uint32_t i = 0; i < mem_size; i++) {
if (mem_base[i] != ((i % 2) ? pattern : antipattern)) {
snprintf(error_buffer, sizeof(error_buffer),
"Address bus slow test failed at 0x%p expected 0x%x found 0x%x",
&mem_base[i], ((i % 2) ? pattern : antipattern), mem_base[i]);
__fatal_error(error_buffer);
return false;
}
}
}
#if (__DCACHE_PRESENT == 1)
// Re-enable caches if they were enabled before the test started.
if (i_cache_disabled) {
SCB_EnableICache();
}
if (d_cache_disabled) {
SCB_EnableDCache();
}
#endif
return true;
}
#if __GNUC__ >= 11
#pragma GCC diagnostic pop
#endif
#endif

36
ports/stm32/xspi_psram.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* STM32 XSPI PSRAM driver.
*/
#ifndef __XSPI_PSRAM_H__
int xspi_psram_init();
bool xspi_psram_test(bool exhaustive);
#endif // __XSPI_PSRAM_H__