diff --git a/firmware/boards/LUCIDCODE_INSPEC_N6/board_config.h b/firmware/boards/LUCIDCODE_INSPEC_N6/board_config.h new file mode 100644 index 000000000..ffa478065 --- /dev/null +++ b/firmware/boards/LUCIDCODE_INSPEC_N6/board_config.h @@ -0,0 +1,363 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2024 Ibrahim Abdelkader + * Copyright (c) 2013-2024 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * Board configuration and pin definitions. + */ +#ifndef __BOARD_CONFIG_H__ +#define __BOARD_CONFIG_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_PLL_CTRL2 (0x64) +#define OMV_OV5640_PLL_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_SOFTCSI_ENABLE (1) +#define OMV_PS5520_ENABLE (1) + +// 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) + +// UMM heap block size +#define OMV_UMM_BLOCK_SIZE 256 + +// USB IRQn. +#define OMV_USB_IRQN (USB1_OTG_HS_IRQn) + +// OpenMV protocol configuration. +#define OMV_PROTOCOL_MAX_BUFFER_SIZE (8192) +#define OMV_PROTOCOL_STDIO_BUFFER_SIZE (2048) +#define OMV_PROTOCOL_HW_CAPS OMV_PROTOCOL_HW_CAPS_MAKE( \ + HAS_GPU, HAS_NPU, HAS_ISP, HAS_VENC, HAS_JPEG, HAS_DRAM, \ + HAS_CRC, HAS_PMU, HAS_WIFI, HAS_BT, HAS_SD, HAS_ETH, HAS_USB_HS) + +//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_IC10_SOURCE (RCC_ICCLKSOURCE_PLL1) +#define OMV_RCC_IC10_CLKDIV (8) + +// Used by MicroPython for ethernet clocks. +#define OMV_RCC_IC12_SOURCE (RCC_ICCLKSOURCE_PLL1) +#define OMV_RCC_IC12_CLKDIV (8) + +// Used by MicroPython for slow peripherals. +#define OMV_RCC_IC14_SOURCE (RCC_ICCLKSOURCE_PLL1) +#define OMV_RCC_IC14_CLKDIV (8) + +#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_IC14) +#define OMV_OSC_USART2_SOURCE (RCC_USART2CLKSOURCE_IC14) + +#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_FB_OVERLAY_MEMORY SRAM1 // Fast fb_alloc memory. +#define OMV_FB_OVERLAY_SIZE (400K) // Fast fb_alloc memory size. +#define OMV_SB_MEMORY DRAM // Streaming buffer memory. +#define OMV_SB_SIZE (1M) // Streaming buffer size. +#define OMV_DMA_MEMORY SRAM1 // Misc DMA buffers memory. +#define OMV_DMA_MEMORY_D2 SRAM7 // Domain 2 DMA buffers. +#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 (24M) +#define OMV_MSC_BUF_SIZE (4K) // USB MSC bot data +#define OMV_VOSPI_DMA_BUFFER ".d2_dma_buffer" + +// Memory map. +#define OMV_DTCM_ORIGIN 0x30000000 +#define OMV_DTCM_LENGTH 128K +#define OMV_ITCM_ORIGIN 0x10000000 +#define OMV_ITCM_LENGTH 64K +#define OMV_SRAM1_ORIGIN 0x34000000 // AXISRAM1 +#define OMV_SRAM1_LENGTH 1M // 1MB +#define OMV_SRAM2_ORIGIN 0x34100000 // AXISRAM2 +#define OMV_SRAM2_LENGTH 1M // 1MB +#define OMV_SRAM3_ORIGIN 0x34200000 // AXISRAM3 +#define OMV_SRAM3_LENGTH 448K // 448KB +#define OMV_SRAM4_ORIGIN 0x34270000 // AXISRAM4 +#define OMV_SRAM4_LENGTH 448K // 448KB +#define OMV_SRAM5_ORIGIN 0x342E0000 // AXISRAM5 +#define OMV_SRAM5_LENGTH 448K // 448KB +#define OMV_SRAM6_ORIGIN 0x34350000 // AXISRAM6 +#define OMV_SRAM6_LENGTH 448K // 448KB +#define OMV_SRAM7_ORIGIN 0x38000000 // 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 2 +#define OMV_SPI2_ID (2) +#define OMV_SPI2_SCLK_PIN (&omv_pin_A12_SPI2) +#define OMV_SPI2_MISO_PIN (&omv_pin_D11_SPI2) +#define OMV_SPI2_MOSI_PIN (&omv_pin_D7_SPI2) +#define OMV_SPI2_SSEL_PIN (&omv_pin_A11_SPI2) +#define OMV_SPI2_DMA_TX_CHANNEL (GPDMA1_Channel11) +#define OMV_SPI2_DMA_TX_REQUEST (GPDMA1_REQUEST_SPI2_TX) +#define OMV_SPI2_DMA_RX_CHANNEL (GPDMA1_Channel12) +#define OMV_SPI2_DMA_RX_REQUEST (GPDMA1_REQUEST_SPI2_RX) + +// 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_Channel13) +#define OMV_SPI5_DMA_TX_REQUEST (GPDMA1_REQUEST_SPI5_TX) +#define OMV_SPI5_DMA_RX_CHANNEL (GPDMA1_Channel14) +#define OMV_SPI5_DMA_RX_REQUEST (GPDMA1_REQUEST_SPI5_RX) +#define OMV_SPI_DMA_LIST_PORTS (DMA_LINK_ALLOCATED_PORT0) +#define OMV_SPI_DMA_XFER_PORTS (DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT1) + +// SPI LCD Interface +#define OMV_SPI_DISPLAY_CONTROLLER (OMV_SPI2_ID) +#define OMV_SPI_DISPLAY_MOSI_PIN (&omv_pin_D7_SPI2) +#define OMV_SPI_DISPLAY_MISO_PIN (&omv_pin_D11_SPI2) +#define OMV_SPI_DISPLAY_SCLK_PIN (&omv_pin_A12_SPI2) +#define OMV_SPI_DISPLAY_SSEL_PIN (&omv_pin_A11_SPI2) + +#define OMV_SPI_DISPLAY_RS_PIN (&omv_pin_D13_GPIO) +#define OMV_SPI_DISPLAY_RST_PIN (&omv_pin_G13_GPIO) +#define OMV_SPI_DISPLAY_BL_PIN (&omv_pin_G0_GPIO) +#define OMV_SPI_DISPLAY_TRIPLE_BUFFER (1) + +// 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) + +// TOF I2C bus +#define OMV_TOF_I2C_ID (OMV_I2C2_ID) +#define OMV_TOF_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_Channel15 +#define OMV_MDF_FLT0_DMA_REQUEST GPDMA1_REQUEST_ADF1_FLT0 +#define OMV_MDF_FLT0_DMA_IRQ GPDMA1_Channel15_IRQn +#define OMV_MDF_DMA_LIST_PORTS (DMA_LINK_ALLOCATED_PORT0) +#define OMV_MDF_DMA_XFER_PORTS (DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT1) + +// Camera Interface +#define OMV_CSI_CLK_SOURCE (OMV_CSI_CLK_SOURCE_TIM) +#define OMV_CSI_CLK_FREQUENCY (24000000) +#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_CLK_SLEEP_DISABLE() __TIM1_CLK_SLEEP_DISABLE() +#define OMV_CSI_DMA_CHANNEL (HPDMA1_Channel12) +#define OMV_CSI_DMA_REQUEST (HPDMA1_REQUEST_DCMI_PSSI) +#define OMV_CSI_DMA_MEMCPY_ENABLE (0) +#define OMV_CSI_DMA_LIST_PORTS (DMA_LINK_ALLOCATED_PORT0) +#define OMV_CSI_DMA_XFER_PORTS (DMA_SRC_ALLOCATED_PORT1 | DMA_DEST_ALLOCATED_PORT0) +#define OMV_CSI_HW_CROP_ENABLE (1) +#define OMV_CSI_MAX_DEVICES (3) +#define OMV_CSI_STATS_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 //__BOARD_CONFIG_H__ diff --git a/firmware/boards/LUCIDCODE_INSPEC_N6/board_config.mk b/firmware/boards/LUCIDCODE_INSPEC_N6/board_config.mk new file mode 100644 index 000000000..75cfbae1c --- /dev/null +++ b/firmware/boards/LUCIDCODE_INSPEC_N6/board_config.mk @@ -0,0 +1,51 @@ +MCU=STM32N657xx +CPU=cortex-m55 +FPU=fpv5-d16 +PORT=stm32 +SYSTEM=st/system_stm32n6 +OMV_USB_VID=0x37C5 +OMV_USB_PID=0x1206 +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_JLINK_ARGS=--device STM32N657L0 +OMV_STLINK_ARGS=--extload $(OMV_PROG_STLDR) +OMV_HSE_VALUE=48000000 +OMV_ENABLE_BL=1 +OMV_BOSON_ENABLE=1 +OMV_GENX320_ENABLE=1 +OMV_LEPTON_SDK_ENABLE=1 +OMV_USB_STACK_TINYUSB=1 +MICROPY_PY_CSI = 1 +MICROPY_PY_CSI_NG = 1 +MICROPY_PY_PROTOCOL = 1 +MICROPY_PY_FIR = 1 +MICROPY_PY_TOF = 1 +MICROPY_PY_IMU = 1 +MICROPY_PY_CRC = 1 +MICROPY_PY_ULAB = 1 +MICROPY_PY_DISPLAY = 1 +MICROPY_PY_TV = 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 diff --git a/firmware/boards/LUCIDCODE_INSPEC_N6/boot_config.h b/firmware/boards/LUCIDCODE_INSPEC_N6/boot_config.h new file mode 100644 index 000000000..b917c9203 --- /dev/null +++ b/firmware/boards/LUCIDCODE_INSPEC_N6/boot_config.h @@ -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 __BOOT_CONFIG_H__ +#define __BOOT_CONFIG_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 //__BOOT_CONFIG_H__ diff --git a/firmware/boards/LUCIDCODE_INSPEC_N6/imlib_config.h b/firmware/boards/LUCIDCODE_INSPEC_N6/imlib_config.h new file mode 100644 index 000000000..acd20bc61 --- /dev/null +++ b/firmware/boards/LUCIDCODE_INSPEC_N6/imlib_config.h @@ -0,0 +1,149 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * 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__ diff --git a/firmware/boards/LUCIDCODE_INSPEC_N6/manifest.py b/firmware/boards/LUCIDCODE_INSPEC_N6/manifest.py new file mode 100644 index 000000000..2ee2982cf --- /dev/null +++ b/firmware/boards/LUCIDCODE_INSPEC_N6/manifest.py @@ -0,0 +1,57 @@ +# OpenMV library +add_library("openmv-lib", "$(OMV_LIB_DIR)") + +# Drivers +require("onewire") +require("ds18x20") +require("dht") +require("neopixel") +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)/", "ssd1351.py") +freeze ("$(OMV_LIB_DIR)/", "pca9674a.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") + +# Bluetooth +require("aioble") +freeze ("$(OMV_LIB_DIR)/", "ble_advertising.py") + +# 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("senml") +require("logging") +freeze ("$(OMV_LIB_DIR)/", "mutex.py") + +# Libraries +require("ml", library="openmv-lib") +include("$(MPY_DIR)/extmod/asyncio") + +# Boot script +freeze ("$(OMV_LIB_DIR)/", "_boot.py") + +# INSPEC +freeze ("$(TOP_DIR)/../../../../software", "ble.py") +freeze ("$(TOP_DIR)/../../../../software", "face.py") +freeze ("$(TOP_DIR)/../../../../software", "config.py") +freeze ("$(TOP_DIR)/../../../../software", "inspec.py") +freeze ("$(TOP_DIR)/../../../../software", "wifi.py") +freeze ("$(TOP_DIR)/../../../../software", "rem.py") +freeze ("$(TOP_DIR)/../../../../software", "nrem.py") +freeze ("$(TOP_DIR)/../../../../software", "quality.py") +freeze ("$(TOP_DIR)/../../../../software", "led.py") +freeze ("$(TOP_DIR)/../../../../software", "lsd.py") +freeze ("$(TOP_DIR)/../../../../software", "version.py") diff --git a/firmware/boards/LUCIDCODE_INSPEC_N6/pins_config.h b/firmware/boards/LUCIDCODE_INSPEC_N6/pins_config.h new file mode 100644 index 000000000..cf6624824 --- /dev/null +++ b/firmware/boards/LUCIDCODE_INSPEC_N6/pins_config.h @@ -0,0 +1,61 @@ +OMV_GPIO_DEFINE(A, 12, AF5, SPI2) // SPI_DISPLAY_SCLK +OMV_GPIO_DEFINE(D, 11, AF5, SPI2) // SPI_DISPLAY_MISO +OMV_GPIO_DEFINE(D, 7, AF5, SPI2) // SPI_DISPLAY_MOSI +OMV_GPIO_DEFINE(A, 11, AF5, SPI2) // SPI_DISPLAY_SSEL +OMV_GPIO_DEFINE(D, 13, NONE, GPIO) // SPI_DISPLAY_RS +OMV_GPIO_DEFINE(G, 13, NONE, GPIO) // SPI_DISPLAY_RST +OMV_GPIO_DEFINE(G, 0, NONE, GPIO) // SPI_DISPLAY_BL + +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 diff --git a/firmware/boards/LUCIDCODE_INSPEC_N6/romfs_config.json b/firmware/boards/LUCIDCODE_INSPEC_N6/romfs_config.json new file mode 100644 index 000000000..63cda2471 --- /dev/null +++ b/firmware/boards/LUCIDCODE_INSPEC_N6/romfs_config.json @@ -0,0 +1,82 @@ +{ + "0": { + "size": "0x1800000", + "entries": [ + { + "type": "tflite", + "path": "{TOP}/lib/models/fomo_face_detection.tflite", + "alignment": 32, + "profile": "default" + }, + { + "type": "tflite", + "path": "{TOP}/lib/models/force_int_quant.tflite", + "alignment": 32, + "profile": "default" + }, + { + "type": "tflite", + "path": "{TOP}/lib/models/person_detect.tflite", + "alignment": 32, + "profile": "default" + }, + { + "type": "tflite", + "path": "{TOP}/lib/models/yolo_lc_192.tflite", + "alignment": 32, + "profile": "default" + }, + { + "type": "tflite", + "path": "{TOP}/lib/models/yolov8n_192.tflite", + "alignment": 32, + "profile": "default" + }, + { + "type": "tflite", + "path": "{TOP}/lib/models/blazeface_front_128.tflite", + "alignment": 32, + "profile": "default" + }, + { + "type": "tflite", + "path": "{TOP}/lib/models/face_landmarks_192.tflite", + "alignment": 32, + "profile": "default" + }, + { + "type": "tflite", + "path": "{TOP}/lib/models/palm_detection_full_192.tflite", + "alignment": 32, + "profile": "default" + }, + { + "type": "tflite", + "path": "{TOP}/lib/models/hand_landmarks_full_224.tflite", + "alignment": 32, + "profile": "default" + }, + { + "type": "tflite", + "path": "{TOP}/lib/models/movenet_singlepose_192.tflite", + "alignment": 32, + "profile": "default" + }, + { + "type": "haar", + "path": "{TOP}/lib/haar/haarcascade_eye.xml", + "stages": 0 + }, + { + "type": "haar", + "path": "{TOP}/lib/haar/haarcascade_smile.xml", + "stages": 0 + }, + { + "type": "haar", + "path": "{TOP}/lib/haar/haarcascade_frontalface.xml", + "stages": 0 + } + ] + } +} diff --git a/firmware/boards/LUCIDCODE_INSPEC_N6/ulab_config.h b/firmware/boards/LUCIDCODE_INSPEC_N6/ulab_config.h new file mode 100644 index 000000000..6205c6745 --- /dev/null +++ b/firmware/boards/LUCIDCODE_INSPEC_N6/ulab_config.h @@ -0,0 +1,17 @@ +/* + * This file is part of the OpenMV project. + * Copyright (c) 2013-2016 Kwabena W. Agyeman + * 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__ diff --git a/firmware/lib/micropython/ports/mimxrt/modmimxrt.c b/firmware/lib/micropython/ports/mimxrt/modmimxrt.c index d69902738..145c8527d 100644 --- a/firmware/lib/micropython/ports/mimxrt/modmimxrt.c +++ b/firmware/lib/micropython/ports/mimxrt/modmimxrt.c @@ -27,10 +27,108 @@ #include "py/mperrno.h" #include "py/runtime.h" #include "modmimxrt.h" +#include "flash.h" +#include BOARD_FLASH_OPS_HEADER_H + +// mimxrt.flash_erase(addr) +// Erase a 4KB sector at the given flash-relative address. +static mp_obj_t mimxrt_flash_erase(mp_obj_t addr_obj) { + uint32_t addr = mp_obj_get_int(addr_obj); + status_t status = flash_erase_sector(addr); + if (status != 0) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mimxrt_flash_erase_obj, mimxrt_flash_erase); + +// mimxrt.flash_write(addr, data) +// Write bytes to flash at the given flash-relative address. Sector must be erased first. +static mp_obj_t mimxrt_flash_write(mp_obj_t addr_obj, mp_obj_t data_obj) { + uint32_t addr = mp_obj_get_int(addr_obj); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ); + status_t status = flash_write_block(addr, bufinfo.buf, bufinfo.len); + if (status != 0) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(mimxrt_flash_write_obj, mimxrt_flash_write); + +// mimxrt.flash_read(addr, length) +// Read bytes from flash at the given flash-relative address. +static mp_obj_t mimxrt_flash_read(mp_obj_t addr_obj, mp_obj_t len_obj) { + uint32_t addr = mp_obj_get_int(addr_obj); + uint32_t length = mp_obj_get_int(len_obj); + vstr_t vstr; + vstr_init_len(&vstr, length); + flash_read_block(addr, (uint8_t *)vstr.buf, length); + return mp_obj_new_bytes_from_vstr(&vstr); +} +static MP_DEFINE_CONST_FUN_OBJ_2(mimxrt_flash_read_obj, mimxrt_flash_read); + +// mimxrt.flash_copy_and_reset(src_addr, dst_addr, length) +// Copy data between flash regions entirely from RAM, then reset. +// Used to copy staged firmware from ROMFS to the app region. +// IRQs are disabled and DCache is off for the entire operation so that +// no code is fetched from the flash region being overwritten. +// This function does not return. +__attribute__((section(".ram_functions"))) +static void flash_copy_and_reset_impl(uint32_t src_addr, uint32_t dst_addr, uint32_t length) { + uint32_t sector_size = SECTOR_SIZE_BYTES; + uint32_t page_size = PAGE_SIZE_BYTES; + uint32_t offset = 0; + + // Keep IRQs disabled and DCache off for the entire copy. + __disable_irq(); + SCB_DisableDCache(); + + while (offset < length) { + // Erase destination sector. + flexspi_nor_flash_erase_sector(BOARD_FLEX_SPI, dst_addr + offset); + + // Write one sector in page-sized chunks. + uint32_t remaining = length - offset; + if (remaining > sector_size) { + remaining = sector_size; + } + uint32_t written = 0; + while (written < remaining) { + uint32_t chunk = remaining - written; + if (chunk > page_size) { + chunk = page_size; + } + // Read from source via memory-mapped flash. + const uint8_t *src = (const uint8_t *)(BOARD_FLEX_SPI_ADDR_BASE + src_addr + offset + written); + flexspi_nor_flash_page_program(BOARD_FLEX_SPI, dst_addr + offset + written, (uint32_t *)src, chunk); + written += chunk; + } + + offset += sector_size; + } + + NVIC_SystemReset(); + while (1) {} +} + +static mp_obj_t mimxrt_flash_copy_and_reset(mp_obj_t src_obj, mp_obj_t dst_obj, mp_obj_t len_obj) { + uint32_t src_addr = mp_obj_get_int(src_obj); + uint32_t dst_addr = mp_obj_get_int(dst_obj); + uint32_t length = mp_obj_get_int(len_obj); + flash_copy_and_reset_impl(src_addr, dst_addr, length); + // Never reached. + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(mimxrt_flash_copy_and_reset_obj, mimxrt_flash_copy_and_reset); static const mp_rom_map_elem_t mimxrt_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mimxrt) }, { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&mimxrt_flash_type) }, + { MP_ROM_QSTR(MP_QSTR_flash_erase), MP_ROM_PTR(&mimxrt_flash_erase_obj) }, + { MP_ROM_QSTR(MP_QSTR_flash_write), MP_ROM_PTR(&mimxrt_flash_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_flash_read), MP_ROM_PTR(&mimxrt_flash_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_flash_copy_and_reset), MP_ROM_PTR(&mimxrt_flash_copy_and_reset_obj) }, #if MICROPY_HW_USB_MSC { MP_ROM_QSTR(MP_QSTR_MSC), MP_ROM_TRUE }, #endif diff --git a/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/bdev.c b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/bdev.c new file mode 100644 index 000000000..c6c918bd6 --- /dev/null +++ b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/bdev.c @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George + * + * 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. + */ + +#include "storage.h" +#include "xspi.h" + +#if MICROPY_HW_SPIFLASH_ENABLE_CACHE +#error "Cannot enable MICROPY_HW_SPIFLASH_ENABLE_CACHE" +#endif + +// External SPI flash uses hardware XSPI interface. +const mp_spiflash_config_t spiflash_config = { + .bus_kind = MP_SPIFLASH_BUS_QSPI, + .bus.u_qspi.data = (void *)&xspi_flash2, + .bus.u_qspi.proto = &xspi_proto, +}; + +spi_bdev_t spi_bdev; diff --git a/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/board.c b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/board.c new file mode 100644 index 000000000..d4b2fa1a1 --- /dev/null +++ b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/board.c @@ -0,0 +1,157 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 Damien P. George + * + * 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. + */ + +#include "py/mphal.h" +#include "boardctrl.h" +#include "xspi.h" + +// Values for OTP fuses for VDDIO2/3, to select low voltage mode (<2.5V). +// See RM0486, Section 5, Table 18. +#define BSEC_HW_CONFIG_ID (124U) +#define BSEC_HWS_HSLV_VDDIO3 (1U << 15) +#define BSEC_HWS_HSLV_VDDIO2 (1U << 16) + +#define OMV_BOOT_MAGIC_ADDR (0x3401FFFCU) +#define OMV_BOOT_MAGIC_VALUE (0xB00710ADU) + +void mboot_board_early_init(void) { + // TODO: move some of the below code to a common location for all N6 boards? + + // Enable PWR, BSEC and SYSCFG clocks. + LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG); + + // Program high speed IO optimization fuses if they aren't already set. + uint32_t fuse; + BSEC_HandleTypeDef hbsec = { .Instance = BSEC }; + const uint32_t mask = BSEC_HWS_HSLV_VDDIO2 | BSEC_HWS_HSLV_VDDIO3; + if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) { + fuse = 0; + } else if ((fuse & mask) != mask) { + // Program the fuse, and read back the set value. + if (HAL_BSEC_OTP_Program(&hbsec, BSEC_HW_CONFIG_ID, fuse | mask, HAL_BSEC_NORMAL_PROG) != HAL_OK) { + fuse = 0; + } else if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) { + fuse = 0; + } + } + + // Enable Vdd ADC, needed for the ADC to work. + LL_PWR_EnableVddADC(); + + // Configure VDDIO2. Only enable 1.8V mode if the fuse is set. + LL_PWR_EnableVddIO2(); + if (fuse & BSEC_HWS_HSLV_VDDIO2) { + LL_PWR_SetVddIO2VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_1V8); + } + SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation + + // Configure VDDIO3. Only enable 1.8V mode if the fuse is set. + LL_PWR_EnableVddIO3(); + if (fuse & BSEC_HWS_HSLV_VDDIO3) { + LL_PWR_SetVddIO3VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_1V8); + } + SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation + + // Configure VDDIO4. + LL_PWR_EnableVddIO4(); + LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3); + SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation + + // Enable VDD for ADC and USB. + LL_PWR_EnableVddADC(); + LL_PWR_EnableVddUSB(); + + // Enable XSPI in memory-mapped mode. + xspi_init(); +} + +void board_enter_bootloader(unsigned int n_args, const void *args) { + // Support both OpenMV bootloader and mboot. + *((uint32_t *)OMV_BOOT_MAGIC_ADDR) = OMV_BOOT_MAGIC_VALUE; + SCB_CleanDCache(); + NVIC_SystemReset(); +} + +static char _boot_mem[128] __attribute__((aligned(1024))); + +__attribute__((naked, noreturn, section(".ram_function"))) void ram_reset(void) { + // NVIC_SystemReset doesn't get inlined here. + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk); + __DSB(); + for (;;) { + __NOP(); + } +} + +void board_early_init(void) { + // TODO: if (HAL_PWREx_ConfigSupply(PWR_EXTERNAL_SOURCE_SUPPLY ) != HAL_OK) + + LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN3 | LL_PWR_WAKEUP_PIN2); + LL_PWR_SetWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN3 | LL_PWR_WAKEUP_PIN2); +} + +void board_enter_standby(void) { + HAL_PWREx_EnableTCMRetention(); + HAL_PWREx_EnableTCMFLXRetention(); + + uint32_t *boot_mem = (uint32_t *)_boot_mem; + boot_mem[0] = (uint32_t)(_boot_mem + sizeof(_boot_mem)); + boot_mem[1] = ((uint32_t)&ram_reset) | 1; + SCB_CleanDCache_by_Addr((uint32_t *)_boot_mem, 32); + + SYSCFG->INITSVTORCR = (uint32_t) boot_mem; + (void) SYSCFG->INITSVTORCR; +} + +void board_leave_standby(void) { + // TODO: move some of the below code to a common location for all N6 boards? + + // Enable PWR, BSEC and SYSCFG clocks. + LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG); + + // Configure VDDIO2 (1.8V mode selection is retained). + LL_PWR_EnableVddIO2(); + SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation + + // Configure VDDIO3 (1.8V mode selection is retained). + LL_PWR_EnableVddIO3(); + SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation + + // Configure VDDIO4. + LL_PWR_EnableVddIO4(); + LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3); + SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation + + // Enable VDD for ADC and USB. + LL_PWR_EnableVddADC(); + LL_PWR_EnableVddUSB(); +} diff --git a/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/board.ld b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/board.ld new file mode 100644 index 000000000..e9ded785f --- /dev/null +++ b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/board.ld @@ -0,0 +1,39 @@ +/* + Linker script for OPENMV_N6. + + Note: upper 512k of SRAM2 is copied from external flash upon reset. +*/ + +/* Specify the memory areas */ +MEMORY +{ + FLEXRAM_S (xrw) : ORIGIN = 0x34000000, LENGTH = 80K + SRAM2_S_RAM (xrw) : ORIGIN = 0x34100000, LENGTH = 1024K + SRAM2_S_FSBL (xrw) : ORIGIN = 0x34180400, LENGTH = 511K /* mboot firmware, not needed after mboot exits */ + EXT_FLASH (rx) : ORIGIN = 0x70080000, LENGTH = 3584K + EXT_FLASH_FS (rx) : ORIGIN = 0x70400000, LENGTH = 4M + EXT_FLASH_ROMFS (rx) : ORIGIN = 0x70800000, LENGTH = 24M +} + +REGION_ALIAS("IRAM", FLEXRAM_S); +REGION_ALIAS("RAM", SRAM2_S_RAM); +REGION_ALIAS("FLASH_APP", EXT_FLASH); + +/* produce a link error if there is not this amount of RAM for these sections */ +_minimum_stack_size = 2K; +_minimum_heap_size = 16K; + +/* Define the stack. The stack is full descending so begins just above last byte + of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ +_estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve; +_sstack = _estack - 16K; /* tunable */ + +/* RAM extents for the garbage collector */ +_ram_start = ORIGIN(RAM); +_ram_end = ORIGIN(RAM) + LENGTH(RAM); +_heap_start = _ebss; /* heap starts just after statically allocated memory */ +_heap_end = _sstack; + +/* ROMFS location */ +_micropy_hw_romfs_part0_start = ORIGIN(EXT_FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(EXT_FLASH_ROMFS); diff --git a/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/manifest.py b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/manifest.py new file mode 100644 index 000000000..62990220f --- /dev/null +++ b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/manifest.py @@ -0,0 +1,3 @@ +include("$(PORT_DIR)/boards/manifest.py") +require("bundle-networking") +require("aioble") diff --git a/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/mpconfigboard.h b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/mpconfigboard.h new file mode 100644 index 000000000..751da650b --- /dev/null +++ b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/mpconfigboard.h @@ -0,0 +1,190 @@ +#define MICROPY_HW_BOARD_NAME "OpenMV N6" +#define MICROPY_HW_MCU_NAME "STM32N657X0" + +#define MICROPY_GC_STACK_ENTRY_TYPE uint32_t +#define MICROPY_ALLOC_GC_STACK_SIZE (128) + +#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) + +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) +#define MICROPY_HW_HAS_SWITCH (0) +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_SDCARD_MOUNT_AT_BOOT (0) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_DAC (0) +#define MICROPY_HW_ENABLE_USB (1) +#define MICROPY_HW_ENABLE_SDCARD (1) +#define MICROPY_PY_PYB_LEGACY (0) +#define MICROPY_FATFS_EXFAT (1) + +#define MICROPY_BOARD_ENTER_BOOTLOADER board_enter_bootloader +#define MICROPY_BOARD_EARLY_INIT board_early_init +#define MICROPY_BOARD_ENTER_STANDBY board_enter_standby(); +#define MICROPY_BOARD_LEAVE_STANDBY board_leave_standby(); + +// HSE is 48MHz, this gives a CPU frequency of 800MHz. +#define MICROPY_HW_CLK_PLLM (6) +#define MICROPY_HW_CLK_PLLN (100) +#define MICROPY_HW_CLK_PLLP1 (1) +#define MICROPY_HW_CLK_PLLP2 (1) +#define MICROPY_HW_CLK_PLLFRAC (0) + +// The LSE is a 32kHz crystal. +#define MICROPY_HW_RTC_USE_LSE (1) +#define MICROPY_HW_RTC_USE_US (1) + +// External SPI flash. +#define MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2 (28) // 256Mbit + +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI (1) +#define MICROPY_HW_ROMFS_XSPI_SPIBDEV_OBJ (&spi_bdev) +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) + +// SPI flash, block device config. +#define MICROPY_HW_BDEV_SPIFLASH (&spi_bdev) +#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev) +#define MICROPY_HW_BDEV_SPIFLASH_CONFIG (&spiflash_config) +#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES (4 * 1024 * 1024) +#define MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES (4 * 1024 * 1024) + +// UART buses +#define MICROPY_HW_UART2_TX (pyb_pin_BT_TXD) +#define MICROPY_HW_UART2_RX (pyb_pin_BT_RXD) +#define MICROPY_HW_UART2_RTS (pyb_pin_BT_RTS) +#define MICROPY_HW_UART2_CTS (pyb_pin_BT_CTS) +#define MICROPY_HW_UART3_TX (pyb_pin_UART3_TX) +#define MICROPY_HW_UART3_RX (pyb_pin_UART3_RX) +#define MICROPY_HW_UART3_RTS (pyb_pin_UART3_RTS) +#define MICROPY_HW_UART4_TX (pyb_pin_UART4_TX) +#define MICROPY_HW_UART4_RX (pyb_pin_UART4_RX) +#define MICROPY_HW_UART7_TX (pyb_pin_UART7_TX) +#define MICROPY_HW_UART7_RX (pyb_pin_UART7_RX) + +// I2C buses +#define MICROPY_HW_I2C2_SCL (pyb_pin_I2C2_SCL) +#define MICROPY_HW_I2C2_SDA (pyb_pin_I2C2_SDA) +#define MICROPY_HW_I2C4_SCL (pyb_pin_I2C4_SCL) +#define MICROPY_HW_I2C4_SDA (pyb_pin_I2C4_SDA) + +// SPI buses +#define MICROPY_HW_SPI2_NSS (pyb_pin_SPI2_CS) +#define MICROPY_HW_SPI2_SCK (pyb_pin_SPI2_SCK) +#define MICROPY_HW_SPI2_MISO (pyb_pin_SPI2_MISO) +#define MICROPY_HW_SPI2_MOSI (pyb_pin_SPI2_MOSI) +#define MICROPY_HW_SPI4_NSS (pyb_pin_SPI4_CS) +#define MICROPY_HW_SPI4_SCK (pyb_pin_SPI4_SCK) +#define MICROPY_HW_SPI4_MISO (pyb_pin_SPI4_MISO) +#define MICROPY_HW_SPI4_MOSI (pyb_pin_SPI4_MOSI) + +// USER is pulled high, and pressing the button makes the input go low. +#define MICROPY_HW_USRSW_PIN (pyb_pin_BUTTON) +#define MICROPY_HW_USRSW_PULL (GPIO_NOPULL) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_FALLING) +#define MICROPY_HW_USRSW_PRESSED (0) + +// LEDs +#define MICROPY_HW_LED1 (pyb_pin_LED_RED) +#define MICROPY_HW_LED2 (pyb_pin_LED_GREEN) +#define MICROPY_HW_LED3 (pyb_pin_LED_BLUE) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) + +// SD Card SDMMC +// SD_VSELECT: low(default)=3.3V IO, high=1.8V IO +// SD_RESET: drive low to turn off SD VCC (pulled high by default) +// SD_DETECT: pulled high in hardware, goes low when SD inserted +#define MICROPY_HW_SDCARD_SDMMC (1) +#define MICROPY_HW_SDCARD_CK (pyb_pin_SD_SDIO_CK) +#define MICROPY_HW_SDCARD_CMD (pyb_pin_SD_SDIO_CMD) +#define MICROPY_HW_SDCARD_D0 (pyb_pin_SD_SDIO_D0) +#define MICROPY_HW_SDCARD_D1 (pyb_pin_SD_SDIO_D1) +#define MICROPY_HW_SDCARD_D2 (pyb_pin_SD_SDIO_D2) +#define MICROPY_HW_SDCARD_D3 (pyb_pin_SD_SDIO_D3) +#define MICROPY_HW_SDCARD_DETECT_PIN (pyb_pin_SD_DETECT) +#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_NOPULL) +#define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_RESET) + +// WiFi SDMMC +#define MICROPY_HW_SDIO_SDMMC (2) +#define MICROPY_HW_SDIO_CK (pyb_pin_WL_SDIO_CK) +#define MICROPY_HW_SDIO_CMD (pyb_pin_WL_SDIO_CMD) +#define MICROPY_HW_SDIO_D0 (pyb_pin_WL_SDIO_D0) +#define MICROPY_HW_SDIO_D1 (pyb_pin_WL_SDIO_D1) +#define MICROPY_HW_SDIO_D2 (pyb_pin_WL_SDIO_D2) +#define MICROPY_HW_SDIO_D3 (pyb_pin_WL_SDIO_D3) + +// USB config +#define MICROPY_HW_USB_HS (1) +#define MICROPY_HW_USB_HS_IN_FS (1) +#define MICROPY_HW_USB_MAIN_DEV (USB_PHY_HS_ID) +#define MICROPY_HW_USB_VID 0x37C5 +#define MICROPY_HW_USB_PID 0x1206 +#define MICROPY_HW_USB_PID_CDC (MICROPY_HW_USB_PID) +#define MICROPY_HW_USB_PID_MSC (MICROPY_HW_USB_PID) +#define MICROPY_HW_USB_PID_CDC_MSC (MICROPY_HW_USB_PID) +#define MICROPY_HW_USB_PID_CDC_HID (MICROPY_HW_USB_PID) +#define MICROPY_HW_USB_PID_CDC_MSC_HID (MICROPY_HW_USB_PID) + +// Ethernet via RGMII +#define NETWORK_LAN_PHY (ETH_PHY_RTL8211) +#define MICROPY_HW_ETH_MDC (pin_D1) +#define MICROPY_HW_ETH_MDIO (pin_D12) +#define MICROPY_HW_ETH_RGMII_CLK125 (pin_F2) +#define MICROPY_HW_ETH_RGMII_GTX_CLK (pin_F0) +#define MICROPY_HW_ETH_RGMII_TXD0 (pin_F12) +#define MICROPY_HW_ETH_RGMII_TXD1 (pin_F13) +#define MICROPY_HW_ETH_RGMII_TXD2 (pin_G3) +#define MICROPY_HW_ETH_RGMII_TXD3 (pin_G4) +#define MICROPY_HW_ETH_RGMII_TX_CTL (pin_F11) +#define MICROPY_HW_ETH_RGMII_RX_CLK (pin_F7) +#define MICROPY_HW_ETH_RGMII_RXD0 (pin_F14) +#define MICROPY_HW_ETH_RGMII_RXD1 (pin_F15) +#define MICROPY_HW_ETH_RGMII_RXD2 (pin_F8) +#define MICROPY_HW_ETH_RGMII_RXD3 (pin_F9) +#define MICROPY_HW_ETH_RGMII_RX_CTL (pin_F10) + +// Murata 1YN configuration +#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/w43439_sdio_1yn_7_95_59_combined.h" +#define CYW43_WIFI_NVRAM_INCLUDE_FILE "lib/cyw43-driver/firmware/wifi_nvram_1yn.h" +#define CYW43_BT_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/cyw43_btfw_1yn.h" + +// Bluetooth config +#define MICROPY_HW_BLE_UART_ID (PYB_UART_2) +#define MICROPY_HW_BLE_UART_BAUDRATE (115200) +#define MICROPY_HW_BLE_UART_BAUDRATE_SECONDARY (3000000) +#define MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE (3000000) + +/******************************************************************************/ +// Bootloader configuration + +#define MBOOT_BOARD_EARLY_INIT(initial_r0) mboot_board_early_init() + +#define MBOOT_FSLOAD (1) +#define MBOOT_VFS_FAT (1) + +#define MBOOT_SPIFLASH_CS (pyb_pin_XSPIM_P2_CS) +#define MBOOT_SPIFLASH_SCK (pyb_pin_XSPIM_P2_SCK) +#define MBOOT_SPIFLASH_MOSI (pyb_pin_XSPIM_P2_IO0) +#define MBOOT_SPIFLASH_MISO (pyb_pin_XSPIM_P2_IO1) +#define MBOOT_SPIFLASH_ADDR (0x70000000) +#define MBOOT_SPIFLASH_BYTE_SIZE (32 * 1024 * 1024) +#define MBOOT_SPIFLASH_LAYOUT "/0x70000000/8192*4Kg" +#define MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE (1) +#define MBOOT_SPIFLASH_SPIFLASH (&spi_bdev.spiflash) +#define MBOOT_SPIFLASH_CONFIG (&spiflash_config) + +/******************************************************************************/ +// Function and variable declarations + +extern const struct _mp_spiflash_config_t spiflash_config; +extern struct _spi_bdev_t spi_bdev; + +void mboot_board_early_init(void); +void mboot_board_entry_init(void); + +void board_enter_bootloader(unsigned int n_args, const void *args); +void board_early_init(void); +void board_enter_standby(void); +void board_leave_standby(void); diff --git a/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/mpconfigboard.mk b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/mpconfigboard.mk new file mode 100644 index 000000000..8f748a0b8 --- /dev/null +++ b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/mpconfigboard.mk @@ -0,0 +1,30 @@ +# This board requires a bootloader, either mboot or OpenMV's bootloader. +USE_MBOOT ?= 1 + +MCU_SERIES = n6 +CMSIS_MCU = STM32N657xx +AF_FILE = boards/stm32n657_af.csv +ifeq ($(BUILDING_MBOOT),1) +SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_fsbl.o +else +SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_s.o +endif +STM32_N6_HEADER_VERSION = 2.3 +DKEL = $(STM32_CUBE_PROGRAMMER)/bin/ExternalLoader/MX25UM51245G_STM32N6570-NUCLEO.stldr + +LD_FILES = boards/OPENMV_N6/board.ld boards/common_n6_flash.ld +TEXT0_ADDR = 0x70080000 + +# MicroPython settings +MICROPY_FLOAT_IMPL = float +MICROPY_PY_BLUETOOTH ?= 1 +MICROPY_BLUETOOTH_NIMBLE ?= 1 +MICROPY_BLUETOOTH_BTSTACK ?= 0 +MICROPY_PY_LWIP ?= 1 +MICROPY_PY_NETWORK_CYW43 ?= 1 +MICROPY_PY_SSL ?= 1 +MICROPY_SSL_MBEDTLS ?= 1 +MICROPY_VFS_LFS2 ?= 0 + +# Board specific frozen modules +FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py diff --git a/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/partition_stm32n657xx.h b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/partition_stm32n657xx.h new file mode 100644 index 000000000..ac38dac74 --- /dev/null +++ b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/partition_stm32n657xx.h @@ -0,0 +1,5 @@ +// This board does not use any security settings, so can just stay in secure +// mode without configuring the SAU. + +static inline void TZ_SAU_Setup(void) { +} diff --git a/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/pins.csv b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/pins.csv new file mode 100644 index 000000000..2ca075a15 --- /dev/null +++ b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/pins.csv @@ -0,0 +1,162 @@ +,PA0 +,PA1 +ONOFF,PA2 +,PA3 +,PA4 +P6_ADC,PA5 +,PA6 +,PA7 +,PA8 +,PA9 +,PA10 +SPI2_CS,PA11 +SPI2_SCK,PA12 +UART4_RX,PA11 +UART4_TX,PA12 +P3,PA11 +P2,PA12 +,PA13 +,PA14 +,PA15 +,PB0 +,PB1 +,PB2 +CHG,PB3 +,PB4 +,PB5 +SPI4_MISO,PB6 +SPI4_MOSI,PB7 +P17,PB6 +P18,PB7 +,PB8 +,PB9 +I2C2_SCL,PB10 +I2C2_SDA,PB11 +UART3_TX,PB10 +UART3_RX,PB11 +UART3_RTS,PG13 +P4,PB10 +P5,PB11 +,PB12 +,PB13 +,PB14 +,PB15 +,PC0 +,PC1 +,PC2 +,PC3 +,PC4 +,PC5 +,PC6 +,PC7 +,PC8 +,PC9 +,PC10 +,PC11 +,PC12 +P11,PC13 +,PC14 +,PC15 +,PD0 +,PD1 +,PD2 +,PD3 +,PD4 +,PD5 +P10,PD6 +SPI2_MOSI,PD7 +P0,PD7 +ST,PD8 +PG,PD9 +,PD10 +SPI2_MISO,PD11 +P1,PD11 +,PD12 +P8,PD13 +,PD14 +,PD15 +,PE0 +,PE1 +,PE2 +,PE3 +,PE4 +,PE5 +,PE6 +UART7_RX,PE7 +UART7_TX,PE8 +P13,PE7 +P14,PE8 +,PE9 +,PE10 +SPI4_CS,PE11 +SPI4_SCK,PE12 +P15,PE11 +P16,PE12 +I2C4_SCL,PE13 +I2C4_SDA,PE14 +,PE15 +,PF0 +,PF2 +,PF7 +,PF8 +,PF9 +,PF10 +,PF11 +,PF12 +,PF13 +,PF14 +,PF15 +P6,PG0 +,PG3 +,PG4 +P9,PG12 +P7,PG13 +BAT_ADC,PG15 + +SW,PF4 +LED_RED,PG10 +LED_GREEN,PA7 +LED_BLUE,PB1 + +-XSPIM_P2_DQS,PN0 +-XSPIM_P2_CS,PN1 +-XSPIM_P2_IO0,PN2 +-XSPIM_P2_IO1,PN3 +-XSPIM_P2_IO2,PN4 +-XSPIM_P2_IO3,PN5 +-XSPIM_P2_SCK,PN6 +-XSPIM_P2_NCLK,PN7 +-XSPIM_P2_IO4,PN8 +-XSPIM_P2_IO5,PN9 +-XSPIM_P2_IO6,PN10 +-XSPIM_P2_IO7,PN11 +-FLASH_RESET,PN12 + +-WL_REG_ON,PB12 +-WL_HOST_WAKE,PB14 +-WL_SDIO_D0,PB8 +-WL_SDIO_D1,PG8 +-WL_SDIO_D2,PB9 +-WL_SDIO_D3,PB4 +-WL_SDIO_CMD,PA0 +-WL_SDIO_CK,PD2 +-WL_I2S_SDO,PG14 +-WL_I2S_WS,PB15 +-WL_I2S_SCLK,PB13 +-BT_RXD,PF6 +-BT_TXD,PD5 +-BT_CTS,PG5 +-BT_RTS,PF3 +-BT_REG_ON,PD10 +-BT_HOST_WAKE,PD14 +-BT_DEV_WAKE,PD15 + +-SD_SDIO_D0,PC8 +-SD_SDIO_D1,PC9 +-SD_SDIO_D2,PC10 +-SD_SDIO_D3,PC11 +-SD_SDIO_CK,PC12 +-SD_SDIO_CMD,PH2 +-SD_RESET,PC7 +-SD_DETECT,PC6 +-SD_VSELECT,PG6 diff --git a/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/stm32n6xx_hal_conf.h b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/stm32n6xx_hal_conf.h new file mode 100644 index 000000000..4012d56e5 --- /dev/null +++ b/firmware/lib/micropython/ports/stm32/boards/LUCIDCODE_INSPEC_N6/stm32n6xx_hal_conf.h @@ -0,0 +1,18 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2019 Damien P. George + */ +#ifndef MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H + +// Oscillator values in Hz +#define HSE_VALUE (48000000) +#define LSE_VALUE (32768) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#include "boards/stm32n6xx_hal_conf_base.h" + +#endif // MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H diff --git a/release/firmware_n6.bin b/release/firmware_n6.bin new file mode 100644 index 000000000..b88889404 Binary files /dev/null and b/release/firmware_n6.bin differ diff --git a/software/ble.py b/software/ble.py index d23dd4a0e..41b643ae5 100644 --- a/software/ble.py +++ b/software/ble.py @@ -1,177 +1,177 @@ -import bluetooth -import random -import struct -import ubinascii -from ble_advertising import advertising_payload -from micropython import const -import gc - -class inspec_comms: - _APPEARANCE_HUMAN_INTERFACE_DEVICE = const(960) - - _IRQ_CENTRAL_CONNECT = const(1) - _IRQ_CENTRAL_DISCONNECT = const(2) - _IRQ_GATTS_WRITE = const(3) - _IRQ_GATTS_INDICATE_DONE = const(20) - - def __init__(self, message_received): - self.connected = False - self._connections = set() - self.sending_image = False - self.sending_file = False - self.send_errors = False - - self.ble = bluetooth.BLE() - self.ble.config(gap_name="INSPEC") - self.ble.active(True) - self.ble.irq(self.irq) - self.register() - self.advertise() - - self.wait = 0 - self.message_received = message_received - - def register(self): - BLE_UUID = '13370001-763c-4507-99fb-100f72f2300a' - RX_UUID = '13370002-763c-4507-99fb-100f72f2300a' - TX_UUID = '13370003-763c-4507-99fb-100f72f2300a' - IMG_UUID = '13370004-763c-4507-99fb-100f72f2300a' - FILE_UUID = '13370005-763c-4507-99fb-100f72f2300a' - - BLE_NUS = bluetooth.UUID(BLE_UUID) - BLE_RX = (bluetooth.UUID(RX_UUID), bluetooth.FLAG_WRITE) - BLE_TX = (bluetooth.UUID(TX_UUID), bluetooth.FLAG_NOTIFY | bluetooth.FLAG_READ | bluetooth.FLAG_INDICATE) - BLE_TX_IMG = (bluetooth.UUID(IMG_UUID), bluetooth.FLAG_NOTIFY | bluetooth.FLAG_READ | bluetooth.FLAG_INDICATE) - BLE_TX_FILE = (bluetooth.UUID(FILE_UUID), bluetooth.FLAG_NOTIFY | bluetooth.FLAG_READ | bluetooth.FLAG_INDICATE) - - BLE_UART = (BLE_NUS, (BLE_TX, BLE_RX, BLE_TX_IMG, BLE_TX_FILE)) - SERVICES = (BLE_UART,) - ((self.tx, self.rx, self.tx_img, self.tx_file),) = self.ble.gatts_register_services(SERVICES) - self.ble.gatts_set_buffer(self.rx, 256, True) - - def irq(self, event, data): - if event == _IRQ_CENTRAL_CONNECT: - conn_handle, _, _ = data - self._connections.add(conn_handle) - self.connected = True - self.send_errors = False - elif event == _IRQ_CENTRAL_DISCONNECT: - self.connected = False - conn_handle, _, _ = data - self._connections.remove(conn_handle) - self.advertise() - elif event == _IRQ_GATTS_INDICATE_DONE: - conn_handle, value_handle, status = data - - if event == _IRQ_GATTS_WRITE: - buffer = self.ble.gatts_read(self.rx) - message = buffer.decode('UTF-8') - self.message_received(message) - - def advertise(self): - self.payload = advertising_payload( - name="INSPEC", - services=[bluetooth.UUID(0x181A)], - appearance=_APPEARANCE_HUMAN_INTERFACE_DEVICE, - ) - self.ble.gap_advertise(100, adv_data=self.payload) - - def send_data(self, data): - if self.sending_image: - return - if self.connected: - for conn_handle in self._connections: - try: - self.ble.gatts_write(self.tx, data) - self.ble.gatts_notify(conn_handle, self.tx) - except: - print("BLE write error") - self.connected = False - - def send_image(self, image): - if self.sending_image: - return - - gc.collect() - - while image.width() >= 240: - image = image.scale(x_scale=0.5, y_scale=0.5) - - self.compressed = image.to_jpeg(quality=70).bytearray() - - self.cframe = bytearray(len(self.compressed)) - self.cframe[:] = self.compressed - - self.chunks_sent = 0 - self.sending_image = True - - def send_config(self, directory, config): - if self.sending_file: - return - if self.sending_image: - return - - self.send_data("config", str(directory)) - - gc.collect() - - self.cframe = bytearray(len(config)) - self.cframe[:] = config - - self.chunks_sent = 0 - self.sending_file = True - - def send_file(self, config): - if self.sending_file: - return - if self.sending_image: - return - - gc.collect() - - self.cframe = bytearray(len(config)) - self.cframe[:] = config - - self.chunks_sent = 0 - self.sending_file = True - - def process_file(self): - if self.wait > 0: - self.wait -= 1 - return - - iterations = 0 - for chunk in range(self.chunks_sent * 200, len(self.cframe), 200): - self.chunks_sent += 1 - part = self.cframe[chunk:chunk + 200] - - if self.sending_file: - self.ble.gatts_write(self.tx_file, part) - - if self.sending_image: - self.ble.gatts_write(self.tx_img, part) - - for conn_handle in self._connections: - if self.sending_file: - self.ble.gatts_notify(conn_handle, self.tx_file) - if self.sending_image: - self.ble.gatts_notify(conn_handle, self.tx_img) - - if len(part) + ((self.chunks_sent - 1) * 200) == len(self.cframe): - self.sending_file = False - self.sending_image = False - - iterations += 1 - if iterations > 10: - self.wait = 5 - break - - def disconnect(self): - if self.connected: - for conn_handle in self._connections: - try: - self.ble.gap_disconnect(conn_handle) - except: - print("BLE disconnect error") - self.connected = False - self.advertise() +import bluetooth +import random +import struct +import ubinascii +from ble_advertising import advertising_payload +from micropython import const +import gc + +class inspec_comms: + _APPEARANCE_HUMAN_INTERFACE_DEVICE = const(960) + + _IRQ_CENTRAL_CONNECT = const(1) + _IRQ_CENTRAL_DISCONNECT = const(2) + _IRQ_GATTS_WRITE = const(3) + _IRQ_GATTS_INDICATE_DONE = const(20) + + def __init__(self, message_received): + self.connected = False + self._connections = set() + self.sending_image = False + self.sending_file = False + self.send_errors = False + + self.ble = bluetooth.BLE() + self.ble.config(gap_name="INSPEC") + self.ble.active(True) + self.ble.irq(self.irq) + self.register() + self.advertise() + + self.wait = 0 + self.message_received = message_received + + def register(self): + BLE_UUID = '13370001-763c-4507-99fb-100f72f2300a' + RX_UUID = '13370002-763c-4507-99fb-100f72f2300a' + TX_UUID = '13370003-763c-4507-99fb-100f72f2300a' + IMG_UUID = '13370004-763c-4507-99fb-100f72f2300a' + FILE_UUID = '13370005-763c-4507-99fb-100f72f2300a' + + BLE_NUS = bluetooth.UUID(BLE_UUID) + BLE_RX = (bluetooth.UUID(RX_UUID), bluetooth.FLAG_WRITE) + BLE_TX = (bluetooth.UUID(TX_UUID), bluetooth.FLAG_NOTIFY | bluetooth.FLAG_READ | bluetooth.FLAG_INDICATE) + BLE_TX_IMG = (bluetooth.UUID(IMG_UUID), bluetooth.FLAG_NOTIFY | bluetooth.FLAG_READ | bluetooth.FLAG_INDICATE) + BLE_TX_FILE = (bluetooth.UUID(FILE_UUID), bluetooth.FLAG_NOTIFY | bluetooth.FLAG_READ | bluetooth.FLAG_INDICATE) + + BLE_UART = (BLE_NUS, (BLE_TX, BLE_RX, BLE_TX_IMG, BLE_TX_FILE)) + SERVICES = (BLE_UART,) + ((self.tx, self.rx, self.tx_img, self.tx_file),) = self.ble.gatts_register_services(SERVICES) + self.ble.gatts_set_buffer(self.rx, 256, True) + + def irq(self, event, data): + if event == _IRQ_CENTRAL_CONNECT: + conn_handle, _, _ = data + self._connections.add(conn_handle) + self.connected = True + self.send_errors = False + elif event == _IRQ_CENTRAL_DISCONNECT: + self.connected = False + conn_handle, _, _ = data + self._connections.remove(conn_handle) + self.advertise() + elif event == _IRQ_GATTS_INDICATE_DONE: + conn_handle, value_handle, status = data + + if event == _IRQ_GATTS_WRITE: + buffer = self.ble.gatts_read(self.rx) + message = buffer.decode('UTF-8') + self.message_received(message) + + def advertise(self): + self.payload = advertising_payload( + name="INSPEC", + services=[bluetooth.UUID(0x181A)], + appearance=_APPEARANCE_HUMAN_INTERFACE_DEVICE, + ) + self.ble.gap_advertise(100, adv_data=self.payload) + + def send_data(self, data): + if self.sending_image: + return + if self.connected: + for conn_handle in self._connections: + try: + self.ble.gatts_write(self.tx, data) + self.ble.gatts_notify(conn_handle, self.tx) + except: + print("BLE write error") + self.connected = False + + def send_image(self, image): + if self.sending_image: + return + + gc.collect() + + while image.width() >= 240: + image = image.scale(x_scale=0.5, y_scale=0.5) + + self.compressed = image.to_jpeg(quality=70).bytearray() + + self.cframe = bytearray(len(self.compressed)) + self.cframe[:] = self.compressed + + self.chunks_sent = 0 + self.sending_image = True + + def send_config(self, directory, config): + if self.sending_file: + return + if self.sending_image: + return + + self.send_data("config", str(directory)) + + gc.collect() + + self.cframe = bytearray(len(config)) + self.cframe[:] = config + + self.chunks_sent = 0 + self.sending_file = True + + def send_file(self, config): + if self.sending_file: + return + if self.sending_image: + return + + gc.collect() + + self.cframe = bytearray(len(config)) + self.cframe[:] = config + + self.chunks_sent = 0 + self.sending_file = True + + def process_file(self): + if self.wait > 0: + self.wait -= 1 + return + + iterations = 0 + for chunk in range(self.chunks_sent * 200, len(self.cframe), 200): + self.chunks_sent += 1 + part = self.cframe[chunk:chunk + 200] + + if self.sending_file: + self.ble.gatts_write(self.tx_file, part) + + if self.sending_image: + self.ble.gatts_write(self.tx_img, part) + + for conn_handle in self._connections: + if self.sending_file: + self.ble.gatts_notify(conn_handle, self.tx_file) + if self.sending_image: + self.ble.gatts_notify(conn_handle, self.tx_img) + + if len(part) + ((self.chunks_sent - 1) * 200) == len(self.cframe): + self.sending_file = False + self.sending_image = False + + iterations += 1 + if iterations > 10: + self.wait = 5 + break + + def disconnect(self): + if self.connected: + for conn_handle in self._connections: + try: + self.ble.gap_disconnect(conn_handle) + except: + print("BLE disconnect error") + self.connected = False + self.advertise() diff --git a/software/inspec.py b/software/inspec.py index 31660c804..eea90c3c4 100644 --- a/software/inspec.py +++ b/software/inspec.py @@ -53,7 +53,7 @@ def configure_sensor(self): self.sensor.hmirror(True if self.config.get('HorizontalMirror') else False) self.sensor.vflip(True if self.config.get('VerticalFlip') else False) - self.sensor.framesize(csi.HQVGA) + self.sensor.framesize(csi.QVGA) pixformat_map = { 'RGB565': csi.RGB565, @@ -92,48 +92,40 @@ def configure_sensor(self): def monitor(self): while True: - try: - self.img = self.sensor.snapshot() - - self.face.detect(self.img, self.global_variance) - self.global_variance, self.variance = self.img.variation(self.extra_fb, self.config.get('PixelThreshold'), self.config.get('PixelRange'), self.face.face_object) - - if self.global_variance > self.peak_variance: - self.peak_variance = self.global_variance - self.extra_fb.draw_image(self.img) - self.face.draw_region(self.img) - - self.detect_motion() - self.detect_face() - self.detect_rem() - self.detect_nrem() - self.quality.measure(self.global_variance) - - self.led.process() - self.process_trigger() - self.process_api("variance", self.global_variance) - - if (utime.ticks_ms() - self.last_update > 128): - face = "1" if self.face.has_face else "0" - data = f'{str(self.peak_variance)};{self.rem.eye_movements};{face};{self.quality.indicator}' - self.comms.send_data(data) - self.send_stream() - self.peak_variance = 0 - self.last_update = utime.ticks_ms() - - if self.comms.sending_image or self.comms.sending_file: - self.comms.process_file() - - if self.error != None and self.comms.send_errors: - self.comms.send_data(f'error:{self.error}') - self.error = None - - except Exception as e: - self.error = str(e) - print("Error", self.error) - self.led.blink("R", 8) - if self.error == "IDE interrupt": - break + self.img = self.sensor.snapshot() + + self.face.detect(self.img, self.global_variance) + self.global_variance, self.variance = self.img.variation(self.extra_fb, self.config.get('PixelThreshold'), self.config.get('PixelRange'), self.face.face_object) + + if self.global_variance > self.peak_variance: + self.peak_variance = self.global_variance + self.extra_fb.draw_image(self.img) + self.face.draw_region(self.img) + + self.detect_motion() + self.detect_face() + self.detect_rem() + self.detect_nrem() + self.quality.measure(self.global_variance) + + self.led.process() + self.process_trigger() + self.process_api("variance", self.global_variance) + + if (utime.ticks_ms() - self.last_update > 128): + face = "1" if self.face.has_face else "0" + data = f'{str(self.peak_variance)};{self.rem.eye_movements};{face};{self.quality.indicator}' + self.comms.send_data(data) + self.send_stream() + self.peak_variance = 0 + self.last_update = utime.ticks_ms() + + if self.comms.sending_image or self.comms.sending_file: + self.comms.process_file() + + if self.error != None and self.comms.send_errors: + self.comms.send_data(f'error:{self.error}') + self.error = None def ble_message_received(self, message): print("ble message", message) diff --git a/software/ota.py b/software/ota.py new file mode 100644 index 000000000..26504e90b --- /dev/null +++ b/software/ota.py @@ -0,0 +1,102 @@ +import gc +import ubinascii +import utime +import mimxrt + + +class ota_updater: + BLOCK_SIZE = 4096 + # Firmware is staged 4MB into the 8MB ROMFS region. + STAGING_ADDR = 0xC00000 + + def __init__(self, comms): + self.comms = comms + self.expected_size = 0 + self.expected_crc = 0 + self.received_bytes = 0 + self.target_addr = 0 + self.write_addr = 0 + self.active = False + self.last_erased_sector = -1 + + def begin(self, addr_hex, size_str, crc_hex): + self.target_addr = int(addr_hex, 16) + self.expected_size = int(size_str) + self.expected_crc = int(crc_hex, 16) + self.received_bytes = 0 + self.active = True + self.last_erased_sector = -1 + + # ROMFS region: write directly to target address. + # App region: stage in unused ROMFS space, copy later via C function. + if self.target_addr >= 0x800000: + self.write_addr = self.target_addr + else: + self.write_addr = self.STAGING_ADDR + + gc.collect() + self.comms.send_data('ota:ready') + + def receive_chunk(self, data): + if not self.active: + return + + write_pos = self.write_addr + self.received_bytes + + # Erase the 4KB sector if this is the first write into it. + sector = write_pos // self.BLOCK_SIZE + if sector != self.last_erased_sector: + mimxrt.flash_erase(sector * self.BLOCK_SIZE) + self.last_erased_sector = sector + + mimxrt.flash_write(write_pos, data) + self.received_bytes += len(data) + + # Report progress every 4KB. + if self.received_bytes % self.BLOCK_SIZE < len(data): + self.comms.send_data(f'ota:{self.received_bytes}') + + if self.received_bytes >= self.expected_size: + self.active = False + self.comms.ota_mode = False + self.comms.send_data(f'ota:complete:{self.received_bytes}') + + def verify(self): + if self.received_bytes != self.expected_size: + self.comms.send_data(f'ota:error:size_mismatch:{self.received_bytes}') + return False + + crc = 0 + remaining = self.expected_size + addr = self.write_addr + while remaining > 0: + chunk_size = min(self.BLOCK_SIZE, remaining) + chunk = mimxrt.flash_read(addr, chunk_size) + crc = ubinascii.crc32(chunk, crc) + addr += chunk_size + remaining -= chunk_size + crc = crc & 0xFFFFFFFF + + if crc == self.expected_crc: + self.comms.send_data('ota:verified') + return True + else: + self.comms.send_data(f'ota:crc_mismatch:{crc:08X}') + return False + + def apply(self): + if self.target_addr >= 0x800000: + # ROMFS was written directly in place. + self.comms.send_data('ota:applied') + else: + # Firmware staged in ROMFS. Copy to app region from RAM, then reset. + self.comms.send_data('ota:applying') + utime.sleep_ms(500) + mimxrt.flash_copy_and_reset(self.STAGING_ADDR, self.target_addr, self.expected_size) + # Does not return. + + def abort(self): + self.active = False + self.received_bytes = 0 + self.comms.ota_mode = False + self.comms.send_data('ota:aborted')