diff --git a/.github/workflows/build-firmware.yml b/.github/workflows/build-firmware.yml index 51236ee..ad95ce9 100644 --- a/.github/workflows/build-firmware.yml +++ b/.github/workflows/build-firmware.yml @@ -6,8 +6,8 @@ on: branches: [ "main", "next" ] tags: - "*.*.*" - - "v*.*.*" - - "pre-*" + - "*.*.*-rc.*" + - "*.*.*-dev.*" release: types: [created] @@ -65,7 +65,7 @@ jobs: echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT - if [[ "$TAG_NAME" == pre-* ]] || [[ "$TAG_NAME" == *"-rc"* ]] || [[ "$TAG_NAME" == *"-beta"* ]]; then + if [[ "$TAG_NAME" == *"-rc."* ]] || [[ "$TAG_NAME" == *"-dev."* ]]; then echo "prerelease=true" >> $GITHUB_OUTPUT else echo "prerelease=false" >> $GITHUB_OUTPUT diff --git a/.mxproject b/.mxproject index 418d38c..16dfa6d 100644 --- a/.mxproject +++ b/.mxproject @@ -1,5 +1,5 @@ [PreviousLibFiles] -LibFiles=Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_tim.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_tim_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_cortex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_cortex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_crc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_crc_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_crc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rcc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rcc_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_bus.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_rcc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_crs.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_system.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_utils.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_flash.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_flash_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_gpio.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_gpio_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_gpio.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_hsem.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_hsem.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_dma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_dma_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_dma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_dmamux.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_mdma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pwr.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pwr_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_pwr.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_def.h;Drivers\STM32H7xx_HAL_Driver\Inc\Legacy\stm32_hal_legacy.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_i2c.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_i2c_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_exti.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_exti.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_i2c.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rng.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rng_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_rng.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_spi.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_spi.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_spi_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_tim.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_uart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_usart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_lpuart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_uart_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_usart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_usart_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pcd.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pcd_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_usb.h;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_cortex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_gpio.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_hsem.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_mdma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_exti.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_ll_usb.c;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_tim.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_tim_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_cortex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_cortex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_crc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_crc_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_crc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rcc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rcc_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_bus.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_rcc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_crs.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_system.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_utils.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_flash.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_flash_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_gpio.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_gpio_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_gpio.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_hsem.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_hsem.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_dma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_dma_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_dma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_dmamux.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_mdma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pwr.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pwr_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_pwr.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_def.h;Drivers\STM32H7xx_HAL_Driver\Inc\Legacy\stm32_hal_legacy.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_i2c.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_i2c_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_exti.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_exti.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_i2c.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rng.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rng_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_rng.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_spi.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_spi.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_spi_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_tim.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_uart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_usart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_lpuart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_uart_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_usart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_usart_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pcd.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pcd_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_usb.h;Drivers\CMSIS\Device\ST\STM32H7xx\Include\stm32h743xx.h;Drivers\CMSIS\Device\ST\STM32H7xx\Include\stm32h7xx.h;Drivers\CMSIS\Device\ST\STM32H7xx\Include\system_stm32h7xx.h;Drivers\CMSIS\Device\ST\STM32H7xx\Include\system_stm32h7xx.h;Drivers\CMSIS\Device\ST\STM32H7xx\Source\Templates\system_stm32h7xx.c;Drivers\CMSIS\Include\cmsis_armcc.h;Drivers\CMSIS\Include\cmsis_armclang.h;Drivers\CMSIS\Include\cmsis_armclang_ltm.h;Drivers\CMSIS\Include\cmsis_compiler.h;Drivers\CMSIS\Include\cmsis_gcc.h;Drivers\CMSIS\Include\cmsis_iccarm.h;Drivers\CMSIS\Include\cmsis_version.h;Drivers\CMSIS\Include\core_armv81mml.h;Drivers\CMSIS\Include\core_armv8mbl.h;Drivers\CMSIS\Include\core_armv8mml.h;Drivers\CMSIS\Include\core_cm0.h;Drivers\CMSIS\Include\core_cm0plus.h;Drivers\CMSIS\Include\core_cm1.h;Drivers\CMSIS\Include\core_cm23.h;Drivers\CMSIS\Include\core_cm3.h;Drivers\CMSIS\Include\core_cm33.h;Drivers\CMSIS\Include\core_cm35p.h;Drivers\CMSIS\Include\core_cm4.h;Drivers\CMSIS\Include\core_cm7.h;Drivers\CMSIS\Include\core_sc000.h;Drivers\CMSIS\Include\core_sc300.h;Drivers\CMSIS\Include\mpu_armv7.h;Drivers\CMSIS\Include\mpu_armv8.h;Drivers\CMSIS\Include\tz_context.h; +LibFiles=Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_tim.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_tim_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_cortex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_cortex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_crc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_crc_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_crc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rcc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rcc_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_bus.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_rcc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_crs.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_system.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_utils.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_flash.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_flash_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_gpio.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_gpio_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_gpio.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_hsem.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_hsem.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_dma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_dma_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_dma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_dmamux.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_mdma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pwr.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pwr_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_pwr.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_def.h;Drivers\STM32H7xx_HAL_Driver\Inc\Legacy\stm32_hal_legacy.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_i2c.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_i2c_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_exti.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_exti.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_i2c.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_iwdg.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_iwdg.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_ramecc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rng.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rng_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_rng.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_spi.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_spi.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_spi_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_tim.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_uart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_usart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_lpuart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_uart_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_usart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_usart_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pcd.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pcd_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_usb.h;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_cortex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_gpio.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_hsem.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_mdma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_exti.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_iwdg.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_ramecc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_ll_usb.c;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_tim.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_tim_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_cortex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_cortex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_crc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_crc_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_crc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rcc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rcc_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_bus.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_rcc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_crs.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_system.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_utils.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_flash.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_flash_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_gpio.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_gpio_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_gpio.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_hsem.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_hsem.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_dma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_dma_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_dma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_dmamux.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_mdma.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pwr.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pwr_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_pwr.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_def.h;Drivers\STM32H7xx_HAL_Driver\Inc\Legacy\stm32_hal_legacy.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_i2c.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_i2c_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_exti.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_exti.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_i2c.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_iwdg.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_iwdg.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_ramecc.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rng.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_rng_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_rng.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_spi.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_spi.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_spi_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_tim.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_uart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_usart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_lpuart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_uart_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_usart.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_usart_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pcd.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_hal_pcd_ex.h;Drivers\STM32H7xx_HAL_Driver\Inc\stm32h7xx_ll_usb.h;Drivers\CMSIS\Device\ST\STM32H7xx\Include\stm32h743xx.h;Drivers\CMSIS\Device\ST\STM32H7xx\Include\stm32h7xx.h;Drivers\CMSIS\Device\ST\STM32H7xx\Include\system_stm32h7xx.h;Drivers\CMSIS\Device\ST\STM32H7xx\Include\system_stm32h7xx.h;Drivers\CMSIS\Device\ST\STM32H7xx\Source\Templates\system_stm32h7xx.c;Drivers\CMSIS\Include\cmsis_armcc.h;Drivers\CMSIS\Include\cmsis_armclang.h;Drivers\CMSIS\Include\cmsis_armclang_ltm.h;Drivers\CMSIS\Include\cmsis_compiler.h;Drivers\CMSIS\Include\cmsis_gcc.h;Drivers\CMSIS\Include\cmsis_iccarm.h;Drivers\CMSIS\Include\cmsis_version.h;Drivers\CMSIS\Include\core_armv81mml.h;Drivers\CMSIS\Include\core_armv8mbl.h;Drivers\CMSIS\Include\core_armv8mml.h;Drivers\CMSIS\Include\core_cm0.h;Drivers\CMSIS\Include\core_cm0plus.h;Drivers\CMSIS\Include\core_cm1.h;Drivers\CMSIS\Include\core_cm23.h;Drivers\CMSIS\Include\core_cm3.h;Drivers\CMSIS\Include\core_cm33.h;Drivers\CMSIS\Include\core_cm35p.h;Drivers\CMSIS\Include\core_cm4.h;Drivers\CMSIS\Include\core_cm7.h;Drivers\CMSIS\Include\core_sc000.h;Drivers\CMSIS\Include\core_sc300.h;Drivers\CMSIS\Include\mpu_armv7.h;Drivers\CMSIS\Include\mpu_armv8.h;Drivers\CMSIS\Include\tz_context.h; [PreviousUsedCubeIDEFiles] SourceFiles=Core/Src/main.c;Core/Src/stm32h7xx_it.c;Core/Src/stm32h7xx_hal_msp.c;Core/Src/stm32h7xx_hal_timebase_tim.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_cortex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_crc.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_crc_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rcc.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rcc_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_flash.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_flash_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_gpio.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_hsem.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_dma.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_dma_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_mdma.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pwr.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pwr_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_i2c.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_i2c_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_exti.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rng.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rng_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_spi.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_spi_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_uart.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_uart_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_usart.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_usart_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pcd.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pcd_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_usb.c;Drivers/CMSIS/Device/ST/STM32H7xx/Source/Templates/system_stm32h7xx.c;Core/Src/system_stm32h7xx.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_tim_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_cortex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_crc.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_crc_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rcc.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rcc_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_flash.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_flash_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_gpio.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_hsem.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_dma.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_dma_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_mdma.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pwr.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pwr_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_i2c.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_i2c_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_exti.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rng.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rng_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_spi.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_spi_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_uart.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_uart_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_usart.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_usart_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pcd.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pcd_ex.c;Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_usb.c;Drivers/CMSIS/Device/ST/STM32H7xx/Source/Templates/system_stm32h7xx.c;Core/Src/system_stm32h7xx.c;;; @@ -7,7 +7,7 @@ HeaderPath=Drivers/STM32H7xx_HAL_Driver/Inc;Drivers/STM32H7xx_HAL_Driver/Inc/Leg CDefines=USE_PWR_LDO_SUPPLY;USE_PWR_LDO_SUPPLY;USE_PWR_LDO_SUPPLY;USE_HAL_DRIVER;STM32H743xx;USE_HAL_DRIVER;USE_HAL_DRIVER; [PreviousUsedCMakes] -SourceFiles=Core\Src\main.c;Core\Src\stm32h7xx_it.c;Core\Src\stm32h7xx_hal_msp.c;Core\Src\stm32h7xx_hal_timebase_tim.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_cortex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_gpio.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_hsem.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_mdma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_exti.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_ll_usb.c;Drivers\CMSIS\Device\ST\STM32H7xx\Source\Templates\system_stm32h7xx.c;Core\Src\system_stm32h7xx.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_cortex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_gpio.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_hsem.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_mdma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_exti.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_ll_usb.c;Drivers\CMSIS\Device\ST\STM32H7xx\Source\Templates\system_stm32h7xx.c;Core\Src\system_stm32h7xx.c;;; +SourceFiles=Core\Src\main.c;Core\Src\stm32h7xx_it.c;Core\Src\stm32h7xx_hal_msp.c;Core\Src\stm32h7xx_hal_timebase_tim.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_cortex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_gpio.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_hsem.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_mdma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_exti.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_iwdg.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_ramecc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_ll_usb.c;Drivers\CMSIS\Device\ST\STM32H7xx\Source\Templates\system_stm32h7xx.c;Core\Src\system_stm32h7xx.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_tim_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_cortex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_crc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rcc_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_flash_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_gpio.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_hsem.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_mdma.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pwr_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_i2c_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_exti.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_iwdg.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_ramecc.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_rng_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_spi_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_uart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_usart_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd_ex.c;Drivers\STM32H7xx_HAL_Driver\Src\stm32h7xx_ll_usb.c;Drivers\CMSIS\Device\ST\STM32H7xx\Source\Templates\system_stm32h7xx.c;Core\Src\system_stm32h7xx.c;;; HeaderPath=Drivers\STM32H7xx_HAL_Driver\Inc;Drivers\STM32H7xx_HAL_Driver\Inc\Legacy;Drivers\CMSIS\Device\ST\STM32H7xx\Include;Drivers\CMSIS\Include;Core\Inc; CDefines=USE_PWR_LDO_SUPPLY;USE_PWR_LDO_SUPPLY;USE_PWR_LDO_SUPPLY;USE_HAL_DRIVER;STM32H743xx;USE_HAL_DRIVER;USE_HAL_DRIVER; diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bc4f40..0fd21e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,9 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE Core/Src/uart_comms.c Core/Src/flash_eeprom.c Core/Src/motion_config.c + Core/Src/system_monitor.c + Core/Src/if_factory_prog.c + Core/Src/ram_scrub.c Core/Src/utils.c USB/Core/Src/usbd_ioreq.c USB/Core/Src/usbd_desc.c @@ -196,3 +199,20 @@ add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMENT "Generating .hex/.bin and merging FPGA bitstream at ${FPGA_BITSTREAM_ADDR}" ) +# Build + DFU-flash targets, one per sensor side. Invokes scripts/deploy.py. +# `--device` is required by the script, so we expose two separate targets. +find_program(PYTHON_FOR_DEPLOY NAMES python3 python) +if(PYTHON_FOR_DEPLOY) + foreach(SIDE left right) + add_custom_target(flash-${SIDE} + COMMAND ${PYTHON_FOR_DEPLOY} ${CMAKE_SOURCE_DIR}/scripts/deploy.py + --device ${SIDE} --config $ --no-build + DEPENDS ${CMAKE_PROJECT_NAME} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + USES_TERMINAL + COMMENT "Flashing ${CMAKE_PROJECT_NAME} (${SIDE} sensor) via DFU" + ) + endforeach() +else() + message(WARNING "Python not found — `flash-left`/`flash-right` targets will not be available") +endif() diff --git a/Core/Inc/camera_manager.h b/Core/Inc/camera_manager.h index 90e6cfc..d993fe8 100644 --- a/Core/Inc/camera_manager.h +++ b/Core/Inc/camera_manager.h @@ -35,6 +35,12 @@ typedef struct { uint8_t exposure; uint8_t *pRecieveHistoBuffer; size_t receiveBufferSize; // Size of the buffer + GPIO_TypeDef * detect_clk_port; + uint16_t detect_clk_pin; + uint32_t detect_clk_af; + GPIO_TypeDef * detect_data_port; + uint16_t detect_data_pin; + uint32_t detect_data_af; } CameraDevice; #define CAMERA_COUNT 8 diff --git a/Core/Inc/common.h b/Core/Inc/common.h index 6eba3d9..6f046de 100644 --- a/Core/Inc/common.h +++ b/Core/Inc/common.h @@ -72,6 +72,7 @@ typedef enum { OW_IMU = 0xE8, OW_I2C_PASSTHRU = 0xE9, OW_CONTROLLER = 0xEA, + OW_FPGA_PROG = 0xEB, OW_BAD_PARSE = 0xEC, OW_BAD_CRC = 0xED, OW_UNKNOWN = 0xEE, @@ -153,6 +154,15 @@ typedef enum { } MotionCameraCommands; +typedef enum { + OW_FACTORY_I2C_SCAN = 0x60, + OW_FACTORY_CRESET = 0x68, + OW_FACTORY_I2C_RD = 0x69, + OW_FACTORY_I2C_WR = 0x6A, + OW_FACTORY_I2C_WRRD = 0x6B, + OW_FACTORY_NVCM_CHECK = 0x6C, +} MotionFactoryCommands; + typedef enum { OW_CTRL_FAN_CTL = 0x0A, diff --git a/Core/Inc/crosslink.h b/Core/Inc/crosslink.h index 27e3b18..798c899 100644 --- a/Core/Inc/crosslink.h +++ b/Core/Inc/crosslink.h @@ -10,6 +10,47 @@ #include "main.h" #include +/* ---- NVCM (non-volatile config memory) read-back / detection ------------- */ +/* Maximum NVCM array rows the probe will read back (16 bytes each). */ +#define FPGA_NVCM_MAX_ROWS 8 + +/* step_status bits — which probe steps completed (HAL_OK) */ +#define FPGA_NVCM_STEP_ACTIVATION (1u << 0) +#define FPGA_NVCM_STEP_IDCODE (1u << 1) +#define FPGA_NVCM_STEP_ISC_ENABLE (1u << 2) +#define FPGA_NVCM_STEP_STATUS (1u << 3) +#define FPGA_NVCM_STEP_FEATROW (1u << 4) +#define FPGA_NVCM_STEP_FEABITS (1u << 5) +#define FPGA_NVCM_STEP_USERCODE (1u << 6) + +/* Raw read-back of every NVCM discriminator (all bytes in I2C wire order). */ +typedef struct { + uint8_t idcode[4]; /* 0xE0 read */ + uint8_t idcode_ok; /* 1 if idcode == {01,2C,00,43} */ + uint8_t step_status; /* FPGA_NVCM_STEP_* bitmask */ + uint8_t status[4]; /* 0x3C LSC_READ_STATUS */ + uint8_t feature_row[8]; /* 0xE7 LSC_READ_FEATURE */ + uint8_t feabits[2]; /* 0xFB LSC_READ_FEABITS */ + uint8_t usercode[4]; /* 0xC0 USERCODE */ + uint8_t boot_probe_done; /* 1 if the boot-behavior test ran */ + uint8_t boot_0x40_responds; /* after auto-boot: 1=ACK (blank), 0=gone (programmed) */ + uint8_t num_rows_read; /* NVCM array rows actually read */ + uint8_t nvcm_rows[FPGA_NVCM_MAX_ROWS * 16]; /* 0x73 LSC_READ_INCR_NV */ +} fpga_nvcm_probe_t; + +/* + * Probe the NVCM state of one CrossLink FPGA WITHOUT booting it and WITHOUT + * touching camera power. Holds the config port in slave mode (activation key + * around the CRESETB transition), enters ISC access mode with the given + * isc_operand (0x08 = NVCM, 0x00 = SRAM), and reads every discriminator. + * Caller must have already selected the camera's TCA mux channel and powered it. + * Every step is best-effort; results land in *out (zeroed first). + */ +int fpga_nvcm_probe(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, + GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, + uint8_t isc_operand, uint8_t num_rows, uint8_t do_boot_test, + fpga_nvcm_probe_t *out); + int fpga_send_activation(I2C_HandleTypeDef *hi2c, uint16_t DevAddress); int fpga_checkid(I2C_HandleTypeDef *hi2c, uint16_t DevAddress); int fpga_enter_sram_prog_mode(I2C_HandleTypeDef *hi2c, uint16_t DevAddress); @@ -20,4 +61,8 @@ uint32_t fpga_read_usercode(I2C_HandleTypeDef *hi2c, uint16_t DevAddress); int fpga_program_sram(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, bool rom_bitstream, uint8_t* pData, uint32_t Data_Len); int fpga_configure(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); +int xi2c_write_bytes(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *data, uint16_t length); +int xi2c_read_bytes(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *data, uint16_t length); +int xi2c_write_and_read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *wbuf, uint16_t wlen, uint8_t *rbuf, uint16_t rlen); + #endif /* INC_CROSSLINK_H_ */ diff --git a/Core/Inc/i2c_master.h b/Core/Inc/i2c_master.h index ab49d13..8bdcfb0 100644 --- a/Core/Inc/i2c_master.h +++ b/Core/Inc/i2c_master.h @@ -20,7 +20,12 @@ uint8_t read_data_register_of_slave(I2C_HandleTypeDef * pI2c, uint8_t slave_addr void reset_slaves(I2C_HandleTypeDef * pI2c); HAL_StatusTypeDef TCA9548A_SelectChannel(I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t channel); HAL_StatusTypeDef TCA9548A_SelectBroadcast(I2C_HandleTypeDef *hi2c, uint8_t address); -HAL_StatusTypeDef TCA9548A_EnableChannel(I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t channel); +/* NOTE: there is intentionally no TCA9548A_EnableChannel (additive select). + * Selecting multiple channels in parallel puts several FPGAs on the shared + * bus at once — see docs/tca9548a-bus-issues.md. Use SelectChannel (exactly + * one channel) or DisableChannel/DisableAll. */ HAL_StatusTypeDef TCA9548A_DisableChannel(I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t channel); +HAL_StatusTypeDef TCA9548A_DisableAll(I2C_HandleTypeDef *hi2c, uint8_t address); +void I2C_BusRecovery(I2C_HandleTypeDef *hi2c); #endif /* INC_I2C_MASTER_H_ */ diff --git a/Core/Inc/if_factory_prog.h b/Core/Inc/if_factory_prog.h new file mode 100644 index 0000000..7c2de2e --- /dev/null +++ b/Core/Inc/if_factory_prog.h @@ -0,0 +1,8 @@ +#ifndef INC_IF_FACTORY_PROG_COMMANDS_H_ +#define INC_IF_FACTORY_PROG_COMMANDS_H_ + +#include "common.h" + +_Bool process_factory_command(UartPacket *response, UartPacket *cmd); + +#endif /* INC_IF_FPGA_PROG_COMMANDS_H_ */ \ No newline at end of file diff --git a/Core/Inc/stm32h7xx_hal_conf.h b/Core/Inc/stm32h7xx_hal_conf.h index 1ca17aa..caf7392 100644 --- a/Core/Inc/stm32h7xx_hal_conf.h +++ b/Core/Inc/stm32h7xx_hal_conf.h @@ -61,11 +61,11 @@ /* #define HAL_OSPI_MODULE_ENABLED */ /* #define HAL_I2S_MODULE_ENABLED */ /* #define HAL_SMBUS_MODULE_ENABLED */ -/* #define HAL_IWDG_MODULE_ENABLED */ +#define HAL_IWDG_MODULE_ENABLED /* #define HAL_LPTIM_MODULE_ENABLED */ /* #define HAL_LTDC_MODULE_ENABLED */ /* #define HAL_QSPI_MODULE_ENABLED */ -/* #define HAL_RAMECC_MODULE_ENABLED */ +#define HAL_RAMECC_MODULE_ENABLED #define HAL_RNG_MODULE_ENABLED /* #define HAL_RTC_MODULE_ENABLED */ /* #define HAL_SAI_MODULE_ENABLED */ diff --git a/Core/Inc/stm32h7xx_it.h b/Core/Inc/stm32h7xx_it.h index fbd5507..b59f5a2 100644 --- a/Core/Inc/stm32h7xx_it.h +++ b/Core/Inc/stm32h7xx_it.h @@ -82,6 +82,7 @@ void TIM15_IRQHandler(void); void TIM16_IRQHandler(void); void TIM17_IRQHandler(void); void BDMA_Channel0_IRQHandler(void); +void ECC_IRQHandler(void); /* USER CODE BEGIN EFP */ /* USER CODE END EFP */ diff --git a/Core/Inc/system_monitor.h b/Core/Inc/system_monitor.h new file mode 100644 index 0000000..5fd4393 --- /dev/null +++ b/Core/Inc/system_monitor.h @@ -0,0 +1,38 @@ +/* + * system_monitor.h + * + * Persistent reset-cause counters, RAMECC SBE/DBE monitoring and DMA + * transfer-error scanning. Counters live in .noinit RAM so they survive + * IWDG/soft resets but are reinitialised on a power-on / brown-out reset + * (detected via a magic value check at boot). + */ + +#ifndef CORE_INC_SYSTEM_MONITOR_H_ +#define CORE_INC_SYSTEM_MONITOR_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Snapshot RCC->CSR and increment per-cause counters. Must be called BEFORE + * __HAL_RCC_CLEAR_RESET_FLAGS(). Safe to call before printf is up. */ +void system_monitor_capture_reset_cause(void); + +/* Print accumulated reset-cause / ECC / DMA counters to stdout. */ +void system_monitor_print_history(void); + +/* Start RAMECC monitoring on all 12 monitors and enable the ECC NVIC IRQ. */ +void system_monitor_ecc_enable(void); + +/* Periodic poll: prints any pending ECC events and scans DMA1/DMA2/BDMA for + * transfer-error / FIFO-error flags. Call from the main loop. */ +void system_monitor_poll(void); + +#ifdef __cplusplus +} +#endif + +#endif /* CORE_INC_SYSTEM_MONITOR_H_ */ diff --git a/Core/Src/0X02C1B.c b/Core/Src/0X02C1B.c index 48772d9..78e0617 100644 --- a/Core/Src/0X02C1B.c +++ b/Core/Src/0X02C1B.c @@ -288,8 +288,8 @@ int X02C1B_FSIN_EXT_enable() /* Configure the FSIN pin (the internal frame sync generator) to an input to rx the frame sync*/ GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = FSIN_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; // Set to input mode, rising edge trigger - GPIO_InitStruct.Pull = GPIO_NOPULL; // No pull-up or pull-down resistors + GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(FSIN_GPIO_Port, &GPIO_InitStruct); diff --git a/Core/Src/camera_manager.c b/Core/Src/camera_manager.c index 56cada7..c749687 100644 --- a/Core/Src/camera_manager.c +++ b/Core/Src/camera_manager.c @@ -90,6 +90,72 @@ static bool camera_request_is_valid(uint8_t cam_id) { } +/** + * Detect whether a CrossLink FPGA has been permanently programmed via NVCM. + * + * Method: enter forced slave config mode (activation key + CRESETB), read the + * STATUS register Done bit. Done=1 means NVCM was fully programmed (the Done + * bit is the last step burned during NVCM programming and gates auto-boot). + * + * The slave I2C config port at 0x40 requires the activation key to become + * active — without it, 0x40 never responds regardless of NVCM state. + */ +static bool fpga_detect_nvcm(CameraDevice *cam) +{ + GPIO_InitTypeDef gpio = {0}; + + /* Deselect all TCA channels before toggling CRESETB — a resetting FPGA + * can glitch the I2C bus through an open mux channel. */ + TCA9548A_DisableAll(&hi2c1, 0x70); + + gpio.Mode = GPIO_MODE_INPUT; + gpio.Pull = GPIO_NOPULL; + gpio.Speed = GPIO_SPEED_FREQ_LOW; + + gpio.Pin = cam->detect_clk_pin; + HAL_GPIO_Init(cam->detect_clk_port, &gpio); + gpio.Pin = cam->detect_data_pin; + HAL_GPIO_Init(cam->detect_data_port, &gpio); + + HAL_GPIO_WritePin(cam->cresetb_port, cam->cresetb_pin, GPIO_PIN_RESET); + delay_ms(5); + HAL_GPIO_WritePin(cam->cresetb_port, cam->cresetb_pin, GPIO_PIN_SET); + delay_ms(100); + + bool clk_low = HAL_GPIO_ReadPin(cam->detect_clk_port, cam->detect_clk_pin) == GPIO_PIN_RESET; + bool data_low = HAL_GPIO_ReadPin(cam->detect_data_port, cam->detect_data_pin) == GPIO_PIN_RESET; + if ((logging_get_debug_flags() & DEBUG_FLAG_CMD_VERBOSE) != 0u) { + printf("C%d: NVCM detect clk=%d data=%d\r\n", cam->id + 1, !clk_low, !data_low); + } + + /* Restore pins to their peripheral alternate function. */ + gpio.Mode = GPIO_MODE_AF_PP; + gpio.Pull = GPIO_NOPULL; + gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + + gpio.Pin = cam->detect_clk_pin; + gpio.Alternate = cam->detect_clk_af; + HAL_GPIO_Init(cam->detect_clk_port, &gpio); + gpio.Pin = cam->detect_data_pin; + gpio.Alternate = cam->detect_data_af; + HAL_GPIO_Init(cam->detect_data_port, &gpio); + + if (clk_low && data_low) { + /* The booted design drives this camera's bus clock; edges seen while + * the pins were re-attached can leave the USART receiver bit-shifted + * (every histogram bin reads multiplied by a power of two). Same + * reset the SRAM programming path requires after fpga_configure(). */ + if (cam->useUsart && cam->pUart != NULL) { + cam->pUart->Instance->CR1 &= ~USART_CR1_UE; + cam->pUart->Instance->CR1 |= USART_CR1_UE; + } + return true; + } + + HAL_GPIO_WritePin(cam->cresetb_port, cam->cresetb_pin, GPIO_PIN_RESET); + return false; +} + static void init_camera(CameraDevice *cam){ GPIO_InitTypeDef GPIO_InitStruct = {0}; @@ -146,6 +212,12 @@ void init_camera_sensors() { cam_array[0].pUart = &husart2; cam_array[0].i2c_target = 0; cam_array[0].pRecieveHistoBuffer = NULL; + cam_array[0].detect_clk_port = GPIOA; + cam_array[0].detect_clk_pin = GPIO_PIN_4; + cam_array[0].detect_clk_af = GPIO_AF7_USART2; + cam_array[0].detect_data_port = GPIOD; + cam_array[0].detect_data_pin = GPIO_PIN_6; + cam_array[0].detect_data_af = GPIO_AF7_USART2; cam_array[1].id = 1; cam_array[1].cresetb_port = CRESET_2_GPIO_Port; @@ -164,6 +236,12 @@ void init_camera_sensors() { cam_array[1].pUart = NULL; cam_array[1].i2c_target = 1; cam_array[1].pRecieveHistoBuffer = NULL; + cam_array[1].detect_clk_port = GPIOB; + cam_array[1].detect_clk_pin = GPIO_PIN_3; + cam_array[1].detect_clk_af = GPIO_AF8_SPI6; + cam_array[1].detect_data_port = GPIOA; + cam_array[1].detect_data_pin = GPIO_PIN_7; + cam_array[1].detect_data_af = GPIO_AF8_SPI6; cam_array[2].id = 2; cam_array[2].cresetb_port = CRESET_3_GPIO_Port; @@ -182,6 +260,12 @@ void init_camera_sensors() { cam_array[2].pUart = &husart3; cam_array[2].i2c_target = 2; cam_array[2].pRecieveHistoBuffer = NULL; + cam_array[2].detect_clk_port = GPIOD; + cam_array[2].detect_clk_pin = GPIO_PIN_10; + cam_array[2].detect_clk_af = GPIO_AF7_USART3; + cam_array[2].detect_data_port = GPIOD; + cam_array[2].detect_data_pin = GPIO_PIN_9; + cam_array[2].detect_data_af = GPIO_AF7_USART3; cam_array[3].id = 3; cam_array[3].cresetb_port = CRESET_4_GPIO_Port; @@ -200,6 +284,12 @@ void init_camera_sensors() { cam_array[3].pUart = &husart6; cam_array[3].i2c_target = 3; cam_array[3].pRecieveHistoBuffer = NULL; + cam_array[3].detect_clk_port = GPIOC; + cam_array[3].detect_clk_pin = GPIO_PIN_8; + cam_array[3].detect_clk_af = GPIO_AF7_USART6; + cam_array[3].detect_data_port = GPIOC; + cam_array[3].detect_data_pin = GPIO_PIN_7; + cam_array[3].detect_data_af = GPIO_AF7_USART6; cam_array[4].id = 4; cam_array[4].cresetb_port = CRESET_5_GPIO_Port; @@ -218,6 +308,12 @@ void init_camera_sensors() { cam_array[4].pUart = &husart1; cam_array[4].i2c_target = 4; cam_array[4].pRecieveHistoBuffer = NULL; + cam_array[4].detect_clk_port = GPIOA; + cam_array[4].detect_clk_pin = GPIO_PIN_8; + cam_array[4].detect_clk_af = GPIO_AF7_USART1; + cam_array[4].detect_data_port = GPIOB; + cam_array[4].detect_data_pin = GPIO_PIN_15; + cam_array[4].detect_data_af = GPIO_AF4_USART1; cam_array[5].id = 5; cam_array[5].cresetb_port = CRESET_6_GPIO_Port; @@ -236,6 +332,12 @@ void init_camera_sensors() { cam_array[5].pUart = NULL; cam_array[5].i2c_target = 5; cam_array[5].pRecieveHistoBuffer = NULL; + cam_array[5].detect_clk_port = GPIOC; + cam_array[5].detect_clk_pin = GPIO_PIN_10; + cam_array[5].detect_clk_af = GPIO_AF6_SPI3; + cam_array[5].detect_data_port = GPIOB; + cam_array[5].detect_data_pin = GPIO_PIN_2; + cam_array[5].detect_data_af = GPIO_AF7_SPI3; cam_array[6].id = 6; cam_array[6].cresetb_port = CRESET_7_GPIO_Port; @@ -254,6 +356,12 @@ void init_camera_sensors() { cam_array[6].pUart = NULL; cam_array[6].i2c_target = 6; cam_array[6].pRecieveHistoBuffer = NULL; + cam_array[6].detect_clk_port = GPIOA; + cam_array[6].detect_clk_pin = GPIO_PIN_9; + cam_array[6].detect_clk_af = GPIO_AF5_SPI2; + cam_array[6].detect_data_port = GPIOC; + cam_array[6].detect_data_pin = GPIO_PIN_1; + cam_array[6].detect_data_af = GPIO_AF5_SPI2; cam_array[7].id = 7; cam_array[7].cresetb_port = CRESET_8_GPIO_Port; @@ -272,6 +380,12 @@ void init_camera_sensors() { cam_array[7].pUart = NULL; cam_array[7].i2c_target = 7; cam_array[7].pRecieveHistoBuffer = NULL; + cam_array[7].detect_clk_port = GPIOE; + cam_array[7].detect_clk_pin = GPIO_PIN_2; + cam_array[7].detect_clk_af = GPIO_AF5_SPI4; + cam_array[7].detect_data_port = GPIOE; + cam_array[7].detect_data_pin = GPIO_PIN_6; + cam_array[7].detect_data_af = GPIO_AF5_SPI4; for(i=0; iisProgrammed) return true; + if(fpga_detect_nvcm(cam)){ + cam->isProgrammed = true; + printf("NVCM programmed, skipping\r\n"); + return true; + } } else { - cam->isProgrammed = false; // set programmed to false and Program FPGA + cam->isProgrammed = false; } if(TCA9548A_SelectChannel(&hi2c1, 0x70, cam->i2c_target) != HAL_OK) @@ -752,11 +871,15 @@ _Bool program_fpga(uint8_t cam_id, _Bool force_update) if(!force_update) { if(cam->isProgrammed){ - // printf("already programmed\r\n"); return true; - } + } + if(fpga_detect_nvcm(cam)){ + cam->isProgrammed = true; + printf("C%d: NVCM programmed, skipping SRAM load\r\n", cam_id+1); + return true; + } } else { - cam->isProgrammed = false; // set isProgrammed to false and program FPGA + cam->isProgrammed = false; } if(TCA9548A_SelectChannel(&hi2c1, 0x70, cam->i2c_target) != HAL_OK) @@ -1865,10 +1988,12 @@ _Bool enable_camera_power(uint8_t cam_id){ HAL_GPIO_WritePin(cam->power_port, cam->power_pin, GPIO_PIN_SET); // Set power pin high cam->isPowered = true; - if (TCA9548A_EnableChannel(&hi2c1, 0x70, cam->i2c_target) != HAL_OK) { - printf("Failed to enable mux channel for Camera %d\r\n", cam_id + 1); - return false; - } + /* Do NOT select the mux channel here. A freshly powered CrossLink with + * blank/unloaded config drives its config pins during its boot attempt, + * and those are the same physical pins as this mux channel — connecting + * the channel now clamps the shared I2C bus and the next transaction + * times out. Every I2C consumer (temp poll, FPGA programming, camera + * config) selects the channel itself right before transacting. */ printf("Enabled Power for Camera %d\r\n", cam_id+1); return true; diff --git a/Core/Src/crosslink.c b/Core/Src/crosslink.c index 7adf0a7..60ee30a 100644 --- a/Core/Src/crosslink.c +++ b/Core/Src/crosslink.c @@ -28,6 +28,8 @@ #define CMD_VERIFY_USERCODE 0xC8 #define CMD_DISABLE 0x26 +#define CROSSLINK_I2C_TIMEOUT_MS 1000U + volatile uint8_t txComplete = 0; volatile uint8_t rxComplete = 0; volatile uint8_t i2cError = 0; @@ -42,11 +44,15 @@ extern uint8_t bitstream_buffer[]; extern uint32_t bitstream_len; -static int xi2c_write_bytes(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *data, uint16_t length) { - return HAL_I2C_Master_Transmit(hi2c, DevAddress << 1, data, length, HAL_MAX_DELAY); +int xi2c_write_bytes(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *data, uint16_t length) { + return HAL_I2C_Master_Transmit(hi2c, DevAddress << 1, data, length, CROSSLINK_I2C_TIMEOUT_MS); +} + +int xi2c_read_bytes(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *data, uint16_t length) { + return HAL_I2C_Master_Receive(hi2c, DevAddress << 1, data, length, CROSSLINK_I2C_TIMEOUT_MS); } -static int xi2c_write_and_read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *wbuf, uint16_t wlen, uint8_t *rbuf, uint16_t rlen) { +int xi2c_write_and_read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *wbuf, uint16_t wlen, uint8_t *rbuf, uint16_t rlen) { txComplete = 0; rxComplete = 0; i2cError = 0; @@ -222,6 +228,156 @@ uint32_t fpga_read_usercode(I2C_HandleTypeDef *hi2c, uint16_t DevAddress) return 0; } +/* Compact always-on hex log (USB routing still gated by DEBUG_FLAG_USB_PRINTF). */ +static void nvcm_log_buf(const char *label, const uint8_t *buf, int len) +{ + printf("NVCM %s:", label); + for (int i = 0; i < len; i++) printf(" %02X", buf[i]); + printf("\r\n"); +} + +int fpga_nvcm_probe(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, + GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, + uint8_t isc_operand, uint8_t num_rows, uint8_t do_boot_test, + fpga_nvcm_probe_t *out) +{ + uint8_t wbuf[4]; + + memset(out, 0, sizeof(*out)); + if (num_rows > FPGA_NVCM_MAX_ROWS) num_rows = FPGA_NVCM_MAX_ROWS; + + printf("NVCM probe: isc_operand=0x%02X num_rows=%u boot_test=%u addr=0x%02X\r\n", + isc_operand, (unsigned)num_rows, (unsigned)do_boot_test, (unsigned)DevAddress); + + /* 1. Hold config port in SLAVE mode: CRESETB low, activation key, CRESETB + * high. Sending the key while/around the CRESETB transition declares the + * slave port active so the device enters config mode instead of booting + * from NVCM — exactly what fpga_configure() does for SRAM. This keeps the + * config I2C port at 0x40 alive throughout and never powers anything. */ + HAL_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PIN_RESET); + delay_ms(1); + if (xi2c_write_bytes(hi2c, DevAddress, activation_key, 5) == HAL_OK) { + out->step_status |= FPGA_NVCM_STEP_ACTIVATION; + } else { + printf("NVCM: activation key write FAILED\r\n"); + } + HAL_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PIN_SET); + delay_ms(10); + + /* 2. IDCODE (sanity: is the config port answering at all?) */ + wbuf[0] = 0xE0; wbuf[1] = 0x00; wbuf[2] = 0x00; wbuf[3] = 0x00; + if (xi2c_write_and_read(hi2c, DevAddress, wbuf, 4, out->idcode, 4) == HAL_OK) { + out->step_status |= FPGA_NVCM_STEP_IDCODE; + out->idcode_ok = (memcmp(out->idcode, expected_idcode, 4) == 0) ? 1 : 0; + nvcm_log_buf("IDCODE", out->idcode, 4); + printf("NVCM IDCODE_OK: %u\r\n", (unsigned)out->idcode_ok); + } else { + printf("NVCM: IDCODE read FAILED\r\n"); + } + + /* 3. ISC_ENABLE — operand 0x08 = NVCM access, 0x00 = SRAM access. */ + wbuf[0] = 0xC6; wbuf[1] = isc_operand; wbuf[2] = 0x00; wbuf[3] = 0x00; + if (xi2c_write_bytes(hi2c, DevAddress, wbuf, 4) == HAL_OK) { + out->step_status |= FPGA_NVCM_STEP_ISC_ENABLE; + } else { + printf("NVCM: ISC_ENABLE(0x%02X) FAILED\r\n", isc_operand); + } + delay_ms(5); + + /* 4. Status register (0x3C): bit8 Done, bit6 OTP/NVCM-blank-check. */ + wbuf[0] = 0x3C; wbuf[1] = 0x00; wbuf[2] = 0x00; wbuf[3] = 0x00; + if (xi2c_write_and_read(hi2c, DevAddress, wbuf, 4, out->status, 4) == HAL_OK) { + out->step_status |= FPGA_NVCM_STEP_STATUS; + nvcm_log_buf("STATUS", out->status, 4); + } else { + printf("NVCM: STATUS read FAILED\r\n"); + } + + /* 5. Feature Row (0xE7, 8 bytes): all-zero = blank, non-zero = programmed. */ + wbuf[0] = 0xE7; wbuf[1] = 0x00; wbuf[2] = 0x00; wbuf[3] = 0x00; + if (xi2c_write_and_read(hi2c, DevAddress, wbuf, 4, out->feature_row, 8) == HAL_OK) { + out->step_status |= FPGA_NVCM_STEP_FEATROW; + nvcm_log_buf("FEATROW", out->feature_row, 8); + } else { + printf("NVCM: FEATROW read FAILED\r\n"); + } + + /* 6. Feature Bits (0xFB, 2 bytes). */ + wbuf[0] = 0xFB; wbuf[1] = 0x00; wbuf[2] = 0x00; wbuf[3] = 0x00; + if (xi2c_write_and_read(hi2c, DevAddress, wbuf, 4, out->feabits, 2) == HAL_OK) { + out->step_status |= FPGA_NVCM_STEP_FEABITS; + nvcm_log_buf("FEABITS", out->feabits, 2); + } else { + printf("NVCM: FEABITS read FAILED\r\n"); + } + + /* 7. USERCODE (0xC0, 4 bytes): non-zero if the build programmed one. */ + wbuf[0] = 0xC0; wbuf[1] = 0x00; wbuf[2] = 0x00; wbuf[3] = 0x00; + if (xi2c_write_and_read(hi2c, DevAddress, wbuf, 4, out->usercode, 4) == HAL_OK) { + out->step_status |= FPGA_NVCM_STEP_USERCODE; + nvcm_log_buf("USERCODE", out->usercode, 4); + } else { + printf("NVCM: USERCODE read FAILED\r\n"); + } + + /* 8. NVCM array rows (0x73 LSC_READ_INCR_NV, 16 bytes/row). Reset the + * address counter first (0x46 LSC_INIT_ADDRESS), then read incrementally. + * Blank rows read all-zero. */ + if (num_rows > 0) { + wbuf[0] = 0x46; wbuf[1] = 0x00; wbuf[2] = 0x00; wbuf[3] = 0x00; + xi2c_write_bytes(hi2c, DevAddress, wbuf, 4); + delay_ms(1); + for (uint8_t r = 0; r < num_rows; r++) { + wbuf[0] = 0x73; wbuf[1] = 0x21; wbuf[2] = 0x00; wbuf[3] = 0x00; + if (xi2c_write_and_read(hi2c, DevAddress, wbuf, 4, + &out->nvcm_rows[r * 16], 16) == HAL_OK) { + out->num_rows_read = r + 1; + char lbl[12]; + snprintf(lbl, sizeof(lbl), "ROW%u", (unsigned)r); + nvcm_log_buf(lbl, &out->nvcm_rows[r * 16], 16); + } else { + printf("NVCM: ROW%u read FAILED\r\n", (unsigned)r); + break; + } + delay_ms(1); + } + } + + /* 9. ISC_DISABLE — end the config-read phase cleanly. */ + wbuf[0] = 0x26; wbuf[1] = 0x00; wbuf[2] = 0x00; wbuf[3] = 0x00; + xi2c_write_bytes(hi2c, DevAddress, wbuf, 4); + + /* 10. Boot-behavior test (behaviorally DEFINITIVE). Release CRESETB WITHOUT + * sending the activation key, so no slave port is declared active and the + * device performs master auto-boot. A programmed NVCM boots its user + * design, which reassigns the config I2C pins (I2C_PORT=DISABLE by + * default) so 0x40 stops ACKing. A blank part has nothing to boot and + * 0x40 keeps ACKing. + * 0x40 ACKs -> blank + * 0x40 gone -> programmed + * Ends by re-asserting CRESETB low to halt any booted user design and + * free the shared I2C bus (TCA-safe). */ + if (do_boot_test) { + HAL_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PIN_RESET); /* assert reset */ + delay_ms(5); + HAL_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PIN_SET); /* release, NO activation key */ + delay_ms(150); /* allow full auto-boot */ + + out->boot_probe_done = 1; + out->boot_0x40_responds = + (HAL_I2C_IsDeviceReady(hi2c, DevAddress << 1, 2, 50) == HAL_OK) ? 1 : 0; + printf("NVCM boot-test: 0x40 responds=%u (1=blank, 0=programmed)\r\n", + (unsigned)out->boot_0x40_responds); + + HAL_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PIN_RESET); /* halt: hold in reset, free bus */ + } + + printf("NVCM probe done: step_status=0x%02X rows=%u boot_done=%u boot_0x40=%u\r\n", + (unsigned)out->step_status, (unsigned)out->num_rows_read, + (unsigned)out->boot_probe_done, (unsigned)out->boot_0x40_responds); + return 0; +} + int fpga_erase_sram(I2C_HandleTypeDef *hi2c, uint16_t DevAddress) { // Step 4: Erase SRAM diff --git a/Core/Src/i2c_master.c b/Core/Src/i2c_master.c index 83711dc..3601bd2 100644 --- a/Core/Src/i2c_master.c +++ b/Core/Src/i2c_master.c @@ -59,8 +59,10 @@ static HAL_StatusTypeDef TCA9548A_WriteControl(I2C_HandleTypeDef *hi2c, uint8_t printf("requested: 0x%02X current: 0x%02X addr: 0x%02X ret: %d err: %lu\r\n", data, I2C_current_target, address, ret, hi2c->ErrorCode); - /* Recover bus by resetting mux state machine and deselecting channels. */ TCA9548A_ResetHardware(); + if (attempt >= 1) { + I2C_BusRecovery(hi2c); + } } return ret; @@ -213,22 +215,66 @@ HAL_StatusTypeDef TCA9548A_SelectBroadcast(I2C_HandleTypeDef *hi2c, uint8_t addr return TCA9548A_WriteControl(hi2c, address, data); } -HAL_StatusTypeDef TCA9548A_EnableChannel(I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t channel) +HAL_StatusTypeDef TCA9548A_DisableChannel(I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t channel) { if (channel > 7) { return HAL_ERROR; } - uint8_t data = (uint8_t)(I2C_current_target | (1u << channel)); + uint8_t data = (uint8_t)(I2C_current_target & (uint8_t)~(1u << channel)); return TCA9548A_WriteControl(hi2c, address, data); } -HAL_StatusTypeDef TCA9548A_DisableChannel(I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t channel) +HAL_StatusTypeDef TCA9548A_DisableAll(I2C_HandleTypeDef *hi2c, uint8_t address) { - if (channel > 7) { - return HAL_ERROR; + return TCA9548A_WriteControl(hi2c, address, 0x00); +} + +void I2C_BusRecovery(I2C_HandleTypeDef *hi2c) +{ + GPIO_InitTypeDef gpio = {0}; + + /* I2C1: PB8 = SCL, PB7 = SDA, AF4 */ + HAL_I2C_DeInit(hi2c); + + gpio.Pin = GPIO_PIN_8; + gpio.Mode = GPIO_MODE_OUTPUT_OD; + gpio.Pull = GPIO_PULLUP; + gpio.Speed = GPIO_SPEED_FREQ_LOW; + HAL_GPIO_Init(GPIOB, &gpio); + + gpio.Pin = GPIO_PIN_7; + HAL_GPIO_Init(GPIOB, &gpio); + + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); + + /* Clock out up to 9 bits to free a slave holding SDA low. */ + for (int i = 0; i < 9; i++) { + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); + delay_us(5); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); + delay_us(5); + if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET) + break; } - uint8_t data = (uint8_t)(I2C_current_target & (uint8_t)~(1u << channel)); - return TCA9548A_WriteControl(hi2c, address, data); + /* Generate STOP: SDA low → SCL high → SDA high. */ + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); + delay_us(5); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); + delay_us(5); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); + delay_us(5); + + /* Restore AF and reinit. */ + gpio.Pin = GPIO_PIN_7 | GPIO_PIN_8; + gpio.Mode = GPIO_MODE_AF_OD; + gpio.Pull = GPIO_NOPULL; + gpio.Speed = GPIO_SPEED_FREQ_LOW; + gpio.Alternate = GPIO_AF4_I2C1; + HAL_GPIO_Init(GPIOB, &gpio); + + HAL_I2C_Init(hi2c); + I2C_current_target = 0x00; + printf("I2C bus recovery complete\r\n"); } diff --git a/Core/Src/if_commands.c b/Core/Src/if_commands.c index 22a5e65..95c1b25 100644 --- a/Core/Src/if_commands.c +++ b/Core/Src/if_commands.c @@ -8,6 +8,7 @@ #include "version.h" #include "main.h" #include "if_commands.h" +#include "if_factory_prog.h" #include "common.h" #include "jsmn.h" #include "i2c_master.h" @@ -1137,6 +1138,9 @@ UartPacket process_if_command(UartPacket cmd) uartReturn.packet_type = OW_ERROR; } break; + case OW_FPGA_PROG: + process_factory_command(&uartReturn, &cmd); + break; default: uartReturn.data_len = 0; uartReturn.packet_type = OW_UNKNOWN; diff --git a/Core/Src/if_factory_prog.c b/Core/Src/if_factory_prog.c new file mode 100644 index 0000000..824e0c8 --- /dev/null +++ b/Core/Src/if_factory_prog.c @@ -0,0 +1,275 @@ +#include "main.h" +#include "common.h" +#include "crosslink.h" +#include "camera_manager.h" +#include "if_factory_prog.h" +#include "i2c_master.h" +#include "utils.h" + +#include +#include +#include +#include + +extern I2C_HandleTypeDef hi2c1; + +static uint8_t i2c_list[128] = {0}; + +uint8_t i2c_write_buf[256] = {0}; +uint8_t i2c_read_buf[256] = {0}; + +static int iRet; +static uint32_t _creset_state = 0; +static CameraDevice* _active_cam = NULL; +static fpga_nvcm_probe_t nvcm_probe; + +_Bool process_factory_command(UartPacket *response, UartPacket *cmd) +{ + response->id = cmd->id; + response->packet_type = OW_RESP; + response->addr = 0; + response->reserved = 0; + response->data_len = 0; + response->data = 0; + + switch (cmd->packet_type) + { + case OW_FPGA_PROG: + response->command = cmd->command; + switch (cmd->command) + { + case OW_FACTORY_I2C_SCAN: + response->command = OW_FACTORY_I2C_SCAN; + _active_cam = get_active_cam(); + if(_active_cam == NULL) + { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + break; + } + + memset(i2c_list, 0, 128); + iRet = I2C_scan(_active_cam->pI2c, i2c_list, 128, true); + if (iRet < 0) + { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + } + else + { + response->data_len = (uint16_t)iRet; + response->data = i2c_list; + } + break; + case OW_FACTORY_CRESET: + response->command = OW_FACTORY_CRESET; + _active_cam = get_active_cam(); + if(_active_cam == NULL) + { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + break; + } + + if(cmd->data_len == 1) + { + if(cmd->data[0] == 0x01){ + HAL_GPIO_WritePin(_active_cam->cresetb_port, _active_cam->cresetb_pin, GPIO_PIN_SET); + _creset_state = 1; + }else{ + HAL_GPIO_WritePin(_active_cam->cresetb_port, _active_cam->cresetb_pin, GPIO_PIN_RESET); + _creset_state = 0; + } + }else{ + _creset_state = HAL_GPIO_ReadPin(_active_cam->cresetb_port, _active_cam->cresetb_pin); + } + + response->data_len = 1; + response->data = (uint8_t*)&_creset_state; + + break; + case OW_FACTORY_I2C_WR: + response->command = OW_FACTORY_I2C_WR; + _active_cam = get_active_cam(); + if(_active_cam == NULL) + { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + break; + } + + if (cmd->data_len < 4) + { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + }else{ + uint16_t write_len = cmd->data[0] << 8 | cmd->data[1]; + uint8_t *write_data = &cmd->data[2]; + memset(i2c_write_buf, 0, sizeof(i2c_write_buf)); + if (write_len > sizeof(i2c_write_buf)) { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + } else { + memcpy(i2c_write_buf, write_data, write_len); + iRet = xi2c_write_bytes(_active_cam->pI2c, _active_cam->device_address, i2c_write_buf, write_len); + if (iRet != HAL_OK) { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + } + } + } + break; + case OW_FACTORY_I2C_RD: + response->command = OW_FACTORY_I2C_RD; + _active_cam = get_active_cam(); + if(_active_cam == NULL) + { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + break; + } + + if (cmd->data_len < 2) + { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + }else{ + uint16_t read_len = cmd->data[0] << 8 | cmd->data[1]; + + memset(i2c_read_buf, 0, sizeof(i2c_read_buf)); + iRet = xi2c_read_bytes(_active_cam->pI2c, _active_cam->device_address, i2c_read_buf, read_len); + if (iRet != HAL_OK) { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + } else { + response->data_len = read_len; + response->data = i2c_read_buf; + } + } + break; + case OW_FACTORY_I2C_WRRD: + response->command = OW_FACTORY_I2C_WRRD; + _active_cam = get_active_cam(); + if(_active_cam == NULL) + { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + break; + } + + if (cmd->data_len < 2) + { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + }else{ + uint16_t write_len = cmd->data[0] << 8 | cmd->data[1]; + uint16_t read_len = cmd->data[2] << 8 | cmd->data[3]; + uint8_t *write_data = &cmd->data[4]; + memset(i2c_write_buf, 0, sizeof(i2c_write_buf)); + memset(i2c_read_buf, 0, sizeof(i2c_read_buf)); + if (write_len > sizeof(i2c_write_buf) || read_len > sizeof(i2c_read_buf)) { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + } else { + memcpy(i2c_write_buf, write_data, write_len); + iRet = xi2c_write_and_read(_active_cam->pI2c, _active_cam->device_address, i2c_write_buf, write_len, i2c_read_buf, read_len); + if (iRet != HAL_OK) { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + } else { + response->data_len = read_len; + response->data = i2c_read_buf; + } + } + } + break; + case OW_FACTORY_NVCM_CHECK: + { + response->command = OW_FACTORY_NVCM_CHECK; + _active_cam = get_active_cam(); + if(_active_cam == NULL) + { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + break; + } + + /* Params: data[0] = ISC_ENABLE operand (default 0x08 = NVCM + * access, 0x00 = SRAM); data[1] = # of 16-byte NVCM rows to + * read back (default 1); data[2] = run boot-behavior test + * (default 1). Parameterized so the host can vary them + * without reflashing. */ + uint8_t isc_operand = (cmd->data_len >= 1) ? cmd->data[0] : 0x08; + uint8_t num_rows = (cmd->data_len >= 2) ? cmd->data[1] : 1; + uint8_t do_boot_test = (cmd->data_len >= 3) ? cmd->data[2] : 1; + + /* Route this camera's mux channel (mux I2C only — no power). */ + if (TCA9548A_SelectChannel(_active_cam->pI2c, 0x70, _active_cam->i2c_target) != HAL_OK) + { + response->packet_type = OW_ERROR; + response->data_len = 0; + response->data = NULL; + break; + } + + fpga_nvcm_probe(_active_cam->pI2c, _active_cam->device_address, + _active_cam->cresetb_port, _active_cam->cresetb_pin, + isc_operand, num_rows, do_boot_test, &nvcm_probe); + + /* Serialize a fixed-layout blob into i2c_read_buf: + * [0..3] idcode, [4] idcode_ok, [5] step_status, + * [6..9] status, [10..17] feature_row, [18..19] feabits, + * [20..23] usercode, [24] boot_probe_done, + * [25] boot_0x40_responds, [26] num_rows_read, + * [27..] rows(16B each) */ + uint16_t n = 0; + memcpy(&i2c_read_buf[n], nvcm_probe.idcode, 4); n += 4; + i2c_read_buf[n++] = nvcm_probe.idcode_ok; + i2c_read_buf[n++] = nvcm_probe.step_status; + memcpy(&i2c_read_buf[n], nvcm_probe.status, 4); n += 4; + memcpy(&i2c_read_buf[n], nvcm_probe.feature_row, 8); n += 8; + memcpy(&i2c_read_buf[n], nvcm_probe.feabits, 2); n += 2; + memcpy(&i2c_read_buf[n], nvcm_probe.usercode, 4); n += 4; + i2c_read_buf[n++] = nvcm_probe.boot_probe_done; + i2c_read_buf[n++] = nvcm_probe.boot_0x40_responds; + i2c_read_buf[n++] = nvcm_probe.num_rows_read; + if (nvcm_probe.num_rows_read > 0) + { + uint16_t rb = (uint16_t)nvcm_probe.num_rows_read * 16u; + memcpy(&i2c_read_buf[n], nvcm_probe.nvcm_rows, rb); + n += rb; + } + response->data_len = n; + response->data = i2c_read_buf; + break; + } + default: + response->packet_type = OW_UNKNOWN; + response->data_len = 0; + response->data = NULL; + break; + } + break; + default: + response->data_len = 0; + response->packet_type = OW_UNKNOWN; + break; + } + + return true; +} \ No newline at end of file diff --git a/Core/Src/main.c b/Core/Src/main.c index 7c88894..168baef 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -1,2038 +1,2214 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file : main.c - * @brief : Main program body - ****************************************************************************** - * @attention - * - * Copyright (c) 2024 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** - */ -/* USER CODE END Header */ -/* Includes ------------------------------------------------------------------*/ -#include "main.h" - -/* Private includes ----------------------------------------------------------*/ -/* USER CODE BEGIN Includes */ -#include "0X02C1B.h" -#include "usb_device.h" -#include "uart_comms.h" -#include "usbd_histo.h" -#include "usbd_imu.h" -#include "usbd_comms.h" -#include "logging.h" -#include "utils.h" -#include "i2c_master.h" -#include "histo_fake.h" -#include "ICM20948.h" -#include "camera_manager.h" - -#include -#include -#include - -#include "stm32h7xx_ll_tim.h" - -/* USER CODE END Includes */ - -/* Private typedef -----------------------------------------------------------*/ -/* USER CODE BEGIN PTD */ - -/* External references to USB endpoint status variables */ -extern volatile uint8_t tx_flag; // From uart_comms.c -extern volatile uint8_t comms_ep_data; // From usbd_comms.c (__IO is volatile) -extern volatile uint8_t histo_ep_data; // From usbd_histo.c (__IO is volatile) -extern volatile uint8_t imu_ep_data; // From usbd_imu.c (__IO is volatile) - -/* USER CODE END PTD */ - -/* Private define ------------------------------------------------------------*/ -/* USER CODE BEGIN PD */ -/* USER CODE END PD */ - -/* Private macro -------------------------------------------------------------*/ -/* USER CODE BEGIN PM */ - -/* USER CODE END PM */ - -/* Private variables ---------------------------------------------------------*/ - -CRC_HandleTypeDef hcrc; - -I2C_HandleTypeDef hi2c1; - -RNG_HandleTypeDef hrng; - -SPI_HandleTypeDef hspi2; -SPI_HandleTypeDef hspi3; -SPI_HandleTypeDef hspi4; -SPI_HandleTypeDef hspi6; -DMA_HandleTypeDef hdma_spi2_rx; -DMA_HandleTypeDef hdma_spi3_rx; -DMA_HandleTypeDef hdma_spi4_rx; -DMA_HandleTypeDef hdma_spi6_rx; - -TIM_HandleTypeDef htim2; -TIM_HandleTypeDef htim4; -TIM_HandleTypeDef htim5; -TIM_HandleTypeDef htim8; -TIM_HandleTypeDef htim14; -TIM_HandleTypeDef htim15; -TIM_HandleTypeDef htim16; - -UART_HandleTypeDef huart4; -USART_HandleTypeDef husart1; -USART_HandleTypeDef husart2; -USART_HandleTypeDef husart3; -USART_HandleTypeDef husart6; -DMA_HandleTypeDef hdma_uart4_rx; -DMA_HandleTypeDef hdma_uart4_tx; -DMA_HandleTypeDef hdma_usart1_rx; -DMA_HandleTypeDef hdma_usart2_rx; -DMA_HandleTypeDef hdma_usart3_rx; -DMA_HandleTypeDef hdma_usart6_rx; - -DMA_HandleTypeDef hdma_memtomem_dma2_stream1; -/* USER CODE BEGIN PV */ - -uint8_t rxBuffer[COMMAND_MAX_SIZE] __attribute__((aligned(4))); -uint8_t txBuffer[COMMAND_MAX_SIZE]; -uint32_t bitstream_len; -__attribute__((section(".RAM_D1"))) uint8_t bitstream_buffer[MAX_BITSTREAM_SIZE]; // 160KB buffer - -volatile uint8_t event_bits = 0x00; // holds the event bits to be flipped -volatile uint8_t event_bits_enabled = 0x00; // holds the event bits for the cameras to be enabled -volatile uint16_t pulse_count = 0; -extern uint32_t imu_frame_counter; -volatile bool _enter_dfu = false; - -ICM_Axis3D a; -ICM_Axis3D m; -ICM_Axis3D g; -float t; -char usb_buf[128]; -// Debug flags (sensor debug and fake data are controlled via OW_CMD_DEBUG_FLAGS) -bool uart_stream = false; - -uint16_t fail_count = 0; - -extern USBD_HandleTypeDef hUsbDeviceHS; - -const char *bit_rep[16] = { - [ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011", - [ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111", - [ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011", - [12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111", -}; -/* USER CODE END PV */ - -/* Private function prototypes -----------------------------------------------*/ -void SystemClock_Config(void); -void PeriphCommonClock_Config(void); -static void MPU_Config(void); -static void MX_GPIO_Init(void); -static void MX_DMA_Init(void); -static void MX_BDMA_Init(void); -static void MX_CRC_Init(void); -static void MX_I2C1_Init(void); -static void MX_RNG_Init(void); -static void MX_SPI2_Init(void); -static void MX_SPI3_Init(void); -static void MX_SPI4_Init(void); -static void MX_SPI6_Init(void); -static void MX_TIM2_Init(void); -static void MX_UART4_Init(void); -static void MX_USART6_Init(void); -static void MX_TIM8_Init(void); -static void MX_TIM4_Init(void); -static void MX_USART1_Init(void); -static void MX_USART2_Init(void); -static void MX_USART3_Init(void); -static void MX_TIM14_Init(void); -static void MX_TIM16_Init(void); -static void MX_TIM5_Init(void); -static void MX_TIM15_Init(void); -/* USER CODE BEGIN PFP */ - -/* USER CODE END PFP */ - -/* Private user code ---------------------------------------------------------*/ -/* USER CODE BEGIN 0 */ -/* Print last reset cause - helps diagnose thermal/brownout (BOR) or watchdog (IWDG) */ -static void print_reset_cause(void) -{ - uint8_t first = 1; - if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) { - printf("Reset cause: BOR (Brown-Out) - possible thermal/voltage droop\r\n"); - first = 0; - } - if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST)) { - printf("%sReset cause: POR (Power-On)\r\n", first ? "" : " "); - first = 0; - } - if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST)) { - printf("%sReset cause: PIN (External NRST)\r\n", first ? "" : " "); - first = 0; - } - if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)) { - printf("%sReset cause: Software\r\n", first ? "" : " "); - first = 0; - } -#if defined(RCC_FLAG_IWDG1RST) - if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDG1RST)) { - printf("%sReset cause: IWDG (Independent Watchdog) - possible lockup/thermal\r\n", first ? "" : " "); - first = 0; - } -#endif -#if defined(RCC_FLAG_WWDG1RST) - if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDG1RST)) { - printf("%sReset cause: WWDG (Window Watchdog)\r\n", first ? "" : " "); - first = 0; - } -#endif -#if defined(RCC_FLAG_LPWR1RST) - if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWR1RST)) { - printf("%sReset cause: LPWR (Low-power)\r\n", first ? "" : " "); - first = 0; - } -#endif - if (first) { - printf("Reset cause: unknown\r\n"); - } - __HAL_RCC_CLEAR_RESET_FLAGS(); -} - -/* Poll MCU junction temperature; print warning if above high threshold */ -#define MCU_TEMP_CHECK_MS 5000u -static void poll_mcu_temperature(void) -{ - static uint32_t last_check_ms = 0; - uint32_t now = HAL_GetTick(); - if ((last_check_ms != 0u) && ((now - last_check_ms) < MCU_TEMP_CHECK_MS)) { - return; - } - last_check_ms = now; - uint32_t level = HAL_PWREx_GetTemperatureLevel(); - if (level == PWR_TEMP_ABOVE_HIGH_THRESHOLD) { - printf("[THERMAL] MCU junction temp ABOVE HIGH THRESHOLD - reduce load/cooling!\r\n"); - } -} - -static void PrintI2CSpeed(I2C_HandleTypeDef *hi2c) -{ - // Assuming the timing is configured in I2C_TIMINGR register - uint32_t timing = hi2c->Init.Timing; - uint32_t presc = (timing >> 28) & 0xF; - uint32_t scll = (timing >> 0) & 0xFF; - uint32_t sclh = (timing >> 8) & 0xFF; - - // Get the I2C clock source frequency - uint32_t i2c_clk_freq = HAL_RCC_GetPCLK1Freq(); - - // Calculate SCL speed (frequency) in Hz - uint32_t scl_freq = i2c_clk_freq / ((presc + 1) * (scll + 1 + sclh + 1)); - - // Print I2C speed - printf("I2C Speed: %ld Hz\r\n", scl_freq); // Print the I2C speed in kHz -} - -/* USER CODE END 0 */ - -/** - * @brief The application entry point. - * @retval int - */ -int main(void) -{ - - /* USER CODE BEGIN 1 */ - - /* USER CODE END 1 */ - - /* MPU Configuration--------------------------------------------------------*/ - MPU_Config(); - - /* MCU Configuration--------------------------------------------------------*/ - - /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ - HAL_Init(); - - /* USER CODE BEGIN Init */ - - /* USER CODE END Init */ - - /* Configure the system clock */ - SystemClock_Config(); - - /* Configure the peripherals common clocks */ - PeriphCommonClock_Config(); - - /* USER CODE BEGIN SysInit */ - - /* USER CODE END SysInit */ - - /* Initialize all configured peripherals */ - MX_GPIO_Init(); - MX_DMA_Init(); - MX_BDMA_Init(); - MX_CRC_Init(); - MX_I2C1_Init(); - MX_RNG_Init(); - MX_SPI2_Init(); - MX_SPI3_Init(); - MX_SPI4_Init(); - MX_SPI6_Init(); - MX_TIM2_Init(); - MX_UART4_Init(); - MX_USART6_Init(); - MX_TIM8_Init(); - MX_TIM4_Init(); - MX_USART1_Init(); - MX_USART2_Init(); - MX_USART3_Init(); - MX_TIM14_Init(); - MX_TIM16_Init(); - MX_TIM5_Init(); - MX_TIM15_Init(); - /* USER CODE BEGIN 2 */ - - if (HAL_TIM_Base_Start(&htim5) != HAL_OK) - { - Error_Handler(); - } - - init_dma_logging(); - - DWT_Init(); - - // enable I2C MUX - HAL_GPIO_WritePin(MUX_RESET_GPIO_Port, MUX_RESET_Pin, GPIO_PIN_RESET); - - // enable USB PHY - HAL_GPIO_WritePin(USB_RESET_GPIO_Port, USB_RESET_Pin, GPIO_PIN_RESET); - - printf("\033c"); - fflush(stdout); - delay_ms(500); - printf("Openwater open-MOTION Aggregator\r\n\r\n"); - printf("FW: %s (%s)\r\nDate: %s\r\n", - FW_VERSION_STRING, - FW_SHA_STRING, - FW_BUILD_TIME_STRING); - printf("CPU Clock Frequency: %lu MHz\r\n", - HAL_RCC_GetSysClockFreq() / 1000000); - print_reset_cause(); - HAL_PWREx_EnableMonitoring(); /* Enable junction temp (TEMPH/TEMPL) monitoring */ - printf("Initializing, please wait ...\r\n"); - - // enable HS USB MUX - HAL_GPIO_WritePin(USB_MUX_GPIO_Port, USB_MUX_Pin, GPIO_PIN_RESET); - - // enable I2C MUX - HAL_GPIO_WritePin(MUX_RESET_GPIO_Port, MUX_RESET_Pin, GPIO_PIN_SET); - - HAL_GPIO_WritePin(FS_OUT_EN_GPIO_Port, FS_OUT_EN_Pin, GPIO_PIN_RESET); //enable Framesync output - - // test i2c - PrintI2CSpeed(&hi2c1); - // I2C_scan(&hi2c1, NULL, 0, true); - delay_ms(100); - X02C1B_FSIN_EXT_disable(); - GPIO_SetOutput(FSIN_EN_GPIO_Port, FSIN_EN_Pin, GPIO_PIN_RESET); // disable fsin output - - if (ICM_WHOAMI() == ICM20948_EXPECTED_ID) - { - if(ICM_Init() != HAL_OK){ - printf("Failed to initialize IMU\r\n\n"); - } - else - { - printf("IMU detected\r\n"); - delay_ms(100); - if(verbose_on) ICM_DumpRegisters(); - } - } - else - { - printf("IMU NOT detected\r\n\n"); - } - - delay_ms(10); - - HAL_GPIO_WritePin(USB_RESET_GPIO_Port, USB_RESET_Pin, GPIO_PIN_SET); - - HAL_GPIO_WritePin(ERROR_LED_GPIO_Port, ERROR_LED_Pin, GPIO_PIN_SET); - - init_camera_sensors(); // init structures and camera configs - - // Select default camera - TCA9548A_SelectChannel(&hi2c1, 0x70, get_active_cam()->i2c_target); - - delay_ms(250); - MX_USB_DEVICE_Init(); - delay_ms(1000); - //GPIO_SetHiZ(GPIOA, GPIO_PIN_2); - - comms_host_start(); - // fill_frame_buffers(); - printf("System Running\r\n"); - /* USER CODE END 2 */ - - /* Infinite loop */ - /* USER CODE BEGIN WHILE */ - while (1) - { - /* USER CODE END WHILE */ - - /* USER CODE BEGIN 3 */ - comms_host_check_received(); // check comms - check_streaming(); - poll_mcu_temperature(); /* Print warning if MCU junction temp above threshold */ - USB_RecoveryCheck(); /* EFT/lock-up watchdog: rebuild USB stack if bus goes dead */ - } - - /* USER CODE END 3 */ -} - -/** - * @brief System Clock Configuration - * @retval None - */ -void SystemClock_Config(void) -{ - RCC_OscInitTypeDef RCC_OscInitStruct = {0}; - RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; - - /** Supply configuration update enable - */ - HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY); - - /** Configure the main internal regulator output voltage - */ - __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0); - - while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {} - - /** Initializes the RCC Oscillators according to the specified parameters - * in the RCC_OscInitTypeDef structure. - */ - RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; - RCC_OscInitStruct.HSEState = RCC_HSE_ON; - RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; - RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; - RCC_OscInitStruct.PLL.PLLM = 3; - RCC_OscInitStruct.PLL.PLLN = 120; - RCC_OscInitStruct.PLL.PLLP = 2; - RCC_OscInitStruct.PLL.PLLQ = 20; - RCC_OscInitStruct.PLL.PLLR = 2; - RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3; - RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE; - RCC_OscInitStruct.PLL.PLLFRACN = 0; - if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) - { - Error_Handler(); - } - - /** Initializes the CPU, AHB and APB buses clocks - */ - RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK - |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2 - |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1; - RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; - RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1; - RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2; - RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; - RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; - RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; - RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; - - if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) - { - Error_Handler(); - } - __HAL_RCC_PLL2CLKOUT_ENABLE(RCC_PLL2_DIVP); - HAL_RCC_MCOConfig(RCC_MCO2, RCC_MCO2SOURCE_PLL2PCLK, RCC_MCODIV_5); -} - -/** - * @brief Peripherals Common Clock Configuration - * @retval None - */ -void PeriphCommonClock_Config(void) -{ - RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; - - /** Initializes the peripherals clock - */ - PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI3|RCC_PERIPHCLK_SPI2; - PeriphClkInitStruct.PLL2.PLL2M = 6; - PeriphClkInitStruct.PLL2.PLL2N = 120; - PeriphClkInitStruct.PLL2.PLL2P = 4; - PeriphClkInitStruct.PLL2.PLL2Q = 2; - PeriphClkInitStruct.PLL2.PLL2R = 2; - PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_2; - PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE; - PeriphClkInitStruct.PLL2.PLL2FRACN = 0; - PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL2; - if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) - { - Error_Handler(); - } -} - -/** - * @brief CRC Initialization Function - * @param None - * @retval None - */ -static void MX_CRC_Init(void) -{ - - /* USER CODE BEGIN CRC_Init 0 */ - - /* USER CODE END CRC_Init 0 */ - - /* USER CODE BEGIN CRC_Init 1 */ - - /* USER CODE END CRC_Init 1 */ - hcrc.Instance = CRC; - hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE; - hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE; - hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE; - hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE; - hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES; - if (HAL_CRC_Init(&hcrc) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN CRC_Init 2 */ - - /* USER CODE END CRC_Init 2 */ - -} - -/** - * @brief I2C1 Initialization Function - * @param None - * @retval None - */ -static void MX_I2C1_Init(void) -{ - - /* USER CODE BEGIN I2C1_Init 0 */ - - /* USER CODE END I2C1_Init 0 */ - - /* USER CODE BEGIN I2C1_Init 1 */ - - /* USER CODE END I2C1_Init 1 */ - hi2c1.Instance = I2C1; - hi2c1.Init.Timing = 0x00B03FDB; - hi2c1.Init.OwnAddress1 = 0; - hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; - hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; - hi2c1.Init.OwnAddress2 = 0; - hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK; - hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; - hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; - if (HAL_I2C_Init(&hi2c1) != HAL_OK) - { - Error_Handler(); - } - - /** Configure Analogue filter - */ - if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK) - { - Error_Handler(); - } - - /** Configure Digital filter - */ - if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN I2C1_Init 2 */ - - /* USER CODE END I2C1_Init 2 */ - -} - -/** - * @brief RNG Initialization Function - * @param None - * @retval None - */ -static void MX_RNG_Init(void) -{ - - /* USER CODE BEGIN RNG_Init 0 */ - - /* USER CODE END RNG_Init 0 */ - - /* USER CODE BEGIN RNG_Init 1 */ - - /* USER CODE END RNG_Init 1 */ - hrng.Instance = RNG; - hrng.Init.ClockErrorDetection = RNG_CED_ENABLE; - if (HAL_RNG_Init(&hrng) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN RNG_Init 2 */ - - /* USER CODE END RNG_Init 2 */ - -} - -/** - * @brief SPI2 Initialization Function - * @param None - * @retval None - */ -static void MX_SPI2_Init(void) -{ - - /* USER CODE BEGIN SPI2_Init 0 */ - - /* USER CODE END SPI2_Init 0 */ - - /* USER CODE BEGIN SPI2_Init 1 */ - - /* USER CODE END SPI2_Init 1 */ - /* SPI2 parameter configuration*/ - hspi2.Instance = SPI2; - hspi2.Init.Mode = SPI_MODE_SLAVE; - hspi2.Init.Direction = SPI_DIRECTION_2LINES; - hspi2.Init.DataSize = SPI_DATASIZE_8BIT; - hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH; - hspi2.Init.CLKPhase = SPI_PHASE_2EDGE; - hspi2.Init.NSS = SPI_NSS_SOFT; - hspi2.Init.FirstBit = SPI_FIRSTBIT_LSB; - hspi2.Init.TIMode = SPI_TIMODE_DISABLE; - hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; - hspi2.Init.CRCPolynomial = 0x0; - hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; - hspi2.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; - hspi2.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; - hspi2.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; - hspi2.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; - hspi2.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; - hspi2.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; - hspi2.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE; - hspi2.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE; - hspi2.Init.IOSwap = SPI_IO_SWAP_DISABLE; - if (HAL_SPI_Init(&hspi2) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN SPI2_Init 2 */ - - /* USER CODE END SPI2_Init 2 */ - -} - -/** - * @brief SPI3 Initialization Function - * @param None - * @retval None - */ -static void MX_SPI3_Init(void) -{ - - /* USER CODE BEGIN SPI3_Init 0 */ - - /* USER CODE END SPI3_Init 0 */ - - /* USER CODE BEGIN SPI3_Init 1 */ - - /* USER CODE END SPI3_Init 1 */ - /* SPI3 parameter configuration*/ - hspi3.Instance = SPI3; - hspi3.Init.Mode = SPI_MODE_SLAVE; - hspi3.Init.Direction = SPI_DIRECTION_2LINES; - hspi3.Init.DataSize = SPI_DATASIZE_8BIT; - hspi3.Init.CLKPolarity = SPI_POLARITY_HIGH; - hspi3.Init.CLKPhase = SPI_PHASE_2EDGE; - hspi3.Init.NSS = SPI_NSS_SOFT; - hspi3.Init.FirstBit = SPI_FIRSTBIT_LSB; - hspi3.Init.TIMode = SPI_TIMODE_DISABLE; - hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; - hspi3.Init.CRCPolynomial = 0x0; - hspi3.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; - hspi3.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; - hspi3.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; - hspi3.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; - hspi3.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; - hspi3.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; - hspi3.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; - hspi3.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE; - hspi3.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE; - hspi3.Init.IOSwap = SPI_IO_SWAP_DISABLE; - if (HAL_SPI_Init(&hspi3) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN SPI3_Init 2 */ - - /* USER CODE END SPI3_Init 2 */ - -} - -/** - * @brief SPI4 Initialization Function - * @param None - * @retval None - */ -static void MX_SPI4_Init(void) -{ - - /* USER CODE BEGIN SPI4_Init 0 */ - - /* USER CODE END SPI4_Init 0 */ - - /* USER CODE BEGIN SPI4_Init 1 */ - - /* USER CODE END SPI4_Init 1 */ - /* SPI4 parameter configuration*/ - hspi4.Instance = SPI4; - hspi4.Init.Mode = SPI_MODE_SLAVE; - hspi4.Init.Direction = SPI_DIRECTION_2LINES; - hspi4.Init.DataSize = SPI_DATASIZE_8BIT; - hspi4.Init.CLKPolarity = SPI_POLARITY_HIGH; - hspi4.Init.CLKPhase = SPI_PHASE_2EDGE; - hspi4.Init.NSS = SPI_NSS_SOFT; - hspi4.Init.FirstBit = SPI_FIRSTBIT_LSB; - hspi4.Init.TIMode = SPI_TIMODE_DISABLE; - hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; - hspi4.Init.CRCPolynomial = 0x0; - hspi4.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; - hspi4.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; - hspi4.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; - hspi4.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; - hspi4.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; - hspi4.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; - hspi4.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; - hspi4.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE; - hspi4.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE; - hspi4.Init.IOSwap = SPI_IO_SWAP_DISABLE; - if (HAL_SPI_Init(&hspi4) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN SPI4_Init 2 */ - - /* USER CODE END SPI4_Init 2 */ - -} - -/** - * @brief SPI6 Initialization Function - * @param None - * @retval None - */ -static void MX_SPI6_Init(void) -{ - - /* USER CODE BEGIN SPI6_Init 0 */ - - /* USER CODE END SPI6_Init 0 */ - - /* USER CODE BEGIN SPI6_Init 1 */ - - /* USER CODE END SPI6_Init 1 */ - /* SPI6 parameter configuration*/ - hspi6.Instance = SPI6; - hspi6.Init.Mode = SPI_MODE_SLAVE; - hspi6.Init.Direction = SPI_DIRECTION_2LINES; - hspi6.Init.DataSize = SPI_DATASIZE_8BIT; - hspi6.Init.CLKPolarity = SPI_POLARITY_HIGH; - hspi6.Init.CLKPhase = SPI_PHASE_2EDGE; - hspi6.Init.NSS = SPI_NSS_SOFT; - hspi6.Init.FirstBit = SPI_FIRSTBIT_LSB; - hspi6.Init.TIMode = SPI_TIMODE_DISABLE; - hspi6.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; - hspi6.Init.CRCPolynomial = 0x0; - hspi6.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; - hspi6.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; - hspi6.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; - hspi6.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; - hspi6.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; - hspi6.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; - hspi6.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; - hspi6.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE; - hspi6.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE; - hspi6.Init.IOSwap = SPI_IO_SWAP_DISABLE; - if (HAL_SPI_Init(&hspi6) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN SPI6_Init 2 */ - - /* USER CODE END SPI6_Init 2 */ - -} - -/** - * @brief TIM2 Initialization Function - * @param None - * @retval None - */ -static void MX_TIM2_Init(void) -{ - - /* USER CODE BEGIN TIM2_Init 0 */ - - /* USER CODE END TIM2_Init 0 */ - - TIM_ClockConfigTypeDef sClockSourceConfig = {0}; - TIM_MasterConfigTypeDef sMasterConfig = {0}; - TIM_OC_InitTypeDef sConfigOC = {0}; - - /* USER CODE BEGIN TIM2_Init 1 */ - - /* USER CODE END TIM2_Init 1 */ - htim2.Instance = TIM2; - htim2.Init.Prescaler = 0; - htim2.Init.CounterMode = TIM_COUNTERMODE_UP; - htim2.Init.Period = 4294967295; - htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - if (HAL_TIM_Base_Init(&htim2) != HAL_OK) - { - Error_Handler(); - } - sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; - if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) - { - Error_Handler(); - } - if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) - { - Error_Handler(); - } - sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; - sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; - if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) - { - Error_Handler(); - } - sConfigOC.OCMode = TIM_OCMODE_PWM1; - sConfigOC.Pulse = 0; - sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; - sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; - if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN TIM2_Init 2 */ - - /* USER CODE END TIM2_Init 2 */ - HAL_TIM_MspPostInit(&htim2); - -} - -/** - * @brief TIM4 Initialization Function - * @param None - * @retval None - */ -static void MX_TIM4_Init(void) -{ - - /* USER CODE BEGIN TIM4_Init 0 */ - - /* USER CODE END TIM4_Init 0 */ - - TIM_MasterConfigTypeDef sMasterConfig = {0}; - TIM_OC_InitTypeDef sConfigOC = {0}; - - /* USER CODE BEGIN TIM4_Init 1 */ - - /* USER CODE END TIM4_Init 1 */ - htim4.Instance = TIM4; - htim4.Init.Prescaler = 1000; - htim4.Init.CounterMode = TIM_COUNTERMODE_UP; - htim4.Init.Period = 5999; - htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - if (HAL_TIM_PWM_Init(&htim4) != HAL_OK) - { - Error_Handler(); - } - if (HAL_TIM_OC_Init(&htim4) != HAL_OK) - { - Error_Handler(); - } - sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; - sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; - if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK) - { - Error_Handler(); - } - sConfigOC.OCMode = TIM_OCMODE_PWM1; - sConfigOC.Pulse = 3000; - sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW; - sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; - if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) - { - Error_Handler(); - } - sConfigOC.OCMode = TIM_OCMODE_TIMING; - sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; - if (HAL_TIM_OC_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN TIM4_Init 2 */ - LL_TIM_EnableIT_CC1(TIM4); - /* USER CODE END TIM4_Init 2 */ - HAL_TIM_MspPostInit(&htim4); - -} - -/** - * @brief TIM5 Initialization Function - * @param None - * @retval None - */ -static void MX_TIM5_Init(void) -{ - - /* USER CODE BEGIN TIM5_Init 0 */ - - /* USER CODE END TIM5_Init 0 */ - - TIM_ClockConfigTypeDef sClockSourceConfig = {0}; - TIM_MasterConfigTypeDef sMasterConfig = {0}; - - /* USER CODE BEGIN TIM5_Init 1 */ - - /* USER CODE END TIM5_Init 1 */ - htim5.Instance = TIM5; - htim5.Init.Prescaler = 2400-1; - htim5.Init.CounterMode = TIM_COUNTERMODE_UP; - htim5.Init.Period = 0xFFFFFFFF; - htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - if (HAL_TIM_Base_Init(&htim5) != HAL_OK) - { - Error_Handler(); - } - sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; - if (HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig) != HAL_OK) - { - Error_Handler(); - } - sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; - sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; - if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN TIM5_Init 2 */ - - /* USER CODE END TIM5_Init 2 */ - -} - -/** - * @brief TIM8 Initialization Function - * @param None - * @retval None - */ -static void MX_TIM8_Init(void) -{ - - /* USER CODE BEGIN TIM8_Init 0 */ - - /* USER CODE END TIM8_Init 0 */ - - TIM_ClockConfigTypeDef sClockSourceConfig = {0}; - TIM_MasterConfigTypeDef sMasterConfig = {0}; - - /* USER CODE BEGIN TIM8_Init 1 */ - - /* USER CODE END TIM8_Init 1 */ - htim8.Instance = TIM8; - htim8.Init.Prescaler = 0; - htim8.Init.CounterMode = TIM_COUNTERMODE_UP; - htim8.Init.Period = 10-1; - htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - htim8.Init.RepetitionCounter = 0; - htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - if (HAL_TIM_Base_Init(&htim8) != HAL_OK) - { - Error_Handler(); - } - sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; - if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK) - { - Error_Handler(); - } - sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; - sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET; - sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; - if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN TIM8_Init 2 */ - - /* USER CODE END TIM8_Init 2 */ - -} - -/** - * @brief TIM14 Initialization Function - * @param None - * @retval None - */ -static void MX_TIM14_Init(void) -{ - - /* USER CODE BEGIN TIM14_Init 0 */ - - /* USER CODE END TIM14_Init 0 */ - - /* USER CODE BEGIN TIM14_Init 1 */ - - /* USER CODE END TIM14_Init 1 */ - htim14.Instance = TIM14; - htim14.Init.Prescaler = 240-1; - htim14.Init.CounterMode = TIM_COUNTERMODE_UP; - htim14.Init.Period = 5000-1; - htim14.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - htim14.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - if (HAL_TIM_Base_Init(&htim14) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN TIM14_Init 2 */ - - /* USER CODE END TIM14_Init 2 */ - -} - -/** - * @brief TIM15 Initialization Function - * @param None - * @retval None - */ -static void MX_TIM15_Init(void) -{ - - /* USER CODE BEGIN TIM15_Init 0 */ - - /* USER CODE END TIM15_Init 0 */ - - TIM_ClockConfigTypeDef sClockSourceConfig = {0}; - TIM_MasterConfigTypeDef sMasterConfig = {0}; - - /* USER CODE BEGIN TIM15_Init 1 */ - - /* USER CODE END TIM15_Init 1 */ - htim15.Instance = TIM15; - htim15.Init.Prescaler = 240-1; - htim15.Init.CounterMode = TIM_COUNTERMODE_UP; - htim15.Init.Period = 65535; - htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - htim15.Init.RepetitionCounter = 0; - htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - if (HAL_TIM_Base_Init(&htim15) != HAL_OK) - { - Error_Handler(); - } - sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; - if (HAL_TIM_ConfigClockSource(&htim15, &sClockSourceConfig) != HAL_OK) - { - Error_Handler(); - } - sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; - sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; - if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &sMasterConfig) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN TIM15_Init 2 */ - - /* USER CODE END TIM15_Init 2 */ - -} - -/** - * @brief TIM16 Initialization Function - * @param None - * @retval None - */ -static void MX_TIM16_Init(void) -{ - - /* USER CODE BEGIN TIM16_Init 0 */ - - /* USER CODE END TIM16_Init 0 */ - - /* USER CODE BEGIN TIM16_Init 1 */ - - /* USER CODE END TIM16_Init 1 */ - htim16.Instance = TIM16; - htim16.Init.Prescaler = 240-1; - htim16.Init.CounterMode = TIM_COUNTERMODE_UP; - htim16.Init.Period = 25000-1; - htim16.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - htim16.Init.RepetitionCounter = 0; - htim16.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - if (HAL_TIM_Base_Init(&htim16) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN TIM16_Init 2 */ - - /* USER CODE END TIM16_Init 2 */ - -} - -/** - * @brief UART4 Initialization Function - * @param None - * @retval None - */ -static void MX_UART4_Init(void) -{ - - /* USER CODE BEGIN UART4_Init 0 */ - - /* USER CODE END UART4_Init 0 */ - - /* USER CODE BEGIN UART4_Init 1 */ - - /* USER CODE END UART4_Init 1 */ - huart4.Instance = UART4; - huart4.Init.BaudRate = 115200; - huart4.Init.WordLength = UART_WORDLENGTH_8B; - huart4.Init.StopBits = UART_STOPBITS_1; - huart4.Init.Parity = UART_PARITY_NONE; - huart4.Init.Mode = UART_MODE_TX_RX; - huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE; - huart4.Init.OverSampling = UART_OVERSAMPLING_16; - huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; - huart4.Init.ClockPrescaler = UART_PRESCALER_DIV1; - huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; - if (HAL_UART_Init(&huart4) != HAL_OK) - { - Error_Handler(); - } - if (HAL_UARTEx_SetTxFifoThreshold(&huart4, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK) - { - Error_Handler(); - } - if (HAL_UARTEx_SetRxFifoThreshold(&huart4, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK) - { - Error_Handler(); - } - if (HAL_UARTEx_DisableFifoMode(&huart4) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN UART4_Init 2 */ - - /* USER CODE END UART4_Init 2 */ - -} - -/** - * @brief USART1 Initialization Function - * @param None - * @retval None - */ -static void MX_USART1_Init(void) -{ - - /* USER CODE BEGIN USART1_Init 0 */ - - /* USER CODE END USART1_Init 0 */ - - /* USER CODE BEGIN USART1_Init 1 */ - - /* USER CODE END USART1_Init 1 */ - husart1.Instance = USART1; - husart1.Init.BaudRate = 115200; - husart1.Init.WordLength = USART_WORDLENGTH_8B; - husart1.Init.StopBits = USART_STOPBITS_1; - husart1.Init.Parity = USART_PARITY_NONE; - husart1.Init.Mode = USART_MODE_RX; - husart1.Init.CLKPolarity = USART_POLARITY_HIGH; - husart1.Init.CLKPhase = USART_PHASE_2EDGE; - husart1.Init.CLKLastBit = USART_LASTBIT_DISABLE; - husart1.Init.ClockPrescaler = USART_PRESCALER_DIV1; - husart1.SlaveMode = USART_SLAVEMODE_ENABLE; - if (HAL_USART_Init(&husart1) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_SetTxFifoThreshold(&husart1, USART_TXFIFO_THRESHOLD_1_8) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_SetRxFifoThreshold(&husart1, USART_RXFIFO_THRESHOLD_8_8) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_EnableFifoMode(&husart1) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_EnableSlaveMode(&husart1) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN USART1_Init 2 */ - - /* USER CODE END USART1_Init 2 */ - -} - -/** - * @brief USART2 Initialization Function - * @param None - * @retval None - */ -static void MX_USART2_Init(void) -{ - - /* USER CODE BEGIN USART2_Init 0 */ - - /* USER CODE END USART2_Init 0 */ - - /* USER CODE BEGIN USART2_Init 1 */ - - /* USER CODE END USART2_Init 1 */ - husart2.Instance = USART2; - husart2.Init.BaudRate = 5000000; - husart2.Init.WordLength = USART_WORDLENGTH_8B; - husart2.Init.StopBits = USART_STOPBITS_1; - husart2.Init.Parity = USART_PARITY_NONE; - husart2.Init.Mode = USART_MODE_RX; - husart2.Init.CLKPolarity = USART_POLARITY_HIGH; - husart2.Init.CLKPhase = USART_PHASE_2EDGE; - husart2.Init.CLKLastBit = USART_LASTBIT_DISABLE; - husart2.Init.ClockPrescaler = USART_PRESCALER_DIV1; - husart2.SlaveMode = USART_SLAVEMODE_ENABLE; - if (HAL_USART_Init(&husart2) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_SetTxFifoThreshold(&husart2, USART_TXFIFO_THRESHOLD_1_8) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_SetRxFifoThreshold(&husart2, USART_RXFIFO_THRESHOLD_8_8) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_EnableFifoMode(&husart2) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_EnableSlaveMode(&husart2) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN USART2_Init 2 */ - - /* USER CODE END USART2_Init 2 */ - -} - -/** - * @brief USART3 Initialization Function - * @param None - * @retval None - */ -static void MX_USART3_Init(void) -{ - - /* USER CODE BEGIN USART3_Init 0 */ - - /* USER CODE END USART3_Init 0 */ - - /* USER CODE BEGIN USART3_Init 1 */ - - /* USER CODE END USART3_Init 1 */ - husart3.Instance = USART3; - husart3.Init.BaudRate = 115200; - husart3.Init.WordLength = USART_WORDLENGTH_8B; - husart3.Init.StopBits = USART_STOPBITS_1; - husart3.Init.Parity = USART_PARITY_NONE; - husart3.Init.Mode = USART_MODE_RX; - husart3.Init.CLKPolarity = USART_POLARITY_HIGH; - husart3.Init.CLKPhase = USART_PHASE_2EDGE; - husart3.Init.CLKLastBit = USART_LASTBIT_DISABLE; - husart3.Init.ClockPrescaler = USART_PRESCALER_DIV1; - husart3.SlaveMode = USART_SLAVEMODE_ENABLE; - if (HAL_USART_Init(&husart3) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_SetTxFifoThreshold(&husart3, USART_TXFIFO_THRESHOLD_1_8) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_SetRxFifoThreshold(&husart3, USART_RXFIFO_THRESHOLD_8_8) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_EnableFifoMode(&husart3) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_EnableSlaveMode(&husart3) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN USART3_Init 2 */ - - /* USER CODE END USART3_Init 2 */ - -} - -/** - * @brief USART6 Initialization Function - * @param None - * @retval None - */ -static void MX_USART6_Init(void) -{ - - /* USER CODE BEGIN USART6_Init 0 */ - - /* USER CODE END USART6_Init 0 */ - - /* USER CODE BEGIN USART6_Init 1 */ - - /* USER CODE END USART6_Init 1 */ - husart6.Instance = USART6; - husart6.Init.BaudRate = 4167000; - husart6.Init.WordLength = USART_WORDLENGTH_8B; - husart6.Init.StopBits = USART_STOPBITS_1; - husart6.Init.Parity = USART_PARITY_NONE; - husart6.Init.Mode = USART_MODE_RX; - husart6.Init.CLKPolarity = USART_POLARITY_HIGH; - husart6.Init.CLKPhase = USART_PHASE_2EDGE; - husart6.Init.CLKLastBit = USART_LASTBIT_DISABLE; - husart6.Init.ClockPrescaler = USART_PRESCALER_DIV1; - husart6.SlaveMode = USART_SLAVEMODE_ENABLE; - if (HAL_USART_Init(&husart6) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_SetTxFifoThreshold(&husart6, USART_TXFIFO_THRESHOLD_1_8) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_SetRxFifoThreshold(&husart6, USART_RXFIFO_THRESHOLD_8_8) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_EnableFifoMode(&husart6) != HAL_OK) - { - Error_Handler(); - } - if (HAL_USARTEx_EnableSlaveMode(&husart6) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN USART6_Init 2 */ - - /* USER CODE END USART6_Init 2 */ - -} - -/** - * Enable DMA controller clock - */ -static void MX_BDMA_Init(void) -{ - - /* DMA controller clock enable */ - __HAL_RCC_BDMA_CLK_ENABLE(); - - /* DMA interrupt init */ - /* BDMA_Channel0_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(BDMA_Channel0_IRQn, DMA_IRQ_PRIORITY, 0); - HAL_NVIC_EnableIRQ(BDMA_Channel0_IRQn); - -} - -/** - * Enable DMA controller clock - * Configure DMA for memory to memory transfers - * hdma_memtomem_dma2_stream1 - */ -static void MX_DMA_Init(void) -{ - - /* DMA controller clock enable */ - __HAL_RCC_DMA1_CLK_ENABLE(); - __HAL_RCC_DMA2_CLK_ENABLE(); - - /* Configure DMA request hdma_memtomem_dma2_stream1 on DMA2_Stream1 */ - hdma_memtomem_dma2_stream1.Instance = DMA2_Stream1; - hdma_memtomem_dma2_stream1.Init.Request = DMA_REQUEST_MEM2MEM; - hdma_memtomem_dma2_stream1.Init.Direction = DMA_MEMORY_TO_MEMORY; - hdma_memtomem_dma2_stream1.Init.PeriphInc = DMA_PINC_DISABLE; - hdma_memtomem_dma2_stream1.Init.MemInc = DMA_MINC_ENABLE; - hdma_memtomem_dma2_stream1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; - hdma_memtomem_dma2_stream1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; - hdma_memtomem_dma2_stream1.Init.Mode = DMA_NORMAL; - hdma_memtomem_dma2_stream1.Init.Priority = DMA_PRIORITY_MEDIUM; - hdma_memtomem_dma2_stream1.Init.FIFOMode = DMA_FIFOMODE_ENABLE; - hdma_memtomem_dma2_stream1.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; - hdma_memtomem_dma2_stream1.Init.MemBurst = DMA_MBURST_INC4; - hdma_memtomem_dma2_stream1.Init.PeriphBurst = DMA_PBURST_INC4; - if (HAL_DMA_Init(&hdma_memtomem_dma2_stream1) != HAL_OK) - { - Error_Handler( ); - } - - /* DMA interrupt init */ - /* DMA1_Stream0_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn); - /* DMA1_Stream1_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn); - /* DMA1_Stream2_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn); - /* DMA1_Stream3_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn); - /* DMA1_Stream4_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn); - /* DMA1_Stream5_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn); - /* DMA1_Stream6_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn); - /* DMA1_Stream7_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn); - /* DMA2_Stream0_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn); - -} - -/** - * @brief GPIO Initialization Function - * @param None - * @retval None - */ -static void MX_GPIO_Init(void) -{ - GPIO_InitTypeDef GPIO_InitStruct = {0}; - /* USER CODE BEGIN MX_GPIO_Init_1 */ - /* USER CODE END MX_GPIO_Init_1 */ - - /* GPIO Ports Clock Enable */ - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOE_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOD_CLK_ENABLE(); - __HAL_RCC_GPIOH_CLK_ENABLE(); - - /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(GPIOC, ERROR_LED_Pin|MUX_RESET_Pin|USB_MUX_Pin, GPIO_PIN_RESET); - - /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(USB_RESET_GPIO_Port, USB_RESET_Pin, GPIO_PIN_RESET); - - /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(GPIOD, CAM_PWR_1_Pin|CAM_PWR_5_Pin|CAM_PWR_8_Pin|CAM_PWR_4_Pin - |FAN_CTL_Pin|FS_OUT_EN_Pin, GPIO_PIN_SET); - - /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(GPIOE, CAM_PWR_7_Pin|CAM_PWR_2_Pin|FSIN_EN_Pin, GPIO_PIN_SET); - - /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(CAM_PWR_3_GPIO_Port, CAM_PWR_3_Pin, GPIO_PIN_SET); - - /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(CAM_PWR_6_GPIO_Port, CAM_PWR_6_Pin, GPIO_PIN_SET); - - /*Configure GPIO pins : ERROR_LED_Pin MUX_RESET_Pin USB_MUX_Pin CAM_PWR_6_Pin */ - GPIO_InitStruct.Pin = ERROR_LED_Pin|MUX_RESET_Pin|USB_MUX_Pin|CAM_PWR_6_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - /*Configure GPIO pins : GPIO0_8_Pin IMU_INT_Pin */ - GPIO_InitStruct.Pin = GPIO0_8_Pin|IMU_INT_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - /*Configure GPIO pins : GPIO0_7_Pin GPIO0_5_Pin */ - GPIO_InitStruct.Pin = GPIO0_7_Pin|GPIO0_5_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - - /*Configure GPIO pins : USB_RESET_Pin CAM_PWR_3_Pin */ - GPIO_InitStruct.Pin = USB_RESET_Pin|CAM_PWR_3_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - - /*Configure GPIO pins : GPIO0_6_Pin CRESET_8_Pin CRESET_7_Pin PE14 - GPIO0_2_Pin GPIO0_3_Pin CRESET_1_Pin CRESET_3_Pin - CRESET_2_Pin CRESET_4_Pin */ - GPIO_InitStruct.Pin = GPIO0_6_Pin|CRESET_8_Pin|CRESET_7_Pin|GPIO_PIN_14 - |GPIO0_2_Pin|GPIO0_3_Pin|CRESET_1_Pin|CRESET_3_Pin - |CRESET_2_Pin|CRESET_4_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); - - /*Configure GPIO pins : CRESET_6_Pin CRESET_5_Pin GPIO0_4_Pin */ - GPIO_InitStruct.Pin = CRESET_6_Pin|CRESET_5_Pin|GPIO0_4_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); - - /*Configure GPIO pins : CAM_PWR_1_Pin CAM_PWR_5_Pin CAM_PWR_8_Pin CAM_PWR_4_Pin - FAN_CTL_Pin FS_OUT_EN_Pin */ - GPIO_InitStruct.Pin = CAM_PWR_1_Pin|CAM_PWR_5_Pin|CAM_PWR_8_Pin|CAM_PWR_4_Pin - |FAN_CTL_Pin|FS_OUT_EN_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); - - /*Configure GPIO pins : PA12 PA11 */ - GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_11; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF10_OTG1_FS; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - - /*Configure GPIO pins : CAM_PWR_7_Pin CAM_PWR_2_Pin FSIN_EN_Pin */ - GPIO_InitStruct.Pin = CAM_PWR_7_Pin|CAM_PWR_2_Pin|FSIN_EN_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); - - /*Configure GPIO pin : PC9 */ - GPIO_InitStruct.Pin = GPIO_PIN_9; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF0_MCO; - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - /*Configure GPIO pin : GPIO0_1_Pin */ - GPIO_InitStruct.Pin = GPIO0_1_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIO0_1_GPIO_Port, &GPIO_InitStruct); - - /* USER CODE BEGIN MX_GPIO_Init_2 */ - /* USER CODE END MX_GPIO_Init_2 */ -} - -/* USER CODE BEGIN 4 */ - -void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) -{ -} - -void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) -{ - if (huart->Instance == UART4) - { - logging_UART_TxCpltCallback(huart); - } -} - -// Error handling callback for USART -void HAL_USART_ErrorCallback(USART_HandleTypeDef *husart) -{ - int8_t cam_id = -1; - - if (husart->Instance == USART1) - { - cam_id = 4; - } - else if (husart->Instance == USART2) - { - cam_id = 0; - } - else if (husart->Instance == USART3) - { - cam_id = 2; - } - else if (husart->Instance == USART6) - { - cam_id = 3; - } - - // Print which USART instance caused the error - if (husart->Instance == USART1) - { - printf("Error in USART1: "); - } - else if (husart->Instance == USART2) - { - printf("Error in USART2: "); - } - else if (husart->Instance == USART3) - { - printf("Error in USART3: "); - } - else if (husart->Instance == USART6) - { - printf("Error in USART6: "); - } - else - { - printf("Error in Unknown USART instance: "); - } - - // Identify specific errors using error codes - if (husart->ErrorCode & HAL_USART_ERROR_PE) - { - printf("Parity error "); - } - if (husart->ErrorCode & HAL_USART_ERROR_NE) - { - printf("Noise error "); - } - if (husart->ErrorCode & HAL_USART_ERROR_FE) - { - printf("Framing error "); - } - if (husart->ErrorCode & HAL_USART_ERROR_ORE) - { - printf("Overrun error "); - __HAL_USART_CLEAR_OREFLAG(husart); - } - if (husart->ErrorCode & HAL_USART_ERROR_DMA) - { - printf("DMA transfer error "); - } - printf("\r\n"); - - /* Attempt graceful recovery for any known camera USART peripheral. - * Same fix as HAL_SPI_ErrorCallback: non-overrun errors (framing, noise, - * DMA) previously fell through to Error_Handler() and halted the MCU. - * Any error on a known camera USART is recoverable via abort+restart. */ - if (cam_id >= 0) - { - abort_data_reception((uint8_t)cam_id); - start_data_reception((uint8_t)cam_id); - return; - } - - /* Truly unknown USART peripheral — log and continue rather than halting. */ - printf("[USART] Unhandled USART error on unknown peripheral, continuing.\r\n"); -} - -// Error handling callback for SPI -void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) -{ - int8_t cam_id = -1; - - if (hspi->Instance == SPI2) - { - cam_id = 6; - } - else if (hspi->Instance == SPI3) - { - cam_id = 5; - } - else if (hspi->Instance == SPI4) - { - cam_id = 7; - } - else if (hspi->Instance == SPI6) - { - cam_id = 1; - } - - // Print which SPI instance caused the error - if (hspi->Instance == SPI2) - { - printf("Error in SPI2: "); - } - else if (hspi->Instance == SPI3) - { - printf("Error in SPI3: "); - } - else if (hspi->Instance == SPI4) - { - printf("Error in SPI4: "); - } - else if (hspi->Instance == SPI6) - { - printf("Error in SPI6: "); - } - else - { - printf("Error in Unknown SPI instance: "); - } - - // Identify specific errors using error codes - if (hspi->ErrorCode & HAL_SPI_ERROR_OVR) - { - printf("Overrun error "); - __HAL_SPI_CLEAR_OVRFLAG(hspi); - } - if (hspi->ErrorCode & HAL_SPI_ERROR_MODF) - { - printf("Mode fault error "); - } - if (hspi->ErrorCode & HAL_SPI_ERROR_CRC) - { - printf("CRC error "); - } - if (hspi->ErrorCode & HAL_SPI_ERROR_FRE) - { - printf("Frame error "); - } - if (hspi->ErrorCode & HAL_SPI_ERROR_DMA) - { - if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_TE) - printf("TE - HAL_DMA_ERROR_TE"); - if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_FE) - printf("HAL_DMA_ERROR_FE "); - if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_DME) - printf("HAL_DMA_ERROR_DME"); - if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_TIMEOUT) - printf("HAL_DMA_ERROR_TIMEOUT"); - if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_PARAM) - printf("HAL_DMA_ERROR_PARAM"); - if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_NO_XFER) - printf("HAL_DMA_ERROR_NO_XFER"); - if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_NOT_SUPPORTED) - printf("HAL_DMA_ERROR_NOT_SUPPORTED"); - if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_SYNC) - printf("HAL_DMA_ERROR_SYNC"); - if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_REQGEN) - printf("HAL_DMA_ERROR_REQGEN"); - if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_BUSY) - printf("HAL_DMA_ERROR_BUSY"); - } - printf("\r\n"); - - /* Attempt graceful recovery for any known camera SPI peripheral. - * Previously only overrun errors were recovered here; all other error types - * fell through to Error_Handler() which calls __disable_irq() + while(1), - * killing USB and all other peripherals. The observed failure mode was: - * Camera 6 overheats → SPI3 DMA/frame error (not OVR) → - * Error_Handler() entered from interrupt context → - * wait_for_usb_queues_to_finish() blocks (USB ISR can't run) → - * __disable_irq() → MCU completely dark. - * Now any error on a known camera SPI triggers abort+restart instead. */ - if (cam_id >= 0) - { - abort_data_reception((uint8_t)cam_id); - start_data_reception((uint8_t)cam_id); - return; - } - - /* Truly unknown SPI peripheral — log and continue rather than halting. */ - printf("[SPI] Unhandled SPI error on unknown peripheral, continuing.\r\n"); -} - - -void set_event_bit_atomic(uint32_t bit) { - __disable_irq(); - event_bits |= bit; - __enable_irq(); -} - -// Interrupt handler for SPI reception -void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) -{ - if (hspi->Instance == SPI2) - { - set_event_bit_atomic(BIT_6); - } - else if (hspi->Instance == SPI3) - { - set_event_bit_atomic(BIT_5); - } - else if (hspi->Instance == SPI4) - { - set_event_bit_atomic(BIT_7); - } - else if (hspi->Instance == SPI6) - { - set_event_bit_atomic(BIT_1); - } -} - -void HAL_USART_RxCpltCallback(USART_HandleTypeDef *husart) -{ - if (husart->Instance == USART1) - { // Check if the interrupt is for USART2 - set_event_bit_atomic(BIT_4); - } - else if (husart->Instance == USART2) - { // Check if the interrupt is for USART2 - set_event_bit_atomic(BIT_0); - } - else if (husart->Instance == USART3) - { // Check if the interrupt is for USART2 - set_event_bit_atomic(BIT_2); - } - else if (husart->Instance == USART6) - { // Check if the interrupt is for USART2 - set_event_bit_atomic(BIT_3); - } - -} - -void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) -{ - if (htim->Instance == TIM4) // Call data sender (internal FSIN)) - { - send_data(); - } -} - -void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) -{ - if (GPIO_Pin == GPIO_PIN_13) // Call REAL data sender if interrupt hit and enabled - { - pulse_count++; - send_data(); - } -} - -void ExitRun0Mode(void) { - // Temporary stub for ExitRun0Mode -} - -/** - * @brief Wait for all USB data queues to finish sending - * @note This function polls USB endpoint status flags until all transmissions complete - * or a timeout occurs. This ensures error messages and diagnostic data are - * transmitted before the system halts. - */ -static void wait_for_usb_queues_to_finish(void) -{ - const uint32_t timeout_ms = 1000; // Maximum wait time: 1 second - uint32_t start_time = get_timestamp_ms(); - uint32_t elapsed = 0; - - printf("Waiting for USB queues to finish...\r\n"); - fflush(stdout); - - // Poll until all endpoints are idle or timeout - while (elapsed < timeout_ms) { - _Bool all_idle = true; - - // Check COMMS endpoint (tx_flag: 1 = idle, 0 = busy) - if (tx_flag == 0) { - all_idle = false; - } - - // Check COMMS bulk endpoint (comms_ep_data: 0 = idle, 1 = transmitting) - if (comms_ep_data != 0) { - all_idle = false; - } - - // Check HISTO endpoint (histo_ep_data: 0 = idle, 1 = transmitting) - if (histo_ep_data != 0) { - all_idle = false; - } - - // Check IMU endpoint (imu_ep_data: 0 = idle, 1 = transmitting) - if (imu_ep_data != 0) { - all_idle = false; - } - - if (all_idle) { - printf("All USB queues finished.\r\n"); - fflush(stdout); - return; - } - - // Small delay to avoid busy-waiting - delay_ms(1); - elapsed = get_timestamp_ms() - start_time; - } - - printf("USB queue wait timeout after %lu ms (some data may not have been sent).\r\n", elapsed); - fflush(stdout); -} -/* USER CODE END 4 */ - - /* MPU Configuration */ - -void MPU_Config(void) -{ - MPU_Region_InitTypeDef MPU_InitStruct = {0}; - - /* Disables the MPU */ - HAL_MPU_Disable(); - - /** Initializes and configures the Region and the memory to be protected - */ - MPU_InitStruct.Enable = MPU_REGION_ENABLE; - MPU_InitStruct.Number = MPU_REGION_NUMBER0; - MPU_InitStruct.BaseAddress = 0x0; - MPU_InitStruct.Size = MPU_REGION_SIZE_4GB; - MPU_InitStruct.SubRegionDisable = 0x87; - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; - MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS; - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; - MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; - MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; - MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; - - HAL_MPU_ConfigRegion(&MPU_InitStruct); - /* Enables the MPU */ - HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); - -} - -/** - * @brief Period elapsed callback in non blocking mode - * @note This function is called when TIM17 interrupt took place, inside - * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment - * a global variable "uwTick" used as application time base. - * @param htim : TIM handle - * @retval None - */ -void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) -{ - /* USER CODE BEGIN Callback 0 */ - - /* USER CODE END Callback 0 */ - if (htim->Instance == TIM17) - { - HAL_IncTick(); - } - /* USER CODE BEGIN Callback 1 */ - if (htim->Instance == TIM16){ - HistoFake_GenerateAndSend(&hUsbDeviceHS); - } - - if (htim->Instance == TIM14) - { - imu_frame_counter++; - // call imu - if(ICM_GetAllRawData(&a,&t, &g, &m) != HAL_OK){ - printf("IMU Read Error\r\n"); - }else{ - memset(usb_buf,0,128); - int len = snprintf( - usb_buf, sizeof(usb_buf), - "{\"F\":%ld,\"G\":[%d,%d,%d],\"M\":[%d,%d,%d],\"A\":[%d,%d,%d],\"T\":%d.%02d}\r\n", - imu_frame_counter, - g.x, g.y, g.z, - m.x, m.y, m.z, - a.x, a.y, a.z, - (int)t, (int)((t - (int)t) * 100.0f) - ); - USBD_IMU_SetTxBuffer(&hUsbDeviceHS, (uint8_t *)usb_buf, len); - } - } - - if (htim->Instance == TIM15) { - HAL_TIM_Base_Stop_IT(htim); - if(_enter_dfu) { - *((uint32_t *)0x38000000) = 0xDEADBEEF; - } - - - // De-initialize other specific modules - HAL_RNG_DeInit(&hrng); - HAL_CRC_DeInit(&hcrc); - - MX_USB_DEVICE_DeInit(); - delay_ms(300); - // Reset the board - NVIC_SystemReset(); - - } - - /* USER CODE END Callback 1 */ -} - -/** - * @brief This function is executed in case of error occurrence. - * @retval None - */ - -void Error_Handler(void) -{ - /* USER CODE BEGIN Error_Handler_Debug */ - /* User can add his own implementation to report the HAL error return state */ - - uint32_t *stack_ptr; - - HAL_GPIO_TogglePin(ERROR_LED_GPIO_Port, ERROR_LED_Pin); - printf(">>> HARD FAULT <<<\r\n"); - fflush(stdout); // Ensure the output is flushed immediately - - // Get the current stack pointer - __ASM volatile("MRS %0, MSP" : "=r"(stack_ptr)); - - // Print general-purpose registers - printf("Stack Pointer (MSP): 0x%08lX\r\n", (unsigned long)stack_ptr); - printf("Register dump:\r\n"); - printf("R0 : 0x%08lX\r\n", stack_ptr[0]); - printf("R1 : 0x%08lX\r\n", stack_ptr[1]); - printf("R2 : 0x%08lX\r\n", stack_ptr[2]); - printf("R3 : 0x%08lX\r\n", stack_ptr[3]); - printf("R12: 0x%08lX\r\n", stack_ptr[4]); - printf("LR : 0x%08lX (Link Register)\r\n", stack_ptr[5]); - printf("PC : 0x%08lX (Program Counter)\r\n", stack_ptr[6]); - printf("xPSR: 0x%08lX\r\n", stack_ptr[7]); - fflush(stdout); - - // Wait for all USB data queues to finish sending before halting - wait_for_usb_queues_to_finish(); - - delay_ms(100); - __disable_irq(); - while (1) - { - } - /* USER CODE END Error_Handler_Debug */ -} -#ifdef USE_FULL_ASSERT -/** - * @brief Reports the name of the source file and the source line number - * where the assert_param error has occurred. - * @param file: pointer to the source file name - * @param line: assert_param error line source number - * @retval None - */ -void assert_failed(uint8_t *file, uint32_t line) -{ - /* USER CODE BEGIN 6 */ - /* User can add his own implementation to report the file name and line number, - ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ - /* USER CODE END 6 */ -} -#endif /* USE_FULL_ASSERT */ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : main.c + * @brief : Main program body + ****************************************************************************** + * @attention + * + * Copyright (c) 2024 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ +/* Includes ------------------------------------------------------------------*/ +#include "main.h" + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +#include "0X02C1B.h" +#include "usb_device.h" +#include "uart_comms.h" +#include "usbd_histo.h" +#include "usbd_imu.h" +#include "usbd_comms.h" +#include "logging.h" +#include "utils.h" +#include "i2c_master.h" +#include "histo_fake.h" +#include "ICM20948.h" +#include "camera_manager.h" +#include "system_monitor.h" + +#include +#include +#include + +#include "stm32h7xx_ll_tim.h" + +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN PTD */ + +/* External references to USB endpoint status variables */ +extern volatile uint8_t tx_flag; // From uart_comms.c +extern volatile uint8_t comms_ep_data; // From usbd_comms.c (__IO is volatile) +extern volatile uint8_t histo_ep_data; // From usbd_histo.c (__IO is volatile) +extern volatile uint8_t imu_ep_data; // From usbd_imu.c (__IO is volatile) + +/* USER CODE END PTD */ + +/* Private define ------------------------------------------------------------*/ +/* USER CODE BEGIN PD */ +/* USER CODE END PD */ + +/* Private macro -------------------------------------------------------------*/ +/* USER CODE BEGIN PM */ + +/* USER CODE END PM */ + +/* Private variables ---------------------------------------------------------*/ + +CRC_HandleTypeDef hcrc; + +I2C_HandleTypeDef hi2c1; + +IWDG_HandleTypeDef hiwdg1; + +RAMECC_HandleTypeDef hramecc1_m1; +RAMECC_HandleTypeDef hramecc1_m2; +RAMECC_HandleTypeDef hramecc1_m3; +RAMECC_HandleTypeDef hramecc1_m4; +RAMECC_HandleTypeDef hramecc1_m5; +RAMECC_HandleTypeDef hramecc2_m1; +RAMECC_HandleTypeDef hramecc2_m2; +RAMECC_HandleTypeDef hramecc2_m3; +RAMECC_HandleTypeDef hramecc2_m4; +RAMECC_HandleTypeDef hramecc2_m5; +RAMECC_HandleTypeDef hramecc3_m1; +RAMECC_HandleTypeDef hramecc3_m2; + +RNG_HandleTypeDef hrng; + +SPI_HandleTypeDef hspi2; +SPI_HandleTypeDef hspi3; +SPI_HandleTypeDef hspi4; +SPI_HandleTypeDef hspi6; +DMA_HandleTypeDef hdma_spi2_rx; +DMA_HandleTypeDef hdma_spi3_rx; +DMA_HandleTypeDef hdma_spi4_rx; +DMA_HandleTypeDef hdma_spi6_rx; + +TIM_HandleTypeDef htim2; +TIM_HandleTypeDef htim4; +TIM_HandleTypeDef htim5; +TIM_HandleTypeDef htim8; +TIM_HandleTypeDef htim14; +TIM_HandleTypeDef htim15; +TIM_HandleTypeDef htim16; + +UART_HandleTypeDef huart4; +USART_HandleTypeDef husart1; +USART_HandleTypeDef husart2; +USART_HandleTypeDef husart3; +USART_HandleTypeDef husart6; +DMA_HandleTypeDef hdma_uart4_rx; +DMA_HandleTypeDef hdma_uart4_tx; +DMA_HandleTypeDef hdma_usart1_rx; +DMA_HandleTypeDef hdma_usart2_rx; +DMA_HandleTypeDef hdma_usart3_rx; +DMA_HandleTypeDef hdma_usart6_rx; + +DMA_HandleTypeDef hdma_memtomem_dma2_stream1; +/* USER CODE BEGIN PV */ + +uint8_t rxBuffer[COMMAND_MAX_SIZE] __attribute__((aligned(4))); +uint8_t txBuffer[COMMAND_MAX_SIZE]; +uint32_t bitstream_len; +__attribute__((section(".RAM_D1"))) uint8_t bitstream_buffer[MAX_BITSTREAM_SIZE]; // 160KB buffer + +volatile uint8_t event_bits = 0x00; // holds the event bits to be flipped +volatile uint8_t event_bits_enabled = 0x00; // holds the event bits for the cameras to be enabled +volatile uint16_t pulse_count = 0; +extern uint32_t imu_frame_counter; +volatile bool _enter_dfu = false; + +ICM_Axis3D a; +ICM_Axis3D m; +ICM_Axis3D g; +float t; +char usb_buf[128]; +// Debug flags (sensor debug and fake data are controlled via OW_CMD_DEBUG_FLAGS) +bool uart_stream = false; + +uint16_t fail_count = 0; + +extern USBD_HandleTypeDef hUsbDeviceHS; + +const char *bit_rep[16] = { + [ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011", + [ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111", + [ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011", + [12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111", +}; +/* USER CODE END PV */ + +/* Private function prototypes -----------------------------------------------*/ +void SystemClock_Config(void); +void PeriphCommonClock_Config(void); +static void MPU_Config(void); +static void MX_GPIO_Init(void); +static void MX_DMA_Init(void); +static void MX_BDMA_Init(void); +static void MX_CRC_Init(void); +static void MX_I2C1_Init(void); +static void MX_RNG_Init(void); +static void MX_SPI2_Init(void); +static void MX_SPI3_Init(void); +static void MX_SPI4_Init(void); +static void MX_SPI6_Init(void); +static void MX_TIM2_Init(void); +static void MX_UART4_Init(void); +static void MX_USART6_Init(void); +static void MX_TIM8_Init(void); +static void MX_TIM4_Init(void); +static void MX_USART1_Init(void); +static void MX_USART2_Init(void); +static void MX_USART3_Init(void); +static void MX_TIM14_Init(void); +static void MX_TIM16_Init(void); +static void MX_TIM5_Init(void); +static void MX_TIM15_Init(void); +static void MX_IWDG1_Init(void); +static void MX_RAMECC_Init(void); +/* USER CODE BEGIN PFP */ + +/* USER CODE END PFP */ + +/* Private user code ---------------------------------------------------------*/ +/* USER CODE BEGIN 0 */ +/* Print last reset cause - helps diagnose thermal/brownout (BOR) or watchdog (IWDG) */ +static void print_reset_cause(void) +{ + uint8_t first = 1; + if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) { + printf("Reset cause: BOR (Brown-Out) - possible thermal/voltage droop\r\n"); + first = 0; + } + if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST)) { + printf("%sReset cause: POR (Power-On)\r\n", first ? "" : " "); + first = 0; + } + if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST)) { + printf("%sReset cause: PIN (External NRST)\r\n", first ? "" : " "); + first = 0; + } + if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)) { + printf("%sReset cause: Software\r\n", first ? "" : " "); + first = 0; + } +#if defined(RCC_FLAG_IWDG1RST) + if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDG1RST)) { + printf("%sReset cause: IWDG (Independent Watchdog) - possible lockup/thermal\r\n", first ? "" : " "); + first = 0; + } +#endif +#if defined(RCC_FLAG_WWDG1RST) + if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDG1RST)) { + printf("%sReset cause: WWDG (Window Watchdog)\r\n", first ? "" : " "); + first = 0; + } +#endif +#if defined(RCC_FLAG_LPWR1RST) + if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWR1RST)) { + printf("%sReset cause: LPWR (Low-power)\r\n", first ? "" : " "); + first = 0; + } +#endif + if (first) { + printf("Reset cause: unknown\r\n"); + } + __HAL_RCC_CLEAR_RESET_FLAGS(); +} + +/* Poll MCU junction temperature; print warning if above high threshold */ +#define MCU_TEMP_CHECK_MS 5000u +static void poll_mcu_temperature(void) +{ + static uint32_t last_check_ms = 0; + uint32_t now = HAL_GetTick(); + if ((last_check_ms != 0u) && ((now - last_check_ms) < MCU_TEMP_CHECK_MS)) { + return; + } + last_check_ms = now; + uint32_t level = HAL_PWREx_GetTemperatureLevel(); + if (level == PWR_TEMP_ABOVE_HIGH_THRESHOLD) { + printf("[THERMAL] MCU junction temp ABOVE HIGH THRESHOLD - reduce load/cooling!\r\n"); + } +} + +static void PrintI2CSpeed(I2C_HandleTypeDef *hi2c) +{ + // Assuming the timing is configured in I2C_TIMINGR register + uint32_t timing = hi2c->Init.Timing; + uint32_t presc = (timing >> 28) & 0xF; + uint32_t scll = (timing >> 0) & 0xFF; + uint32_t sclh = (timing >> 8) & 0xFF; + + // Get the I2C clock source frequency + uint32_t i2c_clk_freq = HAL_RCC_GetPCLK1Freq(); + + // Calculate SCL speed (frequency) in Hz + uint32_t scl_freq = i2c_clk_freq / ((presc + 1) * (scll + 1 + sclh + 1)); + + // Print I2C speed + printf("I2C Speed: %ld Hz\r\n", scl_freq); // Print the I2C speed in kHz +} + +/* USER CODE END 0 */ + +/** + * @brief The application entry point. + * @retval int + */ +int main(void) +{ + + /* USER CODE BEGIN 1 */ + + /* USER CODE END 1 */ + + /* MPU Configuration--------------------------------------------------------*/ + MPU_Config(); + + /* MCU Configuration--------------------------------------------------------*/ + + /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ + HAL_Init(); + + /* USER CODE BEGIN Init */ + + /* USER CODE END Init */ + + /* Configure the system clock */ + SystemClock_Config(); + + /* Configure the peripherals common clocks */ + PeriphCommonClock_Config(); + + /* USER CODE BEGIN SysInit */ + + /* USER CODE END SysInit */ + + /* Initialize all configured peripherals */ + MX_GPIO_Init(); + MX_DMA_Init(); + MX_BDMA_Init(); + MX_CRC_Init(); + MX_I2C1_Init(); + MX_RNG_Init(); + MX_SPI2_Init(); + MX_SPI3_Init(); + MX_SPI4_Init(); + MX_SPI6_Init(); + MX_TIM2_Init(); + MX_UART4_Init(); + MX_USART6_Init(); + MX_TIM8_Init(); + MX_TIM4_Init(); + MX_USART1_Init(); + MX_USART2_Init(); + MX_USART3_Init(); + MX_TIM14_Init(); + MX_TIM16_Init(); + MX_TIM5_Init(); + MX_TIM15_Init(); + MX_IWDG1_Init(); + MX_RAMECC_Init(); + /* USER CODE BEGIN 2 */ + + if (HAL_TIM_Base_Start(&htim5) != HAL_OK) + { + Error_Handler(); + } + + init_dma_logging(); + + DWT_Init(); + + // enable I2C MUX + HAL_GPIO_WritePin(MUX_RESET_GPIO_Port, MUX_RESET_Pin, GPIO_PIN_RESET); + + // enable USB PHY + HAL_GPIO_WritePin(USB_RESET_GPIO_Port, USB_RESET_Pin, GPIO_PIN_RESET); + + printf("\033c"); + fflush(stdout); + delay_ms(500); + printf("Openwater open-MOTION Aggregator\r\n\r\n"); + printf("FW: %s (%s)\r\nDate: %s\r\n", + FW_VERSION_STRING, + FW_SHA_STRING, + FW_BUILD_TIME_STRING); + printf("CPU Clock Frequency: %lu MHz\r\n", + HAL_RCC_GetSysClockFreq() / 1000000); + system_monitor_capture_reset_cause(); + print_reset_cause(); + system_monitor_print_history(); + system_monitor_ecc_enable(); + HAL_PWREx_EnableMonitoring(); /* Enable junction temp (TEMPH/TEMPL) monitoring */ + printf("Initializing, please wait ...\r\n"); + + // enable HS USB MUX + HAL_GPIO_WritePin(USB_MUX_GPIO_Port, USB_MUX_Pin, GPIO_PIN_RESET); + + // enable I2C MUX + HAL_GPIO_WritePin(MUX_RESET_GPIO_Port, MUX_RESET_Pin, GPIO_PIN_SET); + + HAL_GPIO_WritePin(FS_OUT_EN_GPIO_Port, FS_OUT_EN_Pin, GPIO_PIN_RESET); //enable Framesync output + + // test i2c + PrintI2CSpeed(&hi2c1); + // I2C_scan(&hi2c1, NULL, 0, true); + delay_ms(100); + X02C1B_FSIN_EXT_disable(); + GPIO_SetOutput(FSIN_EN_GPIO_Port, FSIN_EN_Pin, GPIO_PIN_RESET); // disable fsin output + + if (ICM_WHOAMI() == ICM20948_EXPECTED_ID) + { + if(ICM_Init() != HAL_OK){ + printf("Failed to initialize IMU\r\n\n"); + } + else + { + printf("IMU detected\r\n"); + delay_ms(100); + if(verbose_on) ICM_DumpRegisters(); + } + } + else + { + printf("IMU NOT detected\r\n\n"); + } + + delay_ms(10); + + HAL_GPIO_WritePin(USB_RESET_GPIO_Port, USB_RESET_Pin, GPIO_PIN_SET); + + HAL_GPIO_WritePin(ERROR_LED_GPIO_Port, ERROR_LED_Pin, GPIO_PIN_SET); + + init_camera_sensors(); // init structures and camera configs + + // Select default camera + TCA9548A_SelectChannel(&hi2c1, 0x70, get_active_cam()->i2c_target); + + delay_ms(250); + MX_USB_DEVICE_Init(); + delay_ms(1000); + //GPIO_SetHiZ(GPIOA, GPIO_PIN_2); + + comms_host_start(); + // fill_frame_buffers(); + HAL_IWDG_Refresh(&hiwdg1); + printf("System Running\r\n"); + /* USER CODE END 2 */ + + /* Infinite loop */ + /* USER CODE BEGIN WHILE */ + while (1) + { + /* USER CODE END WHILE */ + + /* USER CODE BEGIN 3 */ + comms_host_check_received(); // check comms + check_streaming(); + poll_mcu_temperature(); /* Print warning if MCU junction temp above threshold */ + USB_RecoveryCheck(); /* EFT/lock-up watchdog: rebuild USB stack if bus goes dead */ + system_monitor_poll(); /* ECC report drain + DMA error-flag scan */ + HAL_IWDG_Refresh(&hiwdg1); + } + + /* USER CODE END 3 */ +} + +/** + * @brief System Clock Configuration + * @retval None + */ +void SystemClock_Config(void) +{ + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; + + /** Supply configuration update enable + */ + HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY); + + /** Configure the main internal regulator output voltage + */ + __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0); + + while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {} + + /** Initializes the RCC Oscillators according to the specified parameters + * in the RCC_OscInitTypeDef structure. + */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE; + RCC_OscInitStruct.HSEState = RCC_HSE_ON; + RCC_OscInitStruct.LSIState = RCC_LSI_ON; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLM = 3; + RCC_OscInitStruct.PLL.PLLN = 120; + RCC_OscInitStruct.PLL.PLLP = 2; + RCC_OscInitStruct.PLL.PLLQ = 20; + RCC_OscInitStruct.PLL.PLLR = 2; + RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3; + RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE; + RCC_OscInitStruct.PLL.PLLFRACN = 0; + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) + { + Error_Handler(); + } + + /** Initializes the CPU, AHB and APB buses clocks + */ + RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK + |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2 + |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1; + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2; + RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; + RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; + RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; + RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; + + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) + { + Error_Handler(); + } + __HAL_RCC_PLL2CLKOUT_ENABLE(RCC_PLL2_DIVP); + HAL_RCC_MCOConfig(RCC_MCO2, RCC_MCO2SOURCE_PLL2PCLK, RCC_MCODIV_5); +} + +/** + * @brief Peripherals Common Clock Configuration + * @retval None + */ +void PeriphCommonClock_Config(void) +{ + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; + + /** Initializes the peripherals clock + */ + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI3|RCC_PERIPHCLK_SPI2; + PeriphClkInitStruct.PLL2.PLL2M = 6; + PeriphClkInitStruct.PLL2.PLL2N = 120; + PeriphClkInitStruct.PLL2.PLL2P = 4; + PeriphClkInitStruct.PLL2.PLL2Q = 2; + PeriphClkInitStruct.PLL2.PLL2R = 2; + PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_2; + PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE; + PeriphClkInitStruct.PLL2.PLL2FRACN = 0; + PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL2; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) + { + Error_Handler(); + } +} + +/** + * @brief CRC Initialization Function + * @param None + * @retval None + */ +static void MX_CRC_Init(void) +{ + + /* USER CODE BEGIN CRC_Init 0 */ + + /* USER CODE END CRC_Init 0 */ + + /* USER CODE BEGIN CRC_Init 1 */ + + /* USER CODE END CRC_Init 1 */ + hcrc.Instance = CRC; + hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE; + hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE; + hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE; + hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE; + hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES; + if (HAL_CRC_Init(&hcrc) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN CRC_Init 2 */ + + /* USER CODE END CRC_Init 2 */ + +} + +/** + * @brief I2C1 Initialization Function + * @param None + * @retval None + */ +static void MX_I2C1_Init(void) +{ + + /* USER CODE BEGIN I2C1_Init 0 */ + + /* USER CODE END I2C1_Init 0 */ + + /* USER CODE BEGIN I2C1_Init 1 */ + + /* USER CODE END I2C1_Init 1 */ + hi2c1.Instance = I2C1; + hi2c1.Init.Timing = 0x00B03FDB; + hi2c1.Init.OwnAddress1 = 0; + hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; + hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; + hi2c1.Init.OwnAddress2 = 0; + hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK; + hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; + hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; + if (HAL_I2C_Init(&hi2c1) != HAL_OK) + { + Error_Handler(); + } + + /** Configure Analogue filter + */ + if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK) + { + Error_Handler(); + } + + /** Configure Digital filter + */ + if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN I2C1_Init 2 */ + + /* USER CODE END I2C1_Init 2 */ + +} + +/** + * @brief IWDG1 Initialization Function + * @param None + * @retval None + */ +static void MX_IWDG1_Init(void) +{ + + /* USER CODE BEGIN IWDG1_Init 0 */ + + /* USER CODE END IWDG1_Init 0 */ + + /* USER CODE BEGIN IWDG1_Init 1 */ + + /* USER CODE END IWDG1_Init 1 */ + hiwdg1.Instance = IWDG1; + hiwdg1.Init.Prescaler = IWDG_PRESCALER_128; + hiwdg1.Init.Window = 4095; + hiwdg1.Init.Reload = 4095; + if (HAL_IWDG_Init(&hiwdg1) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN IWDG1_Init 2 */ + + /* USER CODE END IWDG1_Init 2 */ + +} + +/** + * @brief RAMECC Initialization Function + * @param None + * @retval None + */ +static void MX_RAMECC_Init(void) +{ + + /* USER CODE BEGIN RAMECC_Init 0 */ + + /* USER CODE END RAMECC_Init 0 */ + + /* USER CODE BEGIN RAMECC_Init 1 */ + + /* USER CODE END RAMECC_Init 1 */ + + /** Initialize RAMECC1 M1 : AXI SRAM + */ + hramecc1_m1.Instance = RAMECC1_Monitor1; + if (HAL_RAMECC_Init(&hramecc1_m1) != HAL_OK) + { + Error_Handler(); + } + + /** Initialize RAMECC1 M2 : ITCM-RAM + */ + hramecc1_m2.Instance = RAMECC1_Monitor2; + if (HAL_RAMECC_Init(&hramecc1_m2) != HAL_OK) + { + Error_Handler(); + } + + /** Initialize RAMECC1 M3 : D0TCM-RAM + */ + hramecc1_m3.Instance = RAMECC1_Monitor3; + if (HAL_RAMECC_Init(&hramecc1_m3) != HAL_OK) + { + Error_Handler(); + } + + /** Initialize RAMECC1 M4 : D1TCM-RAM + */ + hramecc1_m4.Instance = RAMECC1_Monitor4; + if (HAL_RAMECC_Init(&hramecc1_m4) != HAL_OK) + { + Error_Handler(); + } + + /** Initialize RAMECC1 M5 : ETM RAM + */ + hramecc1_m5.Instance = RAMECC1_Monitor5; + if (HAL_RAMECC_Init(&hramecc1_m5) != HAL_OK) + { + Error_Handler(); + } + + /** Initialize RAMECC2 M1 : SRAM1_0 + */ + hramecc2_m1.Instance = RAMECC2_Monitor1; + if (HAL_RAMECC_Init(&hramecc2_m1) != HAL_OK) + { + Error_Handler(); + } + + /** Initialize RAMECC2 M2 SRAM1_1 + */ + hramecc2_m2.Instance = RAMECC2_Monitor2; + if (HAL_RAMECC_Init(&hramecc2_m2) != HAL_OK) + { + Error_Handler(); + } + + /** Initialize RAMECC2 M3 : SRAM2_0 + */ + hramecc2_m3.Instance = RAMECC2_Monitor3; + if (HAL_RAMECC_Init(&hramecc2_m3) != HAL_OK) + { + Error_Handler(); + } + + /** Initialize RAMECC2 M4 : SRAM2_1 + */ + hramecc2_m4.Instance = RAMECC2_Monitor4; + if (HAL_RAMECC_Init(&hramecc2_m4) != HAL_OK) + { + Error_Handler(); + } + + /** Initialize RAMECC2 M5 : SRAM3 + */ + hramecc2_m5.Instance = RAMECC2_Monitor5; + if (HAL_RAMECC_Init(&hramecc2_m5) != HAL_OK) + { + Error_Handler(); + } + + /** Initialize RAMECC3 M1 : SRAM4 + */ + hramecc3_m1.Instance = RAMECC3_Monitor1; + if (HAL_RAMECC_Init(&hramecc3_m1) != HAL_OK) + { + Error_Handler(); + } + + /** Initialize RAMECC3 M2 : Backup RAM + */ + hramecc3_m2.Instance = RAMECC3_Monitor2; + if (HAL_RAMECC_Init(&hramecc3_m2) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN RAMECC_Init 2 */ + + /* USER CODE END RAMECC_Init 2 */ + +} + +/** + * @brief RNG Initialization Function + * @param None + * @retval None + */ +static void MX_RNG_Init(void) +{ + + /* USER CODE BEGIN RNG_Init 0 */ + + /* USER CODE END RNG_Init 0 */ + + /* USER CODE BEGIN RNG_Init 1 */ + + /* USER CODE END RNG_Init 1 */ + hrng.Instance = RNG; + hrng.Init.ClockErrorDetection = RNG_CED_ENABLE; + if (HAL_RNG_Init(&hrng) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN RNG_Init 2 */ + + /* USER CODE END RNG_Init 2 */ + +} + +/** + * @brief SPI2 Initialization Function + * @param None + * @retval None + */ +static void MX_SPI2_Init(void) +{ + + /* USER CODE BEGIN SPI2_Init 0 */ + + /* USER CODE END SPI2_Init 0 */ + + /* USER CODE BEGIN SPI2_Init 1 */ + + /* USER CODE END SPI2_Init 1 */ + /* SPI2 parameter configuration*/ + hspi2.Instance = SPI2; + hspi2.Init.Mode = SPI_MODE_SLAVE; + hspi2.Init.Direction = SPI_DIRECTION_2LINES; + hspi2.Init.DataSize = SPI_DATASIZE_8BIT; + hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH; + hspi2.Init.CLKPhase = SPI_PHASE_2EDGE; + hspi2.Init.NSS = SPI_NSS_SOFT; + hspi2.Init.FirstBit = SPI_FIRSTBIT_LSB; + hspi2.Init.TIMode = SPI_TIMODE_DISABLE; + hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + hspi2.Init.CRCPolynomial = 0x0; + hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; + hspi2.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; + hspi2.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; + hspi2.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; + hspi2.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; + hspi2.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; + hspi2.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; + hspi2.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE; + hspi2.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE; + hspi2.Init.IOSwap = SPI_IO_SWAP_DISABLE; + if (HAL_SPI_Init(&hspi2) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN SPI2_Init 2 */ + + /* USER CODE END SPI2_Init 2 */ + +} + +/** + * @brief SPI3 Initialization Function + * @param None + * @retval None + */ +static void MX_SPI3_Init(void) +{ + + /* USER CODE BEGIN SPI3_Init 0 */ + + /* USER CODE END SPI3_Init 0 */ + + /* USER CODE BEGIN SPI3_Init 1 */ + + /* USER CODE END SPI3_Init 1 */ + /* SPI3 parameter configuration*/ + hspi3.Instance = SPI3; + hspi3.Init.Mode = SPI_MODE_SLAVE; + hspi3.Init.Direction = SPI_DIRECTION_2LINES; + hspi3.Init.DataSize = SPI_DATASIZE_8BIT; + hspi3.Init.CLKPolarity = SPI_POLARITY_HIGH; + hspi3.Init.CLKPhase = SPI_PHASE_2EDGE; + hspi3.Init.NSS = SPI_NSS_SOFT; + hspi3.Init.FirstBit = SPI_FIRSTBIT_LSB; + hspi3.Init.TIMode = SPI_TIMODE_DISABLE; + hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + hspi3.Init.CRCPolynomial = 0x0; + hspi3.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; + hspi3.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; + hspi3.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; + hspi3.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; + hspi3.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; + hspi3.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; + hspi3.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; + hspi3.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE; + hspi3.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE; + hspi3.Init.IOSwap = SPI_IO_SWAP_DISABLE; + if (HAL_SPI_Init(&hspi3) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN SPI3_Init 2 */ + + /* USER CODE END SPI3_Init 2 */ + +} + +/** + * @brief SPI4 Initialization Function + * @param None + * @retval None + */ +static void MX_SPI4_Init(void) +{ + + /* USER CODE BEGIN SPI4_Init 0 */ + + /* USER CODE END SPI4_Init 0 */ + + /* USER CODE BEGIN SPI4_Init 1 */ + + /* USER CODE END SPI4_Init 1 */ + /* SPI4 parameter configuration*/ + hspi4.Instance = SPI4; + hspi4.Init.Mode = SPI_MODE_SLAVE; + hspi4.Init.Direction = SPI_DIRECTION_2LINES; + hspi4.Init.DataSize = SPI_DATASIZE_8BIT; + hspi4.Init.CLKPolarity = SPI_POLARITY_HIGH; + hspi4.Init.CLKPhase = SPI_PHASE_2EDGE; + hspi4.Init.NSS = SPI_NSS_SOFT; + hspi4.Init.FirstBit = SPI_FIRSTBIT_LSB; + hspi4.Init.TIMode = SPI_TIMODE_DISABLE; + hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + hspi4.Init.CRCPolynomial = 0x0; + hspi4.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; + hspi4.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; + hspi4.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; + hspi4.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; + hspi4.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; + hspi4.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; + hspi4.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; + hspi4.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE; + hspi4.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE; + hspi4.Init.IOSwap = SPI_IO_SWAP_DISABLE; + if (HAL_SPI_Init(&hspi4) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN SPI4_Init 2 */ + + /* USER CODE END SPI4_Init 2 */ + +} + +/** + * @brief SPI6 Initialization Function + * @param None + * @retval None + */ +static void MX_SPI6_Init(void) +{ + + /* USER CODE BEGIN SPI6_Init 0 */ + + /* USER CODE END SPI6_Init 0 */ + + /* USER CODE BEGIN SPI6_Init 1 */ + + /* USER CODE END SPI6_Init 1 */ + /* SPI6 parameter configuration*/ + hspi6.Instance = SPI6; + hspi6.Init.Mode = SPI_MODE_SLAVE; + hspi6.Init.Direction = SPI_DIRECTION_2LINES; + hspi6.Init.DataSize = SPI_DATASIZE_8BIT; + hspi6.Init.CLKPolarity = SPI_POLARITY_HIGH; + hspi6.Init.CLKPhase = SPI_PHASE_2EDGE; + hspi6.Init.NSS = SPI_NSS_SOFT; + hspi6.Init.FirstBit = SPI_FIRSTBIT_LSB; + hspi6.Init.TIMode = SPI_TIMODE_DISABLE; + hspi6.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + hspi6.Init.CRCPolynomial = 0x0; + hspi6.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; + hspi6.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; + hspi6.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; + hspi6.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; + hspi6.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; + hspi6.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; + hspi6.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; + hspi6.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE; + hspi6.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE; + hspi6.Init.IOSwap = SPI_IO_SWAP_DISABLE; + if (HAL_SPI_Init(&hspi6) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN SPI6_Init 2 */ + + /* USER CODE END SPI6_Init 2 */ + +} + +/** + * @brief TIM2 Initialization Function + * @param None + * @retval None + */ +static void MX_TIM2_Init(void) +{ + + /* USER CODE BEGIN TIM2_Init 0 */ + + /* USER CODE END TIM2_Init 0 */ + + TIM_ClockConfigTypeDef sClockSourceConfig = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; + TIM_OC_InitTypeDef sConfigOC = {0}; + + /* USER CODE BEGIN TIM2_Init 1 */ + + /* USER CODE END TIM2_Init 1 */ + htim2.Instance = TIM2; + htim2.Init.Prescaler = 0; + htim2.Init.CounterMode = TIM_COUNTERMODE_UP; + htim2.Init.Period = 4294967295; + htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_Base_Init(&htim2) != HAL_OK) + { + Error_Handler(); + } + sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; + if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) + { + Error_Handler(); + } + if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) + { + Error_Handler(); + } + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) + { + Error_Handler(); + } + sConfigOC.OCMode = TIM_OCMODE_PWM1; + sConfigOC.Pulse = 0; + sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; + sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; + if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN TIM2_Init 2 */ + + /* USER CODE END TIM2_Init 2 */ + HAL_TIM_MspPostInit(&htim2); + +} + +/** + * @brief TIM4 Initialization Function + * @param None + * @retval None + */ +static void MX_TIM4_Init(void) +{ + + /* USER CODE BEGIN TIM4_Init 0 */ + + /* USER CODE END TIM4_Init 0 */ + + TIM_MasterConfigTypeDef sMasterConfig = {0}; + TIM_OC_InitTypeDef sConfigOC = {0}; + + /* USER CODE BEGIN TIM4_Init 1 */ + + /* USER CODE END TIM4_Init 1 */ + htim4.Instance = TIM4; + htim4.Init.Prescaler = 1000; + htim4.Init.CounterMode = TIM_COUNTERMODE_UP; + htim4.Init.Period = 5999; + htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_PWM_Init(&htim4) != HAL_OK) + { + Error_Handler(); + } + if (HAL_TIM_OC_Init(&htim4) != HAL_OK) + { + Error_Handler(); + } + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK) + { + Error_Handler(); + } + sConfigOC.OCMode = TIM_OCMODE_PWM1; + sConfigOC.Pulse = 3000; + sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW; + sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; + if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) + { + Error_Handler(); + } + sConfigOC.OCMode = TIM_OCMODE_TIMING; + sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; + if (HAL_TIM_OC_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN TIM4_Init 2 */ + LL_TIM_EnableIT_CC1(TIM4); + /* USER CODE END TIM4_Init 2 */ + HAL_TIM_MspPostInit(&htim4); + +} + +/** + * @brief TIM5 Initialization Function + * @param None + * @retval None + */ +static void MX_TIM5_Init(void) +{ + + /* USER CODE BEGIN TIM5_Init 0 */ + + /* USER CODE END TIM5_Init 0 */ + + TIM_ClockConfigTypeDef sClockSourceConfig = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; + + /* USER CODE BEGIN TIM5_Init 1 */ + + /* USER CODE END TIM5_Init 1 */ + htim5.Instance = TIM5; + htim5.Init.Prescaler = 2400-1; + htim5.Init.CounterMode = TIM_COUNTERMODE_UP; + htim5.Init.Period = 0xFFFFFFFF; + htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_Base_Init(&htim5) != HAL_OK) + { + Error_Handler(); + } + sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; + if (HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig) != HAL_OK) + { + Error_Handler(); + } + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN TIM5_Init 2 */ + + /* USER CODE END TIM5_Init 2 */ + +} + +/** + * @brief TIM8 Initialization Function + * @param None + * @retval None + */ +static void MX_TIM8_Init(void) +{ + + /* USER CODE BEGIN TIM8_Init 0 */ + + /* USER CODE END TIM8_Init 0 */ + + TIM_ClockConfigTypeDef sClockSourceConfig = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; + + /* USER CODE BEGIN TIM8_Init 1 */ + + /* USER CODE END TIM8_Init 1 */ + htim8.Instance = TIM8; + htim8.Init.Prescaler = 0; + htim8.Init.CounterMode = TIM_COUNTERMODE_UP; + htim8.Init.Period = 10-1; + htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + htim8.Init.RepetitionCounter = 0; + htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_Base_Init(&htim8) != HAL_OK) + { + Error_Handler(); + } + sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; + if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK) + { + Error_Handler(); + } + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN TIM8_Init 2 */ + + /* USER CODE END TIM8_Init 2 */ + +} + +/** + * @brief TIM14 Initialization Function + * @param None + * @retval None + */ +static void MX_TIM14_Init(void) +{ + + /* USER CODE BEGIN TIM14_Init 0 */ + + /* USER CODE END TIM14_Init 0 */ + + /* USER CODE BEGIN TIM14_Init 1 */ + + /* USER CODE END TIM14_Init 1 */ + htim14.Instance = TIM14; + htim14.Init.Prescaler = 240-1; + htim14.Init.CounterMode = TIM_COUNTERMODE_UP; + htim14.Init.Period = 5000-1; + htim14.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + htim14.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_Base_Init(&htim14) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN TIM14_Init 2 */ + + /* USER CODE END TIM14_Init 2 */ + +} + +/** + * @brief TIM15 Initialization Function + * @param None + * @retval None + */ +static void MX_TIM15_Init(void) +{ + + /* USER CODE BEGIN TIM15_Init 0 */ + + /* USER CODE END TIM15_Init 0 */ + + TIM_ClockConfigTypeDef sClockSourceConfig = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; + + /* USER CODE BEGIN TIM15_Init 1 */ + + /* USER CODE END TIM15_Init 1 */ + htim15.Instance = TIM15; + htim15.Init.Prescaler = 240-1; + htim15.Init.CounterMode = TIM_COUNTERMODE_UP; + htim15.Init.Period = 65535; + htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + htim15.Init.RepetitionCounter = 0; + htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_Base_Init(&htim15) != HAL_OK) + { + Error_Handler(); + } + sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; + if (HAL_TIM_ConfigClockSource(&htim15, &sClockSourceConfig) != HAL_OK) + { + Error_Handler(); + } + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &sMasterConfig) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN TIM15_Init 2 */ + + /* USER CODE END TIM15_Init 2 */ + +} + +/** + * @brief TIM16 Initialization Function + * @param None + * @retval None + */ +static void MX_TIM16_Init(void) +{ + + /* USER CODE BEGIN TIM16_Init 0 */ + + /* USER CODE END TIM16_Init 0 */ + + /* USER CODE BEGIN TIM16_Init 1 */ + + /* USER CODE END TIM16_Init 1 */ + htim16.Instance = TIM16; + htim16.Init.Prescaler = 240-1; + htim16.Init.CounterMode = TIM_COUNTERMODE_UP; + htim16.Init.Period = 25000-1; + htim16.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + htim16.Init.RepetitionCounter = 0; + htim16.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_Base_Init(&htim16) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN TIM16_Init 2 */ + + /* USER CODE END TIM16_Init 2 */ + +} + +/** + * @brief UART4 Initialization Function + * @param None + * @retval None + */ +static void MX_UART4_Init(void) +{ + + /* USER CODE BEGIN UART4_Init 0 */ + + /* USER CODE END UART4_Init 0 */ + + /* USER CODE BEGIN UART4_Init 1 */ + + /* USER CODE END UART4_Init 1 */ + huart4.Instance = UART4; + huart4.Init.BaudRate = 115200; + huart4.Init.WordLength = UART_WORDLENGTH_8B; + huart4.Init.StopBits = UART_STOPBITS_1; + huart4.Init.Parity = UART_PARITY_NONE; + huart4.Init.Mode = UART_MODE_TX_RX; + huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE; + huart4.Init.OverSampling = UART_OVERSAMPLING_16; + huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; + huart4.Init.ClockPrescaler = UART_PRESCALER_DIV1; + huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; + if (HAL_UART_Init(&huart4) != HAL_OK) + { + Error_Handler(); + } + if (HAL_UARTEx_SetTxFifoThreshold(&huart4, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK) + { + Error_Handler(); + } + if (HAL_UARTEx_SetRxFifoThreshold(&huart4, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK) + { + Error_Handler(); + } + if (HAL_UARTEx_DisableFifoMode(&huart4) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN UART4_Init 2 */ + + /* USER CODE END UART4_Init 2 */ + +} + +/** + * @brief USART1 Initialization Function + * @param None + * @retval None + */ +static void MX_USART1_Init(void) +{ + + /* USER CODE BEGIN USART1_Init 0 */ + + /* USER CODE END USART1_Init 0 */ + + /* USER CODE BEGIN USART1_Init 1 */ + + /* USER CODE END USART1_Init 1 */ + husart1.Instance = USART1; + husart1.Init.BaudRate = 115200; + husart1.Init.WordLength = USART_WORDLENGTH_8B; + husart1.Init.StopBits = USART_STOPBITS_1; + husart1.Init.Parity = USART_PARITY_NONE; + husart1.Init.Mode = USART_MODE_RX; + husart1.Init.CLKPolarity = USART_POLARITY_HIGH; + husart1.Init.CLKPhase = USART_PHASE_2EDGE; + husart1.Init.CLKLastBit = USART_LASTBIT_DISABLE; + husart1.Init.ClockPrescaler = USART_PRESCALER_DIV1; + husart1.SlaveMode = USART_SLAVEMODE_ENABLE; + if (HAL_USART_Init(&husart1) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_SetTxFifoThreshold(&husart1, USART_TXFIFO_THRESHOLD_1_8) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_SetRxFifoThreshold(&husart1, USART_RXFIFO_THRESHOLD_8_8) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_EnableFifoMode(&husart1) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_EnableSlaveMode(&husart1) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN USART1_Init 2 */ + + /* USER CODE END USART1_Init 2 */ + +} + +/** + * @brief USART2 Initialization Function + * @param None + * @retval None + */ +static void MX_USART2_Init(void) +{ + + /* USER CODE BEGIN USART2_Init 0 */ + + /* USER CODE END USART2_Init 0 */ + + /* USER CODE BEGIN USART2_Init 1 */ + + /* USER CODE END USART2_Init 1 */ + husart2.Instance = USART2; + husart2.Init.BaudRate = 5000000; + husart2.Init.WordLength = USART_WORDLENGTH_8B; + husart2.Init.StopBits = USART_STOPBITS_1; + husart2.Init.Parity = USART_PARITY_NONE; + husart2.Init.Mode = USART_MODE_RX; + husart2.Init.CLKPolarity = USART_POLARITY_HIGH; + husart2.Init.CLKPhase = USART_PHASE_2EDGE; + husart2.Init.CLKLastBit = USART_LASTBIT_DISABLE; + husart2.Init.ClockPrescaler = USART_PRESCALER_DIV1; + husart2.SlaveMode = USART_SLAVEMODE_ENABLE; + if (HAL_USART_Init(&husart2) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_SetTxFifoThreshold(&husart2, USART_TXFIFO_THRESHOLD_1_8) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_SetRxFifoThreshold(&husart2, USART_RXFIFO_THRESHOLD_8_8) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_EnableFifoMode(&husart2) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_EnableSlaveMode(&husart2) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN USART2_Init 2 */ + + /* USER CODE END USART2_Init 2 */ + +} + +/** + * @brief USART3 Initialization Function + * @param None + * @retval None + */ +static void MX_USART3_Init(void) +{ + + /* USER CODE BEGIN USART3_Init 0 */ + + /* USER CODE END USART3_Init 0 */ + + /* USER CODE BEGIN USART3_Init 1 */ + + /* USER CODE END USART3_Init 1 */ + husart3.Instance = USART3; + husart3.Init.BaudRate = 115200; + husart3.Init.WordLength = USART_WORDLENGTH_8B; + husart3.Init.StopBits = USART_STOPBITS_1; + husart3.Init.Parity = USART_PARITY_NONE; + husart3.Init.Mode = USART_MODE_RX; + husart3.Init.CLKPolarity = USART_POLARITY_HIGH; + husart3.Init.CLKPhase = USART_PHASE_2EDGE; + husart3.Init.CLKLastBit = USART_LASTBIT_DISABLE; + husart3.Init.ClockPrescaler = USART_PRESCALER_DIV1; + husart3.SlaveMode = USART_SLAVEMODE_ENABLE; + if (HAL_USART_Init(&husart3) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_SetTxFifoThreshold(&husart3, USART_TXFIFO_THRESHOLD_1_8) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_SetRxFifoThreshold(&husart3, USART_RXFIFO_THRESHOLD_8_8) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_EnableFifoMode(&husart3) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_EnableSlaveMode(&husart3) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN USART3_Init 2 */ + + /* USER CODE END USART3_Init 2 */ + +} + +/** + * @brief USART6 Initialization Function + * @param None + * @retval None + */ +static void MX_USART6_Init(void) +{ + + /* USER CODE BEGIN USART6_Init 0 */ + + /* USER CODE END USART6_Init 0 */ + + /* USER CODE BEGIN USART6_Init 1 */ + + /* USER CODE END USART6_Init 1 */ + husart6.Instance = USART6; + husart6.Init.BaudRate = 4167000; + husart6.Init.WordLength = USART_WORDLENGTH_8B; + husart6.Init.StopBits = USART_STOPBITS_1; + husart6.Init.Parity = USART_PARITY_NONE; + husart6.Init.Mode = USART_MODE_RX; + husart6.Init.CLKPolarity = USART_POLARITY_HIGH; + husart6.Init.CLKPhase = USART_PHASE_2EDGE; + husart6.Init.CLKLastBit = USART_LASTBIT_DISABLE; + husart6.Init.ClockPrescaler = USART_PRESCALER_DIV1; + husart6.SlaveMode = USART_SLAVEMODE_ENABLE; + if (HAL_USART_Init(&husart6) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_SetTxFifoThreshold(&husart6, USART_TXFIFO_THRESHOLD_1_8) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_SetRxFifoThreshold(&husart6, USART_RXFIFO_THRESHOLD_8_8) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_EnableFifoMode(&husart6) != HAL_OK) + { + Error_Handler(); + } + if (HAL_USARTEx_EnableSlaveMode(&husart6) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN USART6_Init 2 */ + + /* USER CODE END USART6_Init 2 */ + +} + +/** + * Enable DMA controller clock + */ +static void MX_BDMA_Init(void) +{ + + /* DMA controller clock enable */ + __HAL_RCC_BDMA_CLK_ENABLE(); + + /* DMA interrupt init */ + /* BDMA_Channel0_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(BDMA_Channel0_IRQn, DMA_IRQ_PRIORITY, 0); + HAL_NVIC_EnableIRQ(BDMA_Channel0_IRQn); + +} + +/** + * Enable DMA controller clock + * Configure DMA for memory to memory transfers + * hdma_memtomem_dma2_stream1 + */ +static void MX_DMA_Init(void) +{ + + /* DMA controller clock enable */ + __HAL_RCC_DMA1_CLK_ENABLE(); + __HAL_RCC_DMA2_CLK_ENABLE(); + + /* Configure DMA request hdma_memtomem_dma2_stream1 on DMA2_Stream1 */ + hdma_memtomem_dma2_stream1.Instance = DMA2_Stream1; + hdma_memtomem_dma2_stream1.Init.Request = DMA_REQUEST_MEM2MEM; + hdma_memtomem_dma2_stream1.Init.Direction = DMA_MEMORY_TO_MEMORY; + hdma_memtomem_dma2_stream1.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_memtomem_dma2_stream1.Init.MemInc = DMA_MINC_ENABLE; + hdma_memtomem_dma2_stream1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; + hdma_memtomem_dma2_stream1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; + hdma_memtomem_dma2_stream1.Init.Mode = DMA_NORMAL; + hdma_memtomem_dma2_stream1.Init.Priority = DMA_PRIORITY_MEDIUM; + hdma_memtomem_dma2_stream1.Init.FIFOMode = DMA_FIFOMODE_ENABLE; + hdma_memtomem_dma2_stream1.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; + hdma_memtomem_dma2_stream1.Init.MemBurst = DMA_MBURST_INC4; + hdma_memtomem_dma2_stream1.Init.PeriphBurst = DMA_PBURST_INC4; + if (HAL_DMA_Init(&hdma_memtomem_dma2_stream1) != HAL_OK) + { + Error_Handler( ); + } + + /* DMA interrupt init */ + /* DMA1_Stream0_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn); + /* DMA1_Stream1_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn); + /* DMA1_Stream2_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn); + /* DMA1_Stream3_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn); + /* DMA1_Stream4_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn); + /* DMA1_Stream5_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn); + /* DMA1_Stream6_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn); + /* DMA1_Stream7_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn); + /* DMA2_Stream0_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn); + +} + +/** + * @brief GPIO Initialization Function + * @param None + * @retval None + */ +static void MX_GPIO_Init(void) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + /* USER CODE BEGIN MX_GPIO_Init_1 */ + /* USER CODE END MX_GPIO_Init_1 */ + + /* GPIO Ports Clock Enable */ + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOE_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + __HAL_RCC_GPIOH_CLK_ENABLE(); + + /*Configure GPIO pin Output Level */ + HAL_GPIO_WritePin(GPIOC, ERROR_LED_Pin|MUX_RESET_Pin|USB_MUX_Pin, GPIO_PIN_RESET); + + /*Configure GPIO pin Output Level */ + HAL_GPIO_WritePin(USB_RESET_GPIO_Port, USB_RESET_Pin, GPIO_PIN_RESET); + + /*Configure GPIO pin Output Level */ + HAL_GPIO_WritePin(GPIOD, CAM_PWR_1_Pin|CAM_PWR_5_Pin|CAM_PWR_8_Pin|CAM_PWR_4_Pin + |FAN_CTL_Pin|FS_OUT_EN_Pin, GPIO_PIN_SET); + + /*Configure GPIO pin Output Level */ + HAL_GPIO_WritePin(GPIOE, CAM_PWR_7_Pin|CAM_PWR_2_Pin|FSIN_EN_Pin, GPIO_PIN_SET); + + /*Configure GPIO pin Output Level */ + HAL_GPIO_WritePin(CAM_PWR_3_GPIO_Port, CAM_PWR_3_Pin, GPIO_PIN_SET); + + /*Configure GPIO pin Output Level */ + HAL_GPIO_WritePin(CAM_PWR_6_GPIO_Port, CAM_PWR_6_Pin, GPIO_PIN_SET); + + /*Configure GPIO pins : ERROR_LED_Pin MUX_RESET_Pin USB_MUX_Pin CAM_PWR_6_Pin */ + GPIO_InitStruct.Pin = ERROR_LED_Pin|MUX_RESET_Pin|USB_MUX_Pin|CAM_PWR_6_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /*Configure GPIO pins : GPIO0_8_Pin IMU_INT_Pin */ + GPIO_InitStruct.Pin = GPIO0_8_Pin|IMU_INT_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /*Configure GPIO pins : GPIO0_7_Pin GPIO0_5_Pin */ + GPIO_InitStruct.Pin = GPIO0_7_Pin|GPIO0_5_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /*Configure GPIO pins : USB_RESET_Pin CAM_PWR_3_Pin */ + GPIO_InitStruct.Pin = USB_RESET_Pin|CAM_PWR_3_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /*Configure GPIO pins : GPIO0_6_Pin CRESET_8_Pin CRESET_7_Pin PE14 + GPIO0_2_Pin GPIO0_3_Pin CRESET_1_Pin CRESET_3_Pin + CRESET_2_Pin CRESET_4_Pin */ + GPIO_InitStruct.Pin = GPIO0_6_Pin|CRESET_8_Pin|CRESET_7_Pin|GPIO_PIN_14 + |GPIO0_2_Pin|GPIO0_3_Pin|CRESET_1_Pin|CRESET_3_Pin + |CRESET_2_Pin|CRESET_4_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); + + /*Configure GPIO pins : CRESET_6_Pin CRESET_5_Pin GPIO0_4_Pin */ + GPIO_InitStruct.Pin = CRESET_6_Pin|CRESET_5_Pin|GPIO0_4_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); + + /*Configure GPIO pins : CAM_PWR_1_Pin CAM_PWR_5_Pin CAM_PWR_8_Pin CAM_PWR_4_Pin + FAN_CTL_Pin FS_OUT_EN_Pin */ + GPIO_InitStruct.Pin = CAM_PWR_1_Pin|CAM_PWR_5_Pin|CAM_PWR_8_Pin|CAM_PWR_4_Pin + |FAN_CTL_Pin|FS_OUT_EN_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); + + /*Configure GPIO pins : PA12 PA11 */ + GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_11; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF10_OTG1_FS; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /*Configure GPIO pins : CAM_PWR_7_Pin CAM_PWR_2_Pin FSIN_EN_Pin */ + GPIO_InitStruct.Pin = CAM_PWR_7_Pin|CAM_PWR_2_Pin|FSIN_EN_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); + + /*Configure GPIO pin : PC9 */ + GPIO_InitStruct.Pin = GPIO_PIN_9; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF0_MCO; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /*Configure GPIO pin : GPIO0_1_Pin */ + GPIO_InitStruct.Pin = GPIO0_1_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIO0_1_GPIO_Port, &GPIO_InitStruct); + + /* USER CODE BEGIN MX_GPIO_Init_2 */ + /* USER CODE END MX_GPIO_Init_2 */ +} + +/* USER CODE BEGIN 4 */ + +void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) +{ +} + +void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) +{ + if (huart->Instance == UART4) + { + logging_UART_TxCpltCallback(huart); + } +} + +// Error handling callback for USART +void HAL_USART_ErrorCallback(USART_HandleTypeDef *husart) +{ + int8_t cam_id = -1; + + if (husart->Instance == USART1) + { + cam_id = 4; + } + else if (husart->Instance == USART2) + { + cam_id = 0; + } + else if (husart->Instance == USART3) + { + cam_id = 2; + } + else if (husart->Instance == USART6) + { + cam_id = 3; + } + + // Print which USART instance caused the error + if (husart->Instance == USART1) + { + printf("Error in USART1: "); + } + else if (husart->Instance == USART2) + { + printf("Error in USART2: "); + } + else if (husart->Instance == USART3) + { + printf("Error in USART3: "); + } + else if (husart->Instance == USART6) + { + printf("Error in USART6: "); + } + else + { + printf("Error in Unknown USART instance: "); + } + + // Identify specific errors using error codes + if (husart->ErrorCode & HAL_USART_ERROR_PE) + { + printf("Parity error "); + } + if (husart->ErrorCode & HAL_USART_ERROR_NE) + { + printf("Noise error "); + } + if (husart->ErrorCode & HAL_USART_ERROR_FE) + { + printf("Framing error "); + } + if (husart->ErrorCode & HAL_USART_ERROR_ORE) + { + printf("Overrun error "); + __HAL_USART_CLEAR_OREFLAG(husart); + } + if (husart->ErrorCode & HAL_USART_ERROR_DMA) + { + printf("DMA transfer error "); + } + printf("\r\n"); + + /* Attempt graceful recovery for any known camera USART peripheral. + * Same fix as HAL_SPI_ErrorCallback: non-overrun errors (framing, noise, + * DMA) previously fell through to Error_Handler() and halted the MCU. + * Any error on a known camera USART is recoverable via abort+restart. */ + if (cam_id >= 0) + { + abort_data_reception((uint8_t)cam_id); + start_data_reception((uint8_t)cam_id); + return; + } + + /* Truly unknown USART peripheral — log and continue rather than halting. */ + printf("[USART] Unhandled USART error on unknown peripheral, continuing.\r\n"); +} + +// Error handling callback for SPI +void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) +{ + int8_t cam_id = -1; + + if (hspi->Instance == SPI2) + { + cam_id = 6; + } + else if (hspi->Instance == SPI3) + { + cam_id = 5; + } + else if (hspi->Instance == SPI4) + { + cam_id = 7; + } + else if (hspi->Instance == SPI6) + { + cam_id = 1; + } + + // Print which SPI instance caused the error + if (hspi->Instance == SPI2) + { + printf("Error in SPI2: "); + } + else if (hspi->Instance == SPI3) + { + printf("Error in SPI3: "); + } + else if (hspi->Instance == SPI4) + { + printf("Error in SPI4: "); + } + else if (hspi->Instance == SPI6) + { + printf("Error in SPI6: "); + } + else + { + printf("Error in Unknown SPI instance: "); + } + + // Identify specific errors using error codes + if (hspi->ErrorCode & HAL_SPI_ERROR_OVR) + { + printf("Overrun error "); + __HAL_SPI_CLEAR_OVRFLAG(hspi); + } + if (hspi->ErrorCode & HAL_SPI_ERROR_MODF) + { + printf("Mode fault error "); + } + if (hspi->ErrorCode & HAL_SPI_ERROR_CRC) + { + printf("CRC error "); + } + if (hspi->ErrorCode & HAL_SPI_ERROR_FRE) + { + printf("Frame error "); + } + if (hspi->ErrorCode & HAL_SPI_ERROR_DMA) + { + if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_TE) + printf("TE - HAL_DMA_ERROR_TE"); + if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_FE) + printf("HAL_DMA_ERROR_FE "); + if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_DME) + printf("HAL_DMA_ERROR_DME"); + if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_TIMEOUT) + printf("HAL_DMA_ERROR_TIMEOUT"); + if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_PARAM) + printf("HAL_DMA_ERROR_PARAM"); + if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_NO_XFER) + printf("HAL_DMA_ERROR_NO_XFER"); + if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_NOT_SUPPORTED) + printf("HAL_DMA_ERROR_NOT_SUPPORTED"); + if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_SYNC) + printf("HAL_DMA_ERROR_SYNC"); + if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_REQGEN) + printf("HAL_DMA_ERROR_REQGEN"); + if (hspi->hdmarx->ErrorCode == HAL_DMA_ERROR_BUSY) + printf("HAL_DMA_ERROR_BUSY"); + } + printf("\r\n"); + + /* Attempt graceful recovery for any known camera SPI peripheral. + * Previously only overrun errors were recovered here; all other error types + * fell through to Error_Handler() which calls __disable_irq() + while(1), + * killing USB and all other peripherals. The observed failure mode was: + * Camera 6 overheats → SPI3 DMA/frame error (not OVR) → + * Error_Handler() entered from interrupt context → + * wait_for_usb_queues_to_finish() blocks (USB ISR can't run) → + * __disable_irq() → MCU completely dark. + * Now any error on a known camera SPI triggers abort+restart instead. */ + if (cam_id >= 0) + { + abort_data_reception((uint8_t)cam_id); + start_data_reception((uint8_t)cam_id); + return; + } + + /* Truly unknown SPI peripheral — log and continue rather than halting. */ + printf("[SPI] Unhandled SPI error on unknown peripheral, continuing.\r\n"); +} + + +void set_event_bit_atomic(uint32_t bit) { + __disable_irq(); + event_bits |= bit; + __enable_irq(); +} + +// Interrupt handler for SPI reception +void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) +{ + if (hspi->Instance == SPI2) + { + set_event_bit_atomic(BIT_6); + } + else if (hspi->Instance == SPI3) + { + set_event_bit_atomic(BIT_5); + } + else if (hspi->Instance == SPI4) + { + set_event_bit_atomic(BIT_7); + } + else if (hspi->Instance == SPI6) + { + set_event_bit_atomic(BIT_1); + } +} + +void HAL_USART_RxCpltCallback(USART_HandleTypeDef *husart) +{ + if (husart->Instance == USART1) + { // Check if the interrupt is for USART2 + set_event_bit_atomic(BIT_4); + } + else if (husart->Instance == USART2) + { // Check if the interrupt is for USART2 + set_event_bit_atomic(BIT_0); + } + else if (husart->Instance == USART3) + { // Check if the interrupt is for USART2 + set_event_bit_atomic(BIT_2); + } + else if (husart->Instance == USART6) + { // Check if the interrupt is for USART2 + set_event_bit_atomic(BIT_3); + } + +} + +void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) +{ + if (htim->Instance == TIM4) // Call data sender (internal FSIN)) + { + send_data(); + } +} + +void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) +{ + if (GPIO_Pin == GPIO_PIN_13) // Call REAL data sender if interrupt hit and enabled + { + pulse_count++; + send_data(); + } +} + +void ExitRun0Mode(void) { + // Temporary stub for ExitRun0Mode +} + +/** + * @brief Wait for all USB data queues to finish sending + * @note This function polls USB endpoint status flags until all transmissions complete + * or a timeout occurs. This ensures error messages and diagnostic data are + * transmitted before the system halts. + */ +static void wait_for_usb_queues_to_finish(void) +{ + const uint32_t timeout_ms = 1000; // Maximum wait time: 1 second + uint32_t start_time = get_timestamp_ms(); + uint32_t elapsed = 0; + + printf("Waiting for USB queues to finish...\r\n"); + fflush(stdout); + + // Poll until all endpoints are idle or timeout + while (elapsed < timeout_ms) { + _Bool all_idle = true; + + // Check COMMS endpoint (tx_flag: 1 = idle, 0 = busy) + if (tx_flag == 0) { + all_idle = false; + } + + // Check COMMS bulk endpoint (comms_ep_data: 0 = idle, 1 = transmitting) + if (comms_ep_data != 0) { + all_idle = false; + } + + // Check HISTO endpoint (histo_ep_data: 0 = idle, 1 = transmitting) + if (histo_ep_data != 0) { + all_idle = false; + } + + // Check IMU endpoint (imu_ep_data: 0 = idle, 1 = transmitting) + if (imu_ep_data != 0) { + all_idle = false; + } + + if (all_idle) { + printf("All USB queues finished.\r\n"); + fflush(stdout); + return; + } + + // Small delay to avoid busy-waiting + delay_ms(1); + elapsed = get_timestamp_ms() - start_time; + } + + printf("USB queue wait timeout after %lu ms (some data may not have been sent).\r\n", elapsed); + fflush(stdout); +} + +/* HAL_RAMECC_DetectErrorCallback is implemented in system_monitor.c */ + +/* USER CODE END 4 */ + + /* MPU Configuration */ + +void MPU_Config(void) +{ + MPU_Region_InitTypeDef MPU_InitStruct = {0}; + + /* Disables the MPU */ + HAL_MPU_Disable(); + + /** Initializes and configures the Region and the memory to be protected + */ + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER0; + MPU_InitStruct.BaseAddress = 0x0; + MPU_InitStruct.Size = MPU_REGION_SIZE_4GB; + MPU_InitStruct.SubRegionDisable = 0x87; + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; + MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; + + HAL_MPU_ConfigRegion(&MPU_InitStruct); + /* Enables the MPU */ + HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); + +} + +/** + * @brief Period elapsed callback in non blocking mode + * @note This function is called when TIM17 interrupt took place, inside + * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment + * a global variable "uwTick" used as application time base. + * @param htim : TIM handle + * @retval None + */ +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) +{ + /* USER CODE BEGIN Callback 0 */ + + /* USER CODE END Callback 0 */ + if (htim->Instance == TIM17) + { + HAL_IncTick(); + } + /* USER CODE BEGIN Callback 1 */ + if (htim->Instance == TIM16){ + HistoFake_GenerateAndSend(&hUsbDeviceHS); + } + + if (htim->Instance == TIM14) + { + imu_frame_counter++; + // call imu + if(ICM_GetAllRawData(&a,&t, &g, &m) != HAL_OK){ + printf("IMU Read Error\r\n"); + }else{ + memset(usb_buf,0,128); + int len = snprintf( + usb_buf, sizeof(usb_buf), + "{\"F\":%ld,\"G\":[%d,%d,%d],\"M\":[%d,%d,%d],\"A\":[%d,%d,%d],\"T\":%d.%02d}\r\n", + imu_frame_counter, + g.x, g.y, g.z, + m.x, m.y, m.z, + a.x, a.y, a.z, + (int)t, (int)((t - (int)t) * 100.0f) + ); + USBD_IMU_SetTxBuffer(&hUsbDeviceHS, (uint8_t *)usb_buf, len); + } + } + + if (htim->Instance == TIM15) { + HAL_TIM_Base_Stop_IT(htim); + if(_enter_dfu) { + *((uint32_t *)0x38000000) = 0xDEADBEEF; + } + + + // De-initialize other specific modules + HAL_RNG_DeInit(&hrng); + HAL_CRC_DeInit(&hcrc); + + MX_USB_DEVICE_DeInit(); + delay_ms(300); + // Reset the board + NVIC_SystemReset(); + + } + + /* USER CODE END Callback 1 */ +} + +/** + * @brief This function is executed in case of error occurrence. + * @retval None + */ + +void Error_Handler(void) +{ + /* USER CODE BEGIN Error_Handler_Debug */ + /* User can add his own implementation to report the HAL error return state */ + + uint32_t *stack_ptr; + + HAL_GPIO_TogglePin(ERROR_LED_GPIO_Port, ERROR_LED_Pin); + printf(">>> HARD FAULT <<<\r\n"); + fflush(stdout); // Ensure the output is flushed immediately + + // Get the current stack pointer + __ASM volatile("MRS %0, MSP" : "=r"(stack_ptr)); + + // Print general-purpose registers + printf("Stack Pointer (MSP): 0x%08lX\r\n", (unsigned long)stack_ptr); + printf("Register dump:\r\n"); + printf("R0 : 0x%08lX\r\n", stack_ptr[0]); + printf("R1 : 0x%08lX\r\n", stack_ptr[1]); + printf("R2 : 0x%08lX\r\n", stack_ptr[2]); + printf("R3 : 0x%08lX\r\n", stack_ptr[3]); + printf("R12: 0x%08lX\r\n", stack_ptr[4]); + printf("LR : 0x%08lX (Link Register)\r\n", stack_ptr[5]); + printf("PC : 0x%08lX (Program Counter)\r\n", stack_ptr[6]); + printf("xPSR: 0x%08lX\r\n", stack_ptr[7]); + fflush(stdout); + + // Wait for all USB data queues to finish sending before halting + wait_for_usb_queues_to_finish(); + + delay_ms(100); + __disable_irq(); + while (1) + { + } + /* USER CODE END Error_Handler_Debug */ +} +#ifdef USE_FULL_ASSERT +/** + * @brief Reports the name of the source file and the source line number + * where the assert_param error has occurred. + * @param file: pointer to the source file name + * @param line: assert_param error line source number + * @retval None + */ +void assert_failed(uint8_t *file, uint32_t line) +{ + /* USER CODE BEGIN 6 */ + /* User can add his own implementation to report the file name and line number, + ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ + /* USER CODE END 6 */ +} +#endif /* USE_FULL_ASSERT */ diff --git a/Core/Src/ram_scrub.c b/Core/Src/ram_scrub.c new file mode 100644 index 0000000..ab42911 --- /dev/null +++ b/Core/Src/ram_scrub.c @@ -0,0 +1,55 @@ +/* + * ram_scrub.c + * + * Pre-main RAM scrub. Called from Reset_Handler after SystemInit() and + * before .data/.bss init. + * + * Purpose: STM32H7 SRAM banks are protected with 64-bit SECDED ECC. Until + * a 64-bit word is fully written, its syndrome bits are undefined, so the + * first sub-word (byte/halfword/unaligned) store to that word triggers a + * read-modify-write whose read latches SEDCF+DEDF on the same access. + * Seeding every line with a clean 64-bit zero here makes the syndrome + * valid everywhere, so any [ECC] event reported later is a real fault. + * + * Constraints: + * - May only use the stack for local variables. NO global/static state + * (.data and .bss are not initialized yet at this point). + * - The active stack lives in RAM_D1 (AXI SRAM) per the linker script; + * we still zero RAM_D1, but the few words currently used by this + * function's stack frame get rewritten by normal push/pop afterwards. + */ + +#include +#include "stm32h7xx.h" + +static inline void zero_region_u64(uint32_t start, uint32_t size_bytes) +{ + volatile uint64_t *p = (volatile uint64_t *)(uintptr_t)start; + volatile uint64_t *end = (volatile uint64_t *)(uintptr_t)(start + size_bytes); + while (p < end) { + *p++ = 0u; + } +} + +void ram_scrub(void) +{ + /* Enable AHB2 SRAM1/2/3 clocks; on H7 they are gated at reset and writes + * would otherwise be discarded. AHB SRAM4 (D3) is enabled by default. */ + RCC->AHB2ENR |= RCC_AHB2ENR_SRAM1EN + | RCC_AHB2ENR_SRAM2EN + | RCC_AHB2ENR_SRAM3EN; + (void)RCC->AHB2ENR; /* sync */ + + /* DTCM 128 KB @ 0x20000000 */ + zero_region_u64(0x20000000u, 128u * 1024u); + /* AXI SRAM (RAM_D1) 512 KB @ 0x24000000 — holds stack/.bss/.data */ + zero_region_u64(0x24000000u, 512u * 1024u); + /* SRAM1 128 KB @ 0x30000000 */ + zero_region_u64(0x30000000u, 128u * 1024u); + /* SRAM2 128 KB @ 0x30020000 */ + zero_region_u64(0x30020000u, 128u * 1024u); + /* SRAM3 32 KB @ 0x30040000 */ + zero_region_u64(0x30040000u, 32u * 1024u); + /* SRAM4 64 KB @ 0x38000000 (D3) */ + zero_region_u64(0x38000000u, 64u * 1024u); +} diff --git a/Core/Src/stm32h7xx_it.c b/Core/Src/stm32h7xx_it.c index 51d0b2d..c675d33 100644 --- a/Core/Src/stm32h7xx_it.c +++ b/Core/Src/stm32h7xx_it.c @@ -57,6 +57,18 @@ extern PCD_HandleTypeDef hpcd_USB_OTG_HS; /* External variables --------------------------------------------------------*/ extern I2C_HandleTypeDef hi2c1; +extern RAMECC_HandleTypeDef hramecc1_m1; +extern RAMECC_HandleTypeDef hramecc1_m2; +extern RAMECC_HandleTypeDef hramecc1_m3; +extern RAMECC_HandleTypeDef hramecc1_m4; +extern RAMECC_HandleTypeDef hramecc1_m5; +extern RAMECC_HandleTypeDef hramecc2_m1; +extern RAMECC_HandleTypeDef hramecc2_m2; +extern RAMECC_HandleTypeDef hramecc2_m3; +extern RAMECC_HandleTypeDef hramecc2_m4; +extern RAMECC_HandleTypeDef hramecc2_m5; +extern RAMECC_HandleTypeDef hramecc3_m1; +extern RAMECC_HandleTypeDef hramecc3_m2; extern DMA_HandleTypeDef hdma_spi2_rx; extern DMA_HandleTypeDef hdma_spi3_rx; extern DMA_HandleTypeDef hdma_spi4_rx; @@ -602,6 +614,31 @@ void BDMA_Channel0_IRQHandler(void) /* USER CODE END BDMA_Channel0_IRQn 1 */ } +/** + * @brief This function handles RAM ECC diagnostic global interrupt. + */ +void ECC_IRQHandler(void) +{ + /* USER CODE BEGIN ECC_IRQn 0 */ + + /* USER CODE END ECC_IRQn 0 */ + HAL_RAMECC_IRQHandler(&hramecc1_m1); + HAL_RAMECC_IRQHandler(&hramecc1_m2); + HAL_RAMECC_IRQHandler(&hramecc1_m3); + HAL_RAMECC_IRQHandler(&hramecc1_m4); + HAL_RAMECC_IRQHandler(&hramecc1_m5); + HAL_RAMECC_IRQHandler(&hramecc2_m1); + HAL_RAMECC_IRQHandler(&hramecc2_m2); + HAL_RAMECC_IRQHandler(&hramecc2_m3); + HAL_RAMECC_IRQHandler(&hramecc2_m4); + HAL_RAMECC_IRQHandler(&hramecc2_m5); + HAL_RAMECC_IRQHandler(&hramecc3_m1); + HAL_RAMECC_IRQHandler(&hramecc3_m2); + /* USER CODE BEGIN ECC_IRQn 1 */ + + /* USER CODE END ECC_IRQn 1 */ +} + /* USER CODE BEGIN 1 */ void EXTI15_10_IRQHandler(void) { diff --git a/Core/Src/system_monitor.c b/Core/Src/system_monitor.c new file mode 100644 index 0000000..1855a73 --- /dev/null +++ b/Core/Src/system_monitor.c @@ -0,0 +1,270 @@ +/* + * system_monitor.c + * + * Reset / ECC / DMA health logging. See system_monitor.h. + */ + +#include "system_monitor.h" +#include "main.h" +#include "stm32h7xx_hal.h" +#include +#include + +/* Handles defined in main.c */ +extern RAMECC_HandleTypeDef hramecc1_m1, hramecc1_m2, hramecc1_m3, hramecc1_m4, hramecc1_m5; +extern RAMECC_HandleTypeDef hramecc2_m1, hramecc2_m2, hramecc2_m3, hramecc2_m4, hramecc2_m5; +extern RAMECC_HandleTypeDef hramecc3_m1, hramecc3_m2; + +static RAMECC_HandleTypeDef * const ecc_handles[] = { + &hramecc1_m1, &hramecc1_m2, &hramecc1_m3, &hramecc1_m4, &hramecc1_m5, + &hramecc2_m1, &hramecc2_m2, &hramecc2_m3, &hramecc2_m4, &hramecc2_m5, + &hramecc3_m1, &hramecc3_m2, +}; +static const char * const ecc_names[] = { + "AXI", "ITCM", "DTCM0","DTCM1","ETM", + "SRAM1_0","SRAM1_1","SRAM2_0","SRAM2_1","SRAM3", + "SRAM4","BKPRAM", +}; +#define ECC_MON_COUNT (sizeof(ecc_handles)/sizeof(ecc_handles[0])) + +/* Regions the firmware never writes to (ITCM/ETM/BKPRAM) hold random bits at + * cold boot, so the controller flags every speculative prefetch from them. + * Skip those monitors to keep the log signal-to-noise sane. */ +static const bool ecc_enabled[ECC_MON_COUNT] = { + true, /* AXI */ + false, /* ITCM */ + true, /* DTCM0 */ + true, /* DTCM1 */ + false, /* ETM */ + true, /* SRAM1_0*/ + true, /* SRAM1_1*/ + true, /* SRAM2_0*/ + true, /* SRAM2_1*/ + true, /* SRAM3 */ + true, /* SRAM4 */ + false, /* BKPRAM */ +}; + +/* Cap the number of printf lines per monitor; counters keep incrementing. */ +#define ECC_LOG_LIMIT 4u +static uint8_t s_ecc_log_count[ECC_MON_COUNT]; + +/* ------------------------------------------------------------------ */ +/* Persistent counters (survive IWDG/soft reset, NOT power-on/BOR) */ +/* ------------------------------------------------------------------ */ +#define SYSMON_MAGIC 0x5345434Cu /* "SECL" */ + +typedef struct { + uint32_t magic; + uint32_t boot_count; + uint32_t por_count; + uint32_t pin_count; + uint32_t sft_count; + uint32_t iwdg_count; + uint32_t wwdg_count; + uint32_t bor_count; + uint32_t lpwr_count; + uint32_t ecc_sbe_count; + uint32_t ecc_dbe_count; + uint32_t ecc_last_addr; + uint32_t ecc_last_monitor; /* index into ecc_handles */ + uint32_t dma_err_count; + uint32_t last_rcc_csr; +} sysmon_persist_t; + +/* Placed in .noinit so the C runtime does not zero it at startup. */ +__attribute__((section(".noinit"))) static sysmon_persist_t s_persist; + +/* Volatile flag reserved for future ISR-side use */ +static volatile uint8_t s_ecc_event_pending __attribute__((unused)); + +/* ------------------------------------------------------------------ */ +/* Reset cause */ +/* ------------------------------------------------------------------ */ +void system_monitor_capture_reset_cause(void) +{ + uint32_t rsr = RCC->RSR; + + /* A power-on or brown-out wipes the backup/SRAM state we rely on for + * the magic; treat any boot where the magic doesn't match as cold. */ + bool cold_boot = (s_persist.magic != SYSMON_MAGIC); + if (cold_boot) { + memset(&s_persist, 0, sizeof(s_persist)); + s_persist.magic = SYSMON_MAGIC; + } + + s_persist.boot_count++; + s_persist.last_rcc_csr = rsr; + + if (rsr & RCC_RSR_BORRSTF) s_persist.bor_count++; + if (rsr & RCC_RSR_PORRSTF) s_persist.por_count++; + if (rsr & RCC_RSR_PINRSTF) s_persist.pin_count++; + if (rsr & RCC_RSR_SFTRSTF) s_persist.sft_count++; +#ifdef RCC_RSR_IWDG1RSTF + if (rsr & RCC_RSR_IWDG1RSTF) s_persist.iwdg_count++; +#endif +#ifdef RCC_RSR_WWDG1RSTF + if (rsr & RCC_RSR_WWDG1RSTF) s_persist.wwdg_count++; +#endif +#ifdef RCC_RSR_LPWRRSTF + if (rsr & RCC_RSR_LPWRRSTF) s_persist.lpwr_count++; +#endif +} + +void system_monitor_print_history(void) +{ + printf("Boot stats: boot=%lu POR=%lu PIN=%lu SFT=%lu IWDG=%lu WWDG=%lu BOR=%lu LPWR=%lu\r\n", + (unsigned long)s_persist.boot_count, + (unsigned long)s_persist.por_count, + (unsigned long)s_persist.pin_count, + (unsigned long)s_persist.sft_count, + (unsigned long)s_persist.iwdg_count, + (unsigned long)s_persist.wwdg_count, + (unsigned long)s_persist.bor_count, + (unsigned long)s_persist.lpwr_count); + if (s_persist.ecc_sbe_count || s_persist.ecc_dbe_count) { + printf("ECC stats: SBE=%lu DBE=%lu last @0x%08lx (mon %lu)\r\n", + (unsigned long)s_persist.ecc_sbe_count, + (unsigned long)s_persist.ecc_dbe_count, + (unsigned long)s_persist.ecc_last_addr, + (unsigned long)s_persist.ecc_last_monitor); + } + if (s_persist.dma_err_count) { + printf("DMA stats: transfer-errors=%lu\r\n", + (unsigned long)s_persist.dma_err_count); + } +} + +/* ------------------------------------------------------------------ */ +/* RAMECC (polled) */ +/* ------------------------------------------------------------------ */ +/* Enabling the RAMECC notification interrupt at cold boot is unsafe on + * this part: SRAM2/3/4 contents are random until first written, and a + * read of an uninitialized cache line latches a (false) double-error. + * With the ECC IRQ armed, the CPU then storms in the handler and never + * returns to main, so we poll SR from system_monitor_poll() instead. */ +void system_monitor_ecc_enable(void) +{ + for (uint32_t i = 0; i < ECC_MON_COUNT; i++) { + /* Drop any flags that latched during cold-boot reads of uninit RAM. */ + __HAL_RAMECC_CLEAR_FLAG(ecc_handles[i], RAMECC_FLAGS_ALL); + if (!ecc_enabled[i]) continue; + (void)HAL_RAMECC_StartMonitor(ecc_handles[i]); + } +} + +/* Threshold of double-bit errors per boot that triggers a clean reset. + * Counter is per-session (zeroed by C runtime each boot) so that the + * persistent .noinit ecc_dbe_count can keep accumulating across resets + * without instantly re-triggering the reset path on the next boot. */ +#define ECC_DBE_RESET_THRESHOLD 10u +static uint32_t s_ecc_dbe_session; + +static void ecc_poll(void) +{ + for (uint32_t i = 0; i < ECC_MON_COUNT; i++) { + if (!ecc_enabled[i]) continue; + RAMECC_HandleTypeDef *h = ecc_handles[i]; + uint32_t sr = h->Instance->SR; + if (sr == 0u) continue; + + uint32_t addr = HAL_RAMECC_GetFailingAddress(h); + if (sr & RAMECC_SR_SEDCF) { + s_persist.ecc_sbe_count++; + } + bool dbe = (sr & (RAMECC_SR_DEDF | RAMECC_SR_DEBWDF)) != 0u; + if (dbe) { + s_persist.ecc_dbe_count++; + s_ecc_dbe_session++; + } + s_persist.ecc_last_addr = addr; + s_persist.ecc_last_monitor = i; + + if (s_ecc_log_count[i] < ECC_LOG_LIMIT) { + s_ecc_log_count[i]++; + printf("[ECC] %s SR=0x%08lx @0x%08lx (SBE=%lu DBE=%lu)%s\r\n", + ecc_names[i], (unsigned long)sr, (unsigned long)addr, + (unsigned long)s_persist.ecc_sbe_count, + (unsigned long)s_persist.ecc_dbe_count, + (s_ecc_log_count[i] == ECC_LOG_LIMIT) ? " [silencing]" : ""); + } + + __HAL_RAMECC_CLEAR_FLAG(h, RAMECC_FLAGS_ALL); + + if (dbe && s_ecc_dbe_session >= ECC_DBE_RESET_THRESHOLD) { + printf("[ECC] DBE threshold reached (%lu this boot, %lu total) - resetting\r\n", + (unsigned long)s_ecc_dbe_session, + (unsigned long)s_persist.ecc_dbe_count); + /* Flush UART before pulling the trigger. */ + for (volatile uint32_t d = 0; d < 200000u; d++) { __NOP(); } + __DSB(); + NVIC_SystemReset(); + } + } +} + +/* ------------------------------------------------------------------ */ +/* DMA transfer-error scan */ +/* ------------------------------------------------------------------ */ +/* DMA1/DMA2 streams: TEIFx bit positions in LISR/HISR (streams 0..3 / 4..7). + * Same bit layout in LIFCR/HIFCR for clear. */ +static const uint8_t s_te_bit_lo[4] = { 3, 11, 19, 27 }; +static const uint8_t s_fe_bit_lo[4] = { 0, 6, 16, 22 }; + +static void scan_dma_stream_bank(DMA_TypeDef *dma, bool high, uint8_t stream_base, + const char *name) +{ + volatile uint32_t * const isr = high ? &dma->HISR : &dma->LISR; + volatile uint32_t * const ifc = high ? &dma->HIFCR : &dma->LIFCR; + uint32_t v = *isr; + if (v == 0u) return; + + for (uint32_t s = 0; s < 4; s++) { + uint32_t te = 1u << s_te_bit_lo[s]; + uint32_t fe = 1u << s_fe_bit_lo[s]; + if (v & te) { + s_persist.dma_err_count++; + printf("[DMA] %s stream%lu transfer-error\r\n", + name, (unsigned long)(stream_base + s)); + *ifc = te; + } + if (v & fe) { + /* FIFO errors are common during legitimate operation; only count, + * don't spam. */ + *ifc = fe; + } + } +} + +static void scan_bdma(void) +{ + uint32_t v = BDMA->ISR; + if (v == 0u) return; + for (uint32_t ch = 0; ch < 8; ch++) { + uint32_t te = 1u << (3u + 4u * ch); /* TEIFx */ + if (v & te) { + s_persist.dma_err_count++; + printf("[DMA] BDMA ch%lu transfer-error\r\n", (unsigned long)ch); + BDMA->IFCR = te; + } + } +} + +#define DMA_POLL_MS 1000u + +void system_monitor_poll(void) +{ + /* Periodic ECC and DMA error-flag scan */ + static uint32_t last_ms = 0; + uint32_t now = HAL_GetTick(); + if ((now - last_ms) < DMA_POLL_MS) return; + last_ms = now; + + ecc_poll(); + + scan_dma_stream_bank(DMA1, false, 0, "DMA1"); + scan_dma_stream_bank(DMA1, true, 4, "DMA1"); + scan_dma_stream_bank(DMA2, false, 0, "DMA2"); + scan_dma_stream_bank(DMA2, true, 4, "DMA2"); + scan_bdma(); +} diff --git a/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_iwdg.h b/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_iwdg.h new file mode 100644 index 0000000..895c33b --- /dev/null +++ b/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_iwdg.h @@ -0,0 +1,237 @@ +/** + ****************************************************************************** + * @file stm32h7xx_hal_iwdg.h + * @author MCD Application Team + * @brief Header file of IWDG HAL module. + ****************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32H7xx_HAL_IWDG_H +#define STM32H7xx_HAL_IWDG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32h7xx_hal_def.h" + +/** @addtogroup STM32H7xx_HAL_Driver + * @{ + */ + +/** @defgroup IWDG IWDG + * @{ + */ + +/* Exported types ------------------------------------------------------------*/ +/** @defgroup IWDG_Exported_Types IWDG Exported Types + * @{ + */ + +/** + * @brief IWDG Init structure definition + */ +typedef struct +{ + uint32_t Prescaler; /*!< Select the prescaler of the IWDG. + This parameter can be a value of @ref IWDG_Prescaler */ + + uint32_t Reload; /*!< Specifies the IWDG down-counter reload value. + This parameter must be a number between Min_Data = 0 and Max_Data = 0x0FFF */ + + uint32_t Window; /*!< Specifies the window value to be compared to the down-counter. + This parameter must be a number between Min_Data = 0 and Max_Data = 0x0FFF */ + +} IWDG_InitTypeDef; + +/** + * @brief IWDG Handle Structure definition + */ +typedef struct +{ + IWDG_TypeDef *Instance; /*!< Register base address */ + + IWDG_InitTypeDef Init; /*!< IWDG required parameters */ +} IWDG_HandleTypeDef; + + +/** + * @} + */ + +/* Exported constants --------------------------------------------------------*/ +/** @defgroup IWDG_Exported_Constants IWDG Exported Constants + * @{ + */ + +/** @defgroup IWDG_Prescaler IWDG Prescaler + * @{ + */ +#define IWDG_PRESCALER_4 0x00000000u /*!< IWDG prescaler set to 4 */ +#define IWDG_PRESCALER_8 IWDG_PR_PR_0 /*!< IWDG prescaler set to 8 */ +#define IWDG_PRESCALER_16 IWDG_PR_PR_1 /*!< IWDG prescaler set to 16 */ +#define IWDG_PRESCALER_32 (IWDG_PR_PR_1 | IWDG_PR_PR_0) /*!< IWDG prescaler set to 32 */ +#define IWDG_PRESCALER_64 IWDG_PR_PR_2 /*!< IWDG prescaler set to 64 */ +#define IWDG_PRESCALER_128 (IWDG_PR_PR_2 | IWDG_PR_PR_0) /*!< IWDG prescaler set to 128 */ +#define IWDG_PRESCALER_256 (IWDG_PR_PR_2 | IWDG_PR_PR_1) /*!< IWDG prescaler set to 256 */ +/** + * @} + */ + +/** @defgroup IWDG_Window_option IWDG Window option + * @{ + */ +#define IWDG_WINDOW_DISABLE IWDG_WINR_WIN +/** + * @} + */ + +/** + * @} + */ + +/* Exported macros -----------------------------------------------------------*/ +/** @defgroup IWDG_Exported_Macros IWDG Exported Macros + * @{ + */ + +/** + * @brief Enable the IWDG peripheral. + * @param __HANDLE__ IWDG handle + * @retval None + */ +#define __HAL_IWDG_START(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_ENABLE) + +/** + * @brief Reload IWDG counter with value defined in the reload register + * (write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers disabled). + * @param __HANDLE__ IWDG handle + * @retval None + */ +#define __HAL_IWDG_RELOAD_COUNTER(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_RELOAD) + +/** + * @} + */ + +/* Exported functions --------------------------------------------------------*/ +/** @defgroup IWDG_Exported_Functions IWDG Exported Functions + * @{ + */ + +/** @defgroup IWDG_Exported_Functions_Group1 Initialization and Start functions + * @{ + */ +/* Initialization/Start functions ********************************************/ +HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg); +/** + * @} + */ + +/** @defgroup IWDG_Exported_Functions_Group2 IO operation functions + * @{ + */ +/* I/O operation functions ****************************************************/ +HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg); +/** + * @} + */ + +/** + * @} + */ + +/* Private constants ---------------------------------------------------------*/ +/** @defgroup IWDG_Private_Constants IWDG Private Constants + * @{ + */ + +/** + * @brief IWDG Key Register BitMask + */ +#define IWDG_KEY_RELOAD 0x0000AAAAu /*!< IWDG Reload Counter Enable */ +#define IWDG_KEY_ENABLE 0x0000CCCCu /*!< IWDG Peripheral Enable */ +#define IWDG_KEY_WRITE_ACCESS_ENABLE 0x00005555u /*!< IWDG KR Write Access Enable */ +#define IWDG_KEY_WRITE_ACCESS_DISABLE 0x00000000u /*!< IWDG KR Write Access Disable */ + +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ +/** @defgroup IWDG_Private_Macros IWDG Private Macros + * @{ + */ + +/** + * @brief Enable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers. + * @param __HANDLE__ IWDG handle + * @retval None + */ +#define IWDG_ENABLE_WRITE_ACCESS(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_WRITE_ACCESS_ENABLE) + +/** + * @brief Disable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers. + * @param __HANDLE__ IWDG handle + * @retval None + */ +#define IWDG_DISABLE_WRITE_ACCESS(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_WRITE_ACCESS_DISABLE) + +/** + * @brief Check IWDG prescaler value. + * @param __PRESCALER__ IWDG prescaler value + * @retval None + */ +#define IS_IWDG_PRESCALER(__PRESCALER__) (((__PRESCALER__) == IWDG_PRESCALER_4) || \ + ((__PRESCALER__) == IWDG_PRESCALER_8) || \ + ((__PRESCALER__) == IWDG_PRESCALER_16) || \ + ((__PRESCALER__) == IWDG_PRESCALER_32) || \ + ((__PRESCALER__) == IWDG_PRESCALER_64) || \ + ((__PRESCALER__) == IWDG_PRESCALER_128)|| \ + ((__PRESCALER__) == IWDG_PRESCALER_256)) + +/** + * @brief Check IWDG reload value. + * @param __RELOAD__ IWDG reload value + * @retval None + */ +#define IS_IWDG_RELOAD(__RELOAD__) ((__RELOAD__) <= IWDG_RLR_RL) + +/** + * @brief Check IWDG window value. + * @param __WINDOW__ IWDG window value + * @retval None + */ +#define IS_IWDG_WINDOW(__WINDOW__) ((__WINDOW__) <= IWDG_WINR_WIN) + + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif /* STM32H7xx_HAL_IWDG_H */ diff --git a/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_ramecc.h b/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_ramecc.h new file mode 100644 index 0000000..b681c8d --- /dev/null +++ b/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_ramecc.h @@ -0,0 +1,384 @@ +/** + ****************************************************************************** + * @file stm32h7xx_hal_ramecc.h + * @author MCD Application Team + * @brief Header file of RAMECC HAL module. + ****************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32H7xx_HAL_RAMECC_H +#define STM32H7xx_HAL_RAMECC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32h7xx_hal_def.h" + +/** @addtogroup STM32H7xx_HAL_Driver + * @{ + */ + +/** @addtogroup RAMECC + * @{ + */ + +/* Exported types ------------------------------------------------------------*/ + +/** @defgroup RAMECC_Exported_Types RAMECC Exported Types + * @brief RAMECC Exported Types + * @{ + */ + +/** + * @brief HAL RAMECC State structures definition + */ +typedef enum +{ + HAL_RAMECC_STATE_RESET = 0x00U, /*!< RAMECC not yet initialized or disabled */ + HAL_RAMECC_STATE_READY = 0x01U, /*!< RAMECC initialized and ready for use */ + HAL_RAMECC_STATE_BUSY = 0x02U, /*!< RAMECC process is ongoing */ + HAL_RAMECC_STATE_ERROR = 0x03U, /*!< RAMECC error state */ +}HAL_RAMECC_StateTypeDef; + + +/** + * @brief RAMECC handle Structure definition + */ +#if (USE_HAL_RAMECC_REGISTER_CALLBACKS == 1) +typedef struct __RAMECC_HandleTypeDef +#else +typedef struct +#endif /* USE_HAL_RAMECC_REGISTER_CALLBACKS */ +{ + RAMECC_MonitorTypeDef *Instance; /*!< Register base address */ + __IO HAL_RAMECC_StateTypeDef State; /*!< RAMECC state */ + __IO uint32_t ErrorCode; /*!< RAMECC Error Code */ + __IO uint32_t RAMECCErrorCode; /*!< RAMECC Detected Error Code */ +#if (USE_HAL_RAMECC_REGISTER_CALLBACKS == 1) + void (* DetectErrorCallback)( struct __RAMECC_HandleTypeDef *hramecc); /*!< RAMECC Error Detect callback */ +#endif /* USE_HAL_RAMECC_REGISTER_CALLBACKS */ +}RAMECC_HandleTypeDef; + +/** + * @} + */ + + +/* Exported constants --------------------------------------------------------*/ +/** @defgroup RAMECC_Exported_Constants RAMECC Exported Constants + * @{ + */ +/** @defgroup RAMECC_Error_Codes RAMECC Error Codes + * @{ + */ +#define HAL_RAMECC_ERROR_NONE 0x00000000U /*!< RAMECC No Error */ +#define HAL_RAMECC_ERROR_TIMEOUT 0x00000001U /*!< RAMECC Timeout Error */ +#define HAL_RAMECC_ERROR_BUSY 0x00000002U /*!< RAMECC Busy Error */ +#if (USE_HAL_RAMECC_REGISTER_CALLBACKS == 1) +#define HAL_RAMECC_ERROR_INVALID_CALLBACK 0x00000003U /*!< Invalid Callback error */ +#endif /* USE_HAL_RAMECC_REGISTER_CALLBACKS */ + +/** + * @} + */ + +/** @defgroup RAMECC_Error_Codes RAMECC Error Detected Codes + * @{ + */ +#define HAL_RAMECC_NO_ERROR 0x00000000U /*!< RAMECC No Error Detected */ +#define HAL_RAMECC_SINGLEERROR_DETECTED 0x00000001U /*!< RAMECC Single Error Detected */ +#define HAL_RAMECC_DOUBLEERROR_DETECTED 0x00000002U /*!< RAMECC Double Error Detected */ +/** + * @} + */ + +/** @defgroup RAMECC_Interrupt RAMECC interrupts + * @{ + */ +#define RAMECC_IT_GLOBAL_ID 0x10000000UL +#define RAMECC_IT_MONITOR_ID 0x20000000UL + +#define RAMECC_IT_GLOBAL_ENABLE (RAMECC_IT_GLOBAL_ID | RAMECC_IER_GIE) +#define RAMECC_IT_GLOBAL_SINGLEERR_R (RAMECC_IT_GLOBAL_ID | RAMECC_IER_GECCSEIE) +#define RAMECC_IT_GLOBAL_DOUBLEERR_R (RAMECC_IT_GLOBAL_ID | RAMECC_IER_GECCDEIE) +#define RAMECC_IT_GLOBAL_DOUBLEERR_W (RAMECC_IT_GLOBAL_ID | RAMECC_IER_GECCDEBWIE) +#define RAMECC_IT_GLOBAL_ALL (RAMECC_IT_GLOBAL_ID | RAMECC_IER_GIE | RAMECC_IER_GECCSEIE | RAMECC_IER_GECCDEIE | RAMECC_IER_GECCDEBWIE) + + +#define RAMECC_IT_MONITOR_SINGLEERR_R (RAMECC_IT_MONITOR_ID | RAMECC_CR_ECCSEIE) +#define RAMECC_IT_MONITOR_DOUBLEERR_R (RAMECC_IT_MONITOR_ID | RAMECC_CR_ECCDEIE) +#define RAMECC_IT_MONITOR_DOUBLEERR_W (RAMECC_IT_MONITOR_ID | RAMECC_CR_ECCDEBWIE) +#define RAMECC_IT_MONITOR_ALL (RAMECC_IT_MONITOR_ID | RAMECC_CR_ECCDEBWIE | RAMECC_CR_ECCDEIE | RAMECC_CR_ECCSEIE) +/** + * @} + */ + +/** @defgroup RAMECC_FLAG RAMECC Monitor flags + * @{ + */ +#define RAMECC_FLAG_SINGLEERR_R RAMECC_SR_SEDCF +#define RAMECC_FLAG_DOUBLEERR_R RAMECC_SR_DEDF +#define RAMECC_FLAG_DOUBLEERR_W RAMECC_SR_DEBWDF +#define RAMECC_FLAGS_ALL (RAMECC_SR_SEDCF | RAMECC_SR_DEDF | RAMECC_SR_DEBWDF) + +/** + * @} + */ +/** + * @} + */ + +/* Exported macro ------------------------------------------------------------*/ +/** @defgroup RAMECC_Exported_Macros RAMECC Exported Macros + * @{ + */ + +#define __HAL_RAMECC_ENABLE_GLOBAL_IT(__HANDLE__, __INTERRUPT__) ((((RAMECC_TypeDef *)((uint32_t)(__HANDLE__)->Instance & 0xFFFFFF00U))->IER) |= ((__INTERRUPT__) & ~RAMECC_IT_GLOBAL_ID)) +#define __HAL_RAMECC_ENABLE_MONITOR_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)->Instance->CR |= ((__INTERRUPT__) & ~RAMECC_IT_MONITOR_ID)) + +/** + * @brief Enable the specified RAMECC interrupts. + * @param __HANDLE__ : RAMECC handle. + * @param __INTERRUPT__: specifies the RAMECC interrupt sources to be enabled or disabled. + * This parameter can be one of the following values: + * @arg RAMECC_IT_GLOBAL_ENABLE : Global interrupt enable mask. + * @arg RAMECC_IT_GLOBAL_SINGLEERR_R : Global ECC single error interrupt enable. + * @arg RAMECC_IT_GLOBAL_DOUBLEERR_R : Global ECC double error interrupt enable. + * @arg RAMECC_IT_GLOBAL_DOUBLEERR_W : Global ECC double error on byte write (BW) interrupt enable. + * @arg RAMECC_IT_GLOBAL_ALL : All Global ECC interrupts enable mask. + * @arg RAMECC_IT_MONITOR_SINGLEERR_R : Monitor ECC single error interrupt enable. + * @arg RAMECC_IT_MONITOR_DOUBLEERR_R : Monitor ECC double error interrupt enable. + * @arg RAMECC_IT_MONITOR_DOUBLEERR_W : Monitor ECC double error on byte write (BW) interrupt enable. + * @arg RAMECC_IT_MONITOR_ALL : All Monitor ECC interrupts enable mask. + * @retval None + */ +#define __HAL_RAMECC_ENABLE_IT(__HANDLE__, __INTERRUPT__) ( \ +(IS_RAMECC_GLOBAL_INTERRUPT(__INTERRUPT__)) ? (__HAL_RAMECC_ENABLE_GLOBAL_IT((__HANDLE__), (__INTERRUPT__))) :\ +(__HAL_RAMECC_ENABLE_MONITOR_IT((__HANDLE__), (__INTERRUPT__)))) + + +#define __HAL_RAMECC_DISABLE_GLOBAL_IT(__HANDLE__, __INTERRUPT__) ((((RAMECC_TypeDef *)((uint32_t)(__HANDLE__)->Instance & 0xFFFFFF00U))->IER) &= ~((__INTERRUPT__) & ~RAMECC_IT_GLOBAL_ID)) +#define __HAL_RAMECC_DISABLE_MONITOR_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)->Instance->CR &= ~((__INTERRUPT__) & ~RAMECC_IT_MONITOR_ID)) + +/** + * @brief Disable the specified RAMECC interrupts. + * @param __HANDLE__ : RAMECC handle. + * @param __INTERRUPT__: specifies the RAMECC interrupt sources to be enabled or disabled. + * This parameter can be one of the following values: + * @arg RAMECC_IT_GLOBAL_ENABLE : Global interrupt enable mask. + * @arg RAMECC_IT_GLOBAL_SINGLEERR_R : Global ECC single error interrupt enable. + * @arg RAMECC_IT_GLOBAL_DOUBLEERR_R : Global ECC double error interrupt enable. + * @arg RAMECC_IT_GLOBAL_DOUBLEERR_W : Global ECC double error on byte write (BW) interrupt enable. + * @arg RAMECC_IT_GLOBAL_ALL : All Global ECC interrupts enable mask. + * @arg RAMECC_IT_MONITOR_SINGLEERR_R : Monitor ECC single error interrupt enable. + * @arg RAMECC_IT_MONITOR_DOUBLEERR_R : Monitor ECC double error interrupt enable. + * @arg RAMECC_IT_MONITOR_DOUBLEERR_W : Monitor ECC double error on byte write (BW) interrupt enable. + * @arg RAMECC_IT_MONITOR_ALL : All Monitor ECC interrupts enable mask. + * @retval None + */ +#define __HAL_RAMECC_DISABLE_IT(__HANDLE__, __INTERRUPT__) ( \ +(IS_RAMECC_GLOBAL_INTERRUPT(__INTERRUPT__)) ? (__HAL_RAMECC_DISABLE_GLOBAL_IT((__HANDLE__), (__INTERRUPT__))) :\ +(__HAL_RAMECC_DISABLE_MONITOR_IT((__HANDLE__), (__INTERRUPT__)))) + + +#define __HAL_RAMECC_GET_GLOBAL_IT_SOURCE(__HANDLE__, __INTERRUPT__) (((((RAMECC_TypeDef *)((uint32_t)(__HANDLE__)->Instance & 0xFFFFFF00U))->IER) & ((__INTERRUPT__) & ~RAMECC_IT_GLOBAL_ID)) ? SET : RESET) +#define __HAL_RAMECC_GET_MONITOR_IT_SOURCE(__HANDLE__, __INTERRUPT__) ((((__HANDLE__)->Instance->CR) & ((__INTERRUPT__) & ~RAMECC_IT_GLOBAL_ID)) ? SET : RESET) + +/** + * @brief Check whether the specified RAMECC interrupt source is enabled or not. + * @param __HANDLE__ : Specifies the RAMECC Handle. + * @param __INTERRUPT__ : Specifies the RAMECC interrupt source to check. + * This parameter can be one of the following values: + * @arg RAMECC_IT_GLOBAL_ENABLE : Global interrupt enable mask. + * @arg RAMECC_IT_GLOBAL_SINGLEERR_R : Global ECC single error interrupt enable. + * @arg RAMECC_IT_GLOBAL_DOUBLEERR_R : Global ECC double error interrupt enable. + * @arg RAMECC_IT_GLOBAL_DOUBLEERR_W : Global ECC double error on byte write (BW) interrupt enable. + * @arg RAMECC_IT_GLOBAL_ALL : All Global ECC interrupts enable mask. + * @arg RAMECC_IT_MONITOR_SINGLEERR_R : Monitor ECC single error interrupt enable. + * @arg RAMECC_IT_MONITOR_DOUBLEERR_R : Monitor ECC double error interrupt enable. + * @arg RAMECC_IT_MONITOR_DOUBLEERR_W : Monitor ECC double error on byte write (BW) interrupt enable. + * @arg RAMECC_IT_MONITOR_ALL : All Monitor ECC interrupts enable mask. + * @retval The new state of __INTERRUPT__ (SET or RESET). + */ +#define __HAL_RAMECC_GET_IT_SOURCE(__HANDLE__, __INTERRUPT__) ( \ +(IS_RAMECC_GLOBAL_INTERRUPT(__INTERRUPT__)) ? (__HAL_RAMECC_GET_GLOBAL_IT_SOURCE((__HANDLE__), (__INTERRUPT__))) :\ +(__HAL_RAMECC_GET_MONITOR_IT_SOURCE((__HANDLE__), (__INTERRUPT__)))) + + +/** + * @brief Get the RAMECC pending flags. + * @param __HANDLE__ : RAMECC handle. + * @param __FLAG__ : specifies the flag to clear. + * This parameter can be any combination of the following values: + * @arg RAMECC_FLAG_SINGLEERR_R : RAMECC instance ECC single error detected and corrected flag. + * @arg RAMECC_FLAG_DOUBLEERR_R : RAMECC instance ECC double error detected flag. + * @arg RAMECC_FLAG_DOUBLEERR_W : RAMECC instance ECC double error on byte write (BW) detected flag. + * @arg RAMECC_FLAGS_ALL : RAMECC instance all flag. + * @retval The state of __FLAG__ (SET or RESET). + */ +#define __HAL_RAMECC_GET_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->SR &= (__FLAG__)) + + +/** + * @brief Clear the RAMECC pending flags. + * @param __HANDLE__ : RAMECC handle. + * @param __FLAG__ : specifies the flag to clear. + * This parameter can be any combination of the following values: + * @arg RAMECC_FLAG_SINGLEERR_R : RAMECC instance ECC single error detected and corrected flag. + * @arg RAMECC_FLAG_DOUBLEERR_R : RAMECC instance ECC double error detected flag. + * @arg RAMECC_FLAG_DOUBLEERR_W : RAMECC instance ECC double error on byte write (BW) detected flag. + * @arg RAMECC_FLAGS_ALL : RAMECC instance all flag. + * @retval None. + */ +#define __HAL_RAMECC_CLEAR_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->SR &= ~(__FLAG__)) + +/** + * @brief Reset the RAMECC handle state. + * @param __HANDLE__ : Specifies the RAMECC Handle. + * @retval None. + */ +#define __HAL_RAMECC_RESET_HANDLE_STATE(__HANDLE__) ((__HANDLE__)->State = HAL_RAMECC_STATE_RESET) +/** + * @} + */ + +/* Exported functions --------------------------------------------------------*/ + +/** @defgroup RAMECC_Exported_Functions RAMECC Exported Functions + * @brief RAMECC Exported functions + * @{ + */ + +/** @defgroup RAMECC_Exported_Functions_Group1 Initialization and de-initialization functions + * @brief Initialization and de-initialization functions + * @{ + */ +HAL_StatusTypeDef HAL_RAMECC_Init(RAMECC_HandleTypeDef *hramecc); +HAL_StatusTypeDef HAL_RAMECC_DeInit(RAMECC_HandleTypeDef *hramecc); +/** + * @} + */ + +/** @defgroup RAMECC_Exported_Functions_Group2 monitoring operation functions + * @brief monitoring operation functions + * @{ + */ +HAL_StatusTypeDef HAL_RAMECC_StartMonitor(RAMECC_HandleTypeDef *hramecc); +HAL_StatusTypeDef HAL_RAMECC_StopMonitor(RAMECC_HandleTypeDef *hramecc); +HAL_StatusTypeDef HAL_RAMECC_EnableNotification(RAMECC_HandleTypeDef *hramecc, uint32_t Notifications); +HAL_StatusTypeDef HAL_RAMECC_DisableNotification(RAMECC_HandleTypeDef *hramecc, uint32_t Notifications); + +/** + * @} + */ + +/** @defgroup RAMECC_Exported_Functions_Group3 handle Interrupt and Callbacks Functions + * @brief handle Interrupt and Callbacks Functions + * @{ + */ +void HAL_RAMECC_IRQHandler(RAMECC_HandleTypeDef *hramecc); +void HAL_RAMECC_DetectErrorCallback(RAMECC_HandleTypeDef *hramecc); +#if (USE_HAL_RAMECC_REGISTER_CALLBACKS == 1) +HAL_StatusTypeDef HAL_RAMECC_RegisterCallback(RAMECC_HandleTypeDef *hramecc, void (* pCallback)(RAMECC_HandleTypeDef *_hramecc)); +HAL_StatusTypeDef HAL_RAMECC_UnRegisterCallback(RAMECC_HandleTypeDef *hramecc); +#endif /* USE_HAL_RAMECC_REGISTER_CALLBACKS */ +/** + * @} + */ + +/** @defgroup RAMECC_Exported_Functions_Group4 Error information functions + * @brief Error information functions + * @{ + */ +uint32_t HAL_RAMECC_GetFailingAddress(const RAMECC_HandleTypeDef *hramecc); +uint32_t HAL_RAMECC_GetFailingDataLow(const RAMECC_HandleTypeDef *hramecc); +uint32_t HAL_RAMECC_GetFailingDataHigh(const RAMECC_HandleTypeDef *hramecc); +uint32_t HAL_RAMECC_GetHammingErrorCode(const RAMECC_HandleTypeDef *hramecc); +uint32_t HAL_RAMECC_IsECCSingleErrorDetected(const RAMECC_HandleTypeDef *hramecc); +uint32_t HAL_RAMECC_IsECCDoubleErrorDetected(const RAMECC_HandleTypeDef *hramecc); +/** + * @} + */ + +/** @defgroup RAMECC_Exported_Functions_Group5 State and Error Functions + * @brief State and Error Functions + * @{ + */ +HAL_RAMECC_StateTypeDef HAL_RAMECC_GetState(const RAMECC_HandleTypeDef *hramecc); +uint32_t HAL_RAMECC_GetError(const RAMECC_HandleTypeDef *hramecc); +uint32_t HAL_RAMECC_GetRAMECCError(const RAMECC_HandleTypeDef *hramecc); +/** + * @} + */ + +/** + * @} + */ +/* Private Constants -------------------------------------------------------------*/ +/** @defgroup RAMECC_Private_Constants RAMECC Private Constants + * @brief RAMECC private defines and constants + * @{ + */ +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ +/** @defgroup RAMECC_Private_Macros RAMECC Private Macros + * @brief RAMECC private macros + * @{ + */ + +#define IS_RAMECC_GLOBAL_INTERRUPT(INTERRUPT) (((INTERRUPT) == RAMECC_IT_GLOBAL_ENABLE) || \ + ((INTERRUPT) == RAMECC_IT_GLOBAL_SINGLEERR_R) || \ + ((INTERRUPT) == RAMECC_IT_GLOBAL_DOUBLEERR_R) || \ + ((INTERRUPT) == RAMECC_IT_GLOBAL_DOUBLEERR_W) || \ + ((INTERRUPT) == RAMECC_IT_GLOBAL_ALL)) + + +#define IS_RAMECC_MONITOR_INTERRUPT(INTERRUPT) (((INTERRUPT) == RAMECC_IT_MONITOR_SINGLEERR_R) || \ + ((INTERRUPT) == RAMECC_IT_MONITOR_DOUBLEERR_R) || \ + ((INTERRUPT) == RAMECC_IT_MONITOR_DOUBLEERR_W) || \ + ((INTERRUPT) == RAMECC_IT_MONITOR_ALL)) + +#define IS_RAMECC_INTERRUPT(INTERRUPT) ((IS_RAMECC_GLOBAL_INTERRUPT(INTERRUPT)) || \ + (IS_RAMECC_MONITOR_INTERRUPT(INTERRUPT))) + +/** + * @} + */ + +/* Private functions ---------------------------------------------------------*/ +/** @defgroup RAMECC_Private_Functions RAMECC Private Functions + * @brief RAMECC private functions + * @{ + */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ +#ifdef __cplusplus +} +#endif + +#endif /* STM32H7xx_HAL_RAMECC_H */ diff --git a/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_ll_iwdg.h b/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_ll_iwdg.h new file mode 100644 index 0000000..743c917 --- /dev/null +++ b/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_ll_iwdg.h @@ -0,0 +1,338 @@ +/** + ****************************************************************************** + * @file stm32h7xx_ll_iwdg.h + * @author MCD Application Team + * @brief Header file of IWDG LL module. + ****************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32H7xx_LL_IWDG_H +#define STM32H7xx_LL_IWDG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32h7xx.h" + +/** @addtogroup STM32H7xx_LL_Driver + * @{ + */ + +#if defined(IWDG1) || defined(IWDG2) + +/** @defgroup IWDG_LL IWDG + * @{ + */ + +/* Private types -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ + +/* Private constants ---------------------------------------------------------*/ +/** @defgroup IWDG_LL_Private_Constants IWDG Private Constants + * @{ + */ +#define LL_IWDG_KEY_RELOAD 0x0000AAAAU /*!< IWDG Reload Counter Enable */ +#define LL_IWDG_KEY_ENABLE 0x0000CCCCU /*!< IWDG Peripheral Enable */ +#define LL_IWDG_KEY_WR_ACCESS_ENABLE 0x00005555U /*!< IWDG KR Write Access Enable */ +#define LL_IWDG_KEY_WR_ACCESS_DISABLE 0x00000000U /*!< IWDG KR Write Access Disable */ +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/** @defgroup IWDG_LL_Exported_Constants IWDG Exported Constants + * @{ + */ + +/** @defgroup IWDG_LL_EC_GET_FLAG Get Flags Defines + * @brief Flags defines which can be used with LL_IWDG_ReadReg function + * @{ + */ +#define LL_IWDG_SR_PVU IWDG_SR_PVU /*!< Watchdog prescaler value update */ +#define LL_IWDG_SR_RVU IWDG_SR_RVU /*!< Watchdog counter reload value update */ +#define LL_IWDG_SR_WVU IWDG_SR_WVU /*!< Watchdog counter window value update */ +/** + * @} + */ + +/** @defgroup IWDG_LL_EC_PRESCALER Prescaler Divider + * @{ + */ +#define LL_IWDG_PRESCALER_4 0x00000000U /*!< Divider by 4 */ +#define LL_IWDG_PRESCALER_8 (IWDG_PR_PR_0) /*!< Divider by 8 */ +#define LL_IWDG_PRESCALER_16 (IWDG_PR_PR_1) /*!< Divider by 16 */ +#define LL_IWDG_PRESCALER_32 (IWDG_PR_PR_1 | IWDG_PR_PR_0) /*!< Divider by 32 */ +#define LL_IWDG_PRESCALER_64 (IWDG_PR_PR_2) /*!< Divider by 64 */ +#define LL_IWDG_PRESCALER_128 (IWDG_PR_PR_2 | IWDG_PR_PR_0) /*!< Divider by 128 */ +#define LL_IWDG_PRESCALER_256 (IWDG_PR_PR_2 | IWDG_PR_PR_1) /*!< Divider by 256 */ +/** + * @} + */ + +/** + * @} + */ + +/* Exported macro ------------------------------------------------------------*/ +/** @defgroup IWDG_LL_Exported_Macros IWDG Exported Macros + * @{ + */ + +/** @defgroup IWDG_LL_EM_WRITE_READ Common Write and read registers Macros + * @{ + */ + +/** + * @brief Write a value in IWDG register + * @param __INSTANCE__ IWDG Instance + * @param __REG__ Register to be written + * @param __VALUE__ Value to be written in the register + * @retval None + */ +#define LL_IWDG_WriteReg(__INSTANCE__, __REG__, __VALUE__) WRITE_REG(__INSTANCE__->__REG__, (__VALUE__)) + +/** + * @brief Read a value in IWDG register + * @param __INSTANCE__ IWDG Instance + * @param __REG__ Register to be read + * @retval Register value + */ +#define LL_IWDG_ReadReg(__INSTANCE__, __REG__) READ_REG(__INSTANCE__->__REG__) +/** + * @} + */ + +/** + * @} + */ + + +/* Exported functions --------------------------------------------------------*/ +/** @defgroup IWDG_LL_Exported_Functions IWDG Exported Functions + * @{ + */ +/** @defgroup IWDG_LL_EF_Configuration Configuration + * @{ + */ + +/** + * @brief Start the Independent Watchdog + * @note Except if the hardware watchdog option is selected + * @rmtoll KR KEY LL_IWDG_Enable + * @param IWDGx IWDG Instance + * @retval None + */ +__STATIC_INLINE void LL_IWDG_Enable(IWDG_TypeDef *IWDGx) +{ + WRITE_REG(IWDGx->KR, LL_IWDG_KEY_ENABLE); +} + +/** + * @brief Reloads IWDG counter with value defined in the reload register + * @rmtoll KR KEY LL_IWDG_ReloadCounter + * @param IWDGx IWDG Instance + * @retval None + */ +__STATIC_INLINE void LL_IWDG_ReloadCounter(IWDG_TypeDef *IWDGx) +{ + WRITE_REG(IWDGx->KR, LL_IWDG_KEY_RELOAD); +} + +/** + * @brief Enable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers + * @rmtoll KR KEY LL_IWDG_EnableWriteAccess + * @param IWDGx IWDG Instance + * @retval None + */ +__STATIC_INLINE void LL_IWDG_EnableWriteAccess(IWDG_TypeDef *IWDGx) +{ + WRITE_REG(IWDGx->KR, LL_IWDG_KEY_WR_ACCESS_ENABLE); +} + +/** + * @brief Disable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers + * @rmtoll KR KEY LL_IWDG_DisableWriteAccess + * @param IWDGx IWDG Instance + * @retval None + */ +__STATIC_INLINE void LL_IWDG_DisableWriteAccess(IWDG_TypeDef *IWDGx) +{ + WRITE_REG(IWDGx->KR, LL_IWDG_KEY_WR_ACCESS_DISABLE); +} + +/** + * @brief Select the prescaler of the IWDG + * @rmtoll PR PR LL_IWDG_SetPrescaler + * @param IWDGx IWDG Instance + * @param Prescaler This parameter can be one of the following values: + * @arg @ref LL_IWDG_PRESCALER_4 + * @arg @ref LL_IWDG_PRESCALER_8 + * @arg @ref LL_IWDG_PRESCALER_16 + * @arg @ref LL_IWDG_PRESCALER_32 + * @arg @ref LL_IWDG_PRESCALER_64 + * @arg @ref LL_IWDG_PRESCALER_128 + * @arg @ref LL_IWDG_PRESCALER_256 + * @retval None + */ +__STATIC_INLINE void LL_IWDG_SetPrescaler(IWDG_TypeDef *IWDGx, uint32_t Prescaler) +{ + WRITE_REG(IWDGx->PR, IWDG_PR_PR & Prescaler); +} + +/** + * @brief Get the selected prescaler of the IWDG + * @rmtoll PR PR LL_IWDG_GetPrescaler + * @param IWDGx IWDG Instance + * @retval Returned value can be one of the following values: + * @arg @ref LL_IWDG_PRESCALER_4 + * @arg @ref LL_IWDG_PRESCALER_8 + * @arg @ref LL_IWDG_PRESCALER_16 + * @arg @ref LL_IWDG_PRESCALER_32 + * @arg @ref LL_IWDG_PRESCALER_64 + * @arg @ref LL_IWDG_PRESCALER_128 + * @arg @ref LL_IWDG_PRESCALER_256 + */ +__STATIC_INLINE uint32_t LL_IWDG_GetPrescaler(const IWDG_TypeDef *IWDGx) +{ + return (READ_REG(IWDGx->PR)); +} + +/** + * @brief Specify the IWDG down-counter reload value + * @rmtoll RLR RL LL_IWDG_SetReloadCounter + * @param IWDGx IWDG Instance + * @param Counter Value between Min_Data=0 and Max_Data=0x0FFF + * @retval None + */ +__STATIC_INLINE void LL_IWDG_SetReloadCounter(IWDG_TypeDef *IWDGx, uint32_t Counter) +{ + WRITE_REG(IWDGx->RLR, IWDG_RLR_RL & Counter); +} + +/** + * @brief Get the specified IWDG down-counter reload value + * @rmtoll RLR RL LL_IWDG_GetReloadCounter + * @param IWDGx IWDG Instance + * @retval Value between Min_Data=0 and Max_Data=0x0FFF + */ +__STATIC_INLINE uint32_t LL_IWDG_GetReloadCounter(const IWDG_TypeDef *IWDGx) +{ + return (READ_REG(IWDGx->RLR)); +} + +/** + * @brief Specify high limit of the window value to be compared to the down-counter. + * @rmtoll WINR WIN LL_IWDG_SetWindow + * @param IWDGx IWDG Instance + * @param Window Value between Min_Data=0 and Max_Data=0x0FFF + * @retval None + */ +__STATIC_INLINE void LL_IWDG_SetWindow(IWDG_TypeDef *IWDGx, uint32_t Window) +{ + WRITE_REG(IWDGx->WINR, IWDG_WINR_WIN & Window); +} + +/** + * @brief Get the high limit of the window value specified. + * @rmtoll WINR WIN LL_IWDG_GetWindow + * @param IWDGx IWDG Instance + * @retval Value between Min_Data=0 and Max_Data=0x0FFF + */ +__STATIC_INLINE uint32_t LL_IWDG_GetWindow(const IWDG_TypeDef *IWDGx) +{ + return (READ_REG(IWDGx->WINR)); +} + +/** + * @} + */ + +/** @defgroup IWDG_LL_EF_FLAG_Management FLAG_Management + * @{ + */ + +/** + * @brief Check if flag Prescaler Value Update is set or not + * @rmtoll SR PVU LL_IWDG_IsActiveFlag_PVU + * @param IWDGx IWDG Instance + * @retval State of bit (1 or 0). + */ +__STATIC_INLINE uint32_t LL_IWDG_IsActiveFlag_PVU(const IWDG_TypeDef *IWDGx) +{ + return ((READ_BIT(IWDGx->SR, IWDG_SR_PVU) == (IWDG_SR_PVU)) ? 1UL : 0UL); +} + +/** + * @brief Check if flag Reload Value Update is set or not + * @rmtoll SR RVU LL_IWDG_IsActiveFlag_RVU + * @param IWDGx IWDG Instance + * @retval State of bit (1 or 0). + */ +__STATIC_INLINE uint32_t LL_IWDG_IsActiveFlag_RVU(const IWDG_TypeDef *IWDGx) +{ + return ((READ_BIT(IWDGx->SR, IWDG_SR_RVU) == (IWDG_SR_RVU)) ? 1UL : 0UL); +} + +/** + * @brief Check if flag Window Value Update is set or not + * @rmtoll SR WVU LL_IWDG_IsActiveFlag_WVU + * @param IWDGx IWDG Instance + * @retval State of bit (1 or 0). + */ +__STATIC_INLINE uint32_t LL_IWDG_IsActiveFlag_WVU(const IWDG_TypeDef *IWDGx) +{ + return ((READ_BIT(IWDGx->SR, IWDG_SR_WVU) == (IWDG_SR_WVU)) ? 1UL : 0UL); +} + +/** + * @brief Check if all flags Prescaler, Reload & Window Value Update are reset or not + * @rmtoll SR PVU LL_IWDG_IsReady\n + * SR RVU LL_IWDG_IsReady\n + * SR WVU LL_IWDG_IsReady + * @param IWDGx IWDG Instance + * @retval State of bits (1 or 0). + */ +__STATIC_INLINE uint32_t LL_IWDG_IsReady(const IWDG_TypeDef *IWDGx) +{ + return ((READ_BIT(IWDGx->SR, IWDG_SR_PVU | IWDG_SR_RVU | IWDG_SR_WVU) == 0U) ? 1UL : 0UL); +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#endif /* IWDG1 || IWDG2 */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* STM32H7xx_LL_IWDG_H */ diff --git a/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_iwdg.c b/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_iwdg.c new file mode 100644 index 0000000..7114791 --- /dev/null +++ b/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_iwdg.c @@ -0,0 +1,284 @@ +/** + ****************************************************************************** + * @file stm32h7xx_hal_iwdg.c + * @author MCD Application Team + * @brief IWDG HAL module driver. + * This file provides firmware functions to manage the following + * functionalities of the Independent Watchdog (IWDG) peripheral: + * + Initialization and Start functions + * + IO operation functions + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + @verbatim + ============================================================================== + ##### IWDG Generic features ##### + ============================================================================== + [..] + (+) The IWDG can be started by either software or hardware (configurable + through option byte). + + (+) The IWDG is clocked by the Low-Speed Internal clock (LSI) and thus stays + active even if the main clock fails. + + (+) Once the IWDG is started, the LSI is forced ON and both cannot be + disabled. The counter starts counting down from the reset value (0xFFF). + When it reaches the end of count value (0x000) a reset signal is + generated (IWDG reset). + + (+) Whenever the key value 0x0000 AAAA is written in the IWDG_KR register, + the IWDG_RLR value is reloaded into the counter and the watchdog reset + is prevented. + + (+) The IWDG is implemented in the VDD voltage domain that is still functional + in STOP and STANDBY mode (IWDG reset can wake up the CPU from STANDBY). + IWDGRST flag in RCC_CSR register can be used to inform when an IWDG + reset occurs. + + (+) Debug mode: When the microcontroller enters debug mode (core halted), + the IWDG counter either continues to work normally or stops, depending + on DBG_IWDG_STOP configuration bit in DBG module, accessible through + __HAL_DBGMCU_FREEZE_IWDG1() or __HAL_DBGMCU_FREEZE2_IWDG2() and + __HAL_DBGMCU_UnFreeze_IWDG1 or __HAL_DBGMCU_UnFreeze2_IWDG2() macros. + + [..] Min-max timeout value @32KHz (LSI): ~125us / ~32.7s + The IWDG timeout may vary due to LSI clock frequency dispersion. + STM32H7xx devices provide the capability to measure the LSI clock + frequency (LSI clock is internally connected to TIM16 CH1 input capture). + The measured value can be used to have an IWDG timeout with an + acceptable accuracy. + + [..] Default timeout value (necessary for IWDG_SR status register update): + Constant LSI_VALUE is defined based on the nominal LSI clock frequency. + This frequency being subject to variations as mentioned above, the + default timeout value (defined through constant HAL_IWDG_DEFAULT_TIMEOUT + below) may become too short or too long. + In such cases, this default timeout value can be tuned by redefining + the constant LSI_VALUE at user-application level (based, for instance, + on the measured LSI clock frequency as explained above). + + ##### How to use this driver ##### + ============================================================================== + [..] + (#) Use IWDG using HAL_IWDG_Init() function to : + (++) Enable instance by writing Start keyword in IWDG_KEY register. LSI + clock is forced ON and IWDG counter starts counting down. + (++) Enable write access to configuration registers: + IWDG_PR, IWDG_RLR and IWDG_WINR. + (++) Configure the IWDG prescaler and counter reload value. This reload + value will be loaded in the IWDG counter each time the watchdog is + reloaded, then the IWDG will start counting down from this value. + (++) Depending on window parameter: + (+++) If Window Init parameter is same as Window register value, + nothing more is done but reload counter value in order to exit + function with exact time base. + (+++) Else modify Window register. This will automatically reload + watchdog counter. + (++) Wait for status flags to be reset. + + (#) Then the application program must refresh the IWDG counter at regular + intervals during normal operation to prevent an MCU reset, using + HAL_IWDG_Refresh() function. + + *** IWDG HAL driver macros list *** + ==================================== + [..] + Below the list of most used macros in IWDG HAL driver: + (+) __HAL_IWDG_START: Enable the IWDG peripheral + (+) __HAL_IWDG_RELOAD_COUNTER: Reloads IWDG counter with value defined in + the reload register + + @endverbatim + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32h7xx_hal.h" + +/** @addtogroup STM32H7xx_HAL_Driver + * @{ + */ + +#ifdef HAL_IWDG_MODULE_ENABLED +/** @addtogroup IWDG + * @brief IWDG HAL module driver. + * @{ + */ + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/** @defgroup IWDG_Private_Defines IWDG Private Defines + * @{ + */ +/* Status register needs up to 5 LSI clock periods divided by the clock + prescaler to be updated. The number of LSI clock periods is upper-rounded to + 6 for the timeout value calculation. + The timeout value is calculated using the highest prescaler (256) and + the LSI_VALUE constant. The value of this constant can be changed by the user + to take into account possible LSI clock period variations. + The timeout value is multiplied by 1000 to be converted in milliseconds. + LSI startup time is also considered here by adding LSI_STARTUP_TIME + converted in milliseconds. */ +#define HAL_IWDG_DEFAULT_TIMEOUT (((6UL * 256UL * 1000UL) / (LSI_VALUE / 128U)) + \ + ((LSI_STARTUP_TIME / 1000UL) + 1UL)) +#define IWDG_KERNEL_UPDATE_FLAGS (IWDG_SR_WVU | IWDG_SR_RVU | IWDG_SR_PVU) +/** + * @} + */ + +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ + +/** @addtogroup IWDG_Exported_Functions + * @{ + */ + +/** @addtogroup IWDG_Exported_Functions_Group1 + * @brief Initialization and Start functions. + * +@verbatim + =============================================================================== + ##### Initialization and Start functions ##### + =============================================================================== + [..] This section provides functions allowing to: + (+) Initialize the IWDG according to the specified parameters in the + IWDG_InitTypeDef of associated handle. + (+) Manage Window option. + (+) Once initialization is performed in HAL_IWDG_Init function, Watchdog + is reloaded in order to exit function with correct time base. + +@endverbatim + * @{ + */ + +/** + * @brief Initialize the IWDG according to the specified parameters in the + * IWDG_InitTypeDef and start watchdog. Before exiting function, + * watchdog is refreshed in order to have correct time base. + * @param hiwdg pointer to a IWDG_HandleTypeDef structure that contains + * the configuration information for the specified IWDG module. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg) +{ + uint32_t tickstart; + + /* Check the IWDG handle allocation */ + if (hiwdg == NULL) + { + return HAL_ERROR; + } + + /* Check the parameters */ + assert_param(IS_IWDG_ALL_INSTANCE(hiwdg->Instance)); + assert_param(IS_IWDG_PRESCALER(hiwdg->Init.Prescaler)); + assert_param(IS_IWDG_RELOAD(hiwdg->Init.Reload)); + assert_param(IS_IWDG_WINDOW(hiwdg->Init.Window)); + + /* Enable IWDG. LSI is turned on automatically */ + __HAL_IWDG_START(hiwdg); + + /* Enable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers by writing + 0x5555 in KR */ + IWDG_ENABLE_WRITE_ACCESS(hiwdg); + + /* Write to IWDG registers the Prescaler & Reload values to work with */ + hiwdg->Instance->PR = hiwdg->Init.Prescaler; + hiwdg->Instance->RLR = hiwdg->Init.Reload; + + /* Check pending flag, if previous update not done, return timeout */ + tickstart = HAL_GetTick(); + + /* Wait for register to be updated */ + while ((hiwdg->Instance->SR & IWDG_KERNEL_UPDATE_FLAGS) != 0x00u) + { + if ((HAL_GetTick() - tickstart) > HAL_IWDG_DEFAULT_TIMEOUT) + { + if ((hiwdg->Instance->SR & IWDG_KERNEL_UPDATE_FLAGS) != 0x00u) + { + return HAL_TIMEOUT; + } + } + } + + /* If window parameter is different than current value, modify window + register */ + if (hiwdg->Instance->WINR != hiwdg->Init.Window) + { + /* Write to IWDG WINR the IWDG_Window value to compare with. In any case, + even if window feature is disabled, Watchdog will be reloaded by writing + windows register */ + hiwdg->Instance->WINR = hiwdg->Init.Window; + } + else + { + /* Reload IWDG counter with value defined in the reload register */ + __HAL_IWDG_RELOAD_COUNTER(hiwdg); + } + + /* Return function status */ + return HAL_OK; +} + + +/** + * @} + */ + + +/** @addtogroup IWDG_Exported_Functions_Group2 + * @brief IO operation functions + * +@verbatim + =============================================================================== + ##### IO operation functions ##### + =============================================================================== + [..] This section provides functions allowing to: + (+) Refresh the IWDG. + +@endverbatim + * @{ + */ + +/** + * @brief Refresh the IWDG. + * @param hiwdg pointer to a IWDG_HandleTypeDef structure that contains + * the configuration information for the specified IWDG module. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg) +{ + /* Reload IWDG counter with value defined in the reload register */ + __HAL_IWDG_RELOAD_COUNTER(hiwdg); + + /* Return function status */ + return HAL_OK; +} + + +/** + * @} + */ + +/** + * @} + */ + +#endif /* HAL_IWDG_MODULE_ENABLED */ +/** + * @} + */ + +/** + * @} + */ diff --git a/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_ramecc.c b/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_ramecc.c new file mode 100644 index 0000000..c260e43 --- /dev/null +++ b/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_ramecc.c @@ -0,0 +1,779 @@ +/** + ****************************************************************************** + * @file stm32h7xx_hal_ramecc.c + * @author MCD Application Team + * @brief RAMECC HAL module driver. + * This file provides firmware functions to manage the following + * functionalities of the RAM ECC monitoring (RAMECC) peripheral: + * + Initialization and de-initialization functions + * + Monitoring operation functions + * + Error information functions + * + State and error functions + ****************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + @verbatim + ============================================================================== + ##### How to use this driver ##### + ============================================================================== + [..] + (#) Enable and latch error information through HAL_RAMECC_Init(). + + (#) For a given Monitor, enable and disable interrupt through + HAL_RAMECC_EnableNotification(). + To enable a notification for a given RAMECC instance, use global + interrupts. + To enable a notification for only RAMECC monitor, use monitor interrupts. + All possible notifications are defined in the driver header file under + RAMECC_Interrupt group. + + *** Silent mode *** + =================== + [..] + (+) Use HAL_RAMECC_StartMonitor() to start RAMECC latch failing + information without enabling any notification. + + *** Interrupt mode *** + ====================== + [..] + (+) Use HAL_RAMECC_EnableNotification() to enable interrupts for a + given error. + (+) Configure the RAMECC interrupt priority using + HAL_NVIC_SetPriority(). + (+) Enable the RAMECC IRQ handler using HAL_NVIC_EnableIRQ(). + (+) Start RAMECC latch failing information using HAL_RAMECC_StartMonitor(). + + *** Failing information *** + ====================== + [..] + (#) Use HAL_RAMECC_GetFailingAddress() function to return the RAMECC + failing address. + (#) Use HAL_RAMECC_GetFailingDataLow() function to return the RAMECC + failing data low. + (#) Use HAL_RAMECC_GetFailingDataHigh() function to return the RAMECC + failing data high. + (#) Use HAL_RAMECC_GetHammingErrorCode() function to return the RAMECC + Hamming bits injected. + (#) Use HAL_RAMECC_IsECCSingleErrorDetected() function to check if a single + error was detected and corrected. + (#) Use HAL_RAMECC_IsECCDoubleErrorDetected() function to check if a double + error was dedetected. + + *** RAMECC HAL driver macros list *** + ============================================= + [..] + Below the list of used macros in RAMECC HAL driver. + + (+) __HAL_RAMECC_ENABLE_IT : Enable the specified ECCRAM Monitor + interrupts. + (+) __HAL_RAMECC_DISABLE_IT : Disable the specified ECCRAM Monitor + interrupts. + (+) __HAL_RAMECC_GET_FLAG : Return the current RAMECC Monitor selected + flag. + (+) __HAL_RAMECC_CLEAR_FLAG : Clear the current RAMECC Monitor selected + flag. + + ##### Callback registration ##### + ================================== + [..] + (#) The compilation define USE_HAL_RAMECC_REGISTER_CALLBACKS when set to 1 + allows the user to configure dynamically the driver callback. + + [..] + (#) Use Function HAL_RAMECC_RegisterCallback() to register a user callback. + (#) Function HAL_RAMECC_RegisterCallback() allows to register following callback: + (+) RAMECCErrorCode : RAMECC error code detection. + (#) This function takes as parameters the HAL peripheral handle + and a pointer to the user callback function. + + [..] + (#) Use function HAL_RAMECC_UnRegisterCallback() to reset a callback to the default + weak function. + (#) HAL_RAMECC_UnRegisterCallback() takes as parameters the HAL peripheral handle. + (#) This function allows to reset following callback: + (+) RAMECCErrorCode : RAMECC error code detection. + [..] + (#) When The compilation define USE_HAL_RAMECC_REGISTER_CALLBACKS is set to 0 or + not defined, the callback registration feature is not available + and weak callbacks are used. + + @endverbatim + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32h7xx_hal.h" + +/** @addtogroup STM32H7xx_HAL_Driver + * @{ + */ + +/** @defgroup RAMECC RAMECC + * @brief RAMECC HAL module driver + * @{ + */ + +#ifdef HAL_RAMECC_MODULE_ENABLED + +/* Private types -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private constants ---------------------------------------------------------*/ +/* Private macros ------------------------------------------------------------*/ +/* Private functions ---------------------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ + +/** @addtogroup RAMECC_Exported_Functions + * @{ + */ + +/** @addtogroup RAMECC_Exported_Functions_Group1 + * +@verbatim + =============================================================================== + ##### Initialization and de-initialization functions ##### + =============================================================================== + [..] + This section provides functions allowing to initialize the RAMECC Monitor. + [..] + The HAL_RAMECC_Init() function follows the RAMECC configuration procedures + as described in reference manual. + The HAL_RAMECC_DeInit() function allows to deinitialize the RAMECC monitor. + +@endverbatim + * @{ + */ + +/** + * @brief Initialize the RAMECC by clearing flags and disabling interrupts. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval HAL status. + */ +HAL_StatusTypeDef HAL_RAMECC_Init(RAMECC_HandleTypeDef *hramecc) +{ + /* Check the RAMECC peripheral handle */ + if (hramecc == NULL) + { + /* Return HAL status */ + return HAL_ERROR; + } + + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Change RAMECC peripheral state */ + hramecc->State = HAL_RAMECC_STATE_BUSY; + + /* Disable RAMECC monitor */ + hramecc->Instance->CR &= ~RAMECC_CR_ECCELEN; + + /* Disable all global interrupts */ + ((RAMECC_TypeDef *)((uint32_t)hramecc->Instance & 0xFFFFFF00U))->IER &= \ + ~(RAMECC_IER_GIE | RAMECC_IER_GECCSEIE | RAMECC_IER_GECCDEIE | RAMECC_IER_GECCDEBWIE); + + /* Disable all interrupts monitor */ + hramecc->Instance->CR &= ~(RAMECC_CR_ECCSEIE | RAMECC_CR_ECCDEIE | RAMECC_CR_ECCDEBWIE); + + /* Clear RAMECC monitor flags */ + __HAL_RAMECC_CLEAR_FLAG (hramecc, RAMECC_FLAGS_ALL); + + /* Initialise the RAMECC error code */ + hramecc->ErrorCode = HAL_RAMECC_ERROR_NONE; + + /* Initialise the RAMECC error detected code */ + hramecc->RAMECCErrorCode = HAL_RAMECC_NO_ERROR; + + /* Update the RAMECC state */ + hramecc->State = HAL_RAMECC_STATE_READY; + + /* Return HAL status */ + return HAL_OK; +} + +/** + * @brief DeInitializes the RAMECC peripheral. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval HAL status. + */ +HAL_StatusTypeDef HAL_RAMECC_DeInit(RAMECC_HandleTypeDef *hramecc) +{ + /* Check the RAMECC peripheral handle */ + if (hramecc == NULL) + { + /* Return HAL status */ + return HAL_ERROR; + } + + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Disable RAMECC monitor */ + hramecc->Instance->CR &= ~RAMECC_CR_ECCELEN; + + /* Disable all global interrupts */ + ((RAMECC_TypeDef *)((uint32_t)hramecc->Instance & 0xFFFFFF00U))->IER &= \ + ~(RAMECC_IER_GIE | RAMECC_IER_GECCSEIE | RAMECC_IER_GECCDEIE | RAMECC_IER_GECCDEBWIE); + + /* Disable all interrupts monitor */ + hramecc->Instance->CR &= ~(RAMECC_CR_ECCSEIE | RAMECC_CR_ECCDEIE | RAMECC_CR_ECCDEBWIE); + + /* Clear RAMECC monitor flags */ + __HAL_RAMECC_CLEAR_FLAG (hramecc, RAMECC_FLAGS_ALL); + +#if (USE_HAL_RAMECC_REGISTER_CALLBACKS == 1) + /* Clean callback */ + hramecc->DetectErrorCallback = NULL; +#endif /* USE_HAL_RAMECC_REGISTER_CALLBACKS */ + + /* Initialize the RAMECC error code */ + hramecc->ErrorCode = HAL_RAMECC_ERROR_NONE; + + /* Initialize the RAMECC error detected code */ + hramecc->RAMECCErrorCode = HAL_RAMECC_NO_ERROR; + + /* Change RAMECC peripheral state */ + hramecc->State = HAL_RAMECC_STATE_RESET; + + /* Return HAL status */ + return HAL_OK; +} + +/** + * @} + */ + +/** @addtogroup RAMECC_Exported_Functions_Group2 + * +@verbatim + =============================================================================== + ##### Monitoring operation functions ##### + =============================================================================== + [..] This section provides functions allowing to: + (+) Configure latching error information. + (+) Configure RAMECC Global/Monitor interrupts. + (+) Register and Unregister RAMECC callbacks + (+) Handle RAMECC interrupt request + +@endverbatim + * @{ + */ + +/** + * @brief Starts the RAMECC latching error information. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval HAL status. + */ +HAL_StatusTypeDef HAL_RAMECC_StartMonitor(RAMECC_HandleTypeDef *hramecc) +{ + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Check RAMECC state */ + if (hramecc->State == HAL_RAMECC_STATE_READY) + { + /* Change RAMECC peripheral state */ + hramecc->State = HAL_RAMECC_STATE_BUSY; + + /* Enable RAMECC monitor */ + hramecc->Instance->CR |= RAMECC_CR_ECCELEN; + + /* Change RAMECC peripheral state */ + hramecc->State = HAL_RAMECC_STATE_READY; + } + else + { + /* Update the error code */ + hramecc->ErrorCode = HAL_RAMECC_ERROR_BUSY; + + /* Return HAL status */ + return HAL_ERROR; + } + + /* Return HAL status */ + return HAL_OK; +} + +/** + * @brief Stop the RAMECC latching error information. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval HAL status. + */ +HAL_StatusTypeDef HAL_RAMECC_StopMonitor(RAMECC_HandleTypeDef *hramecc) +{ + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Check RAMECC state */ + if (hramecc->State == HAL_RAMECC_STATE_READY) + { + /* Change RAMECC peripheral state */ + hramecc->State = HAL_RAMECC_STATE_BUSY; + + /* Disable RAMECC monitor */ + hramecc->Instance->CR &= ~RAMECC_CR_ECCELEN; + + /* Change RAMECC peripheral state */ + hramecc->State = HAL_RAMECC_STATE_READY; + } + else + { + /* Update the error code */ + hramecc->ErrorCode = HAL_RAMECC_ERROR_BUSY; + + /* Return HAL status */ + return HAL_ERROR; + } + + /* Return HAL status */ + return HAL_OK; +} + +/** + * @brief Enable the RAMECC error interrupts. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that + * contains the configuration information for the + * specified RAMECC Monitor. + * @param Notifications Select the notification. + * @retval HAL status. + */ +HAL_StatusTypeDef HAL_RAMECC_EnableNotification(RAMECC_HandleTypeDef *hramecc, uint32_t Notifications) +{ + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + assert_param (IS_RAMECC_INTERRUPT (Notifications)); + + /* Check RAMECC state */ + if (hramecc->State == HAL_RAMECC_STATE_READY) + { + /* Change RAMECC peripheral state */ + hramecc->State = HAL_RAMECC_STATE_BUSY; + + /* Enable RAMECC interrupts */ + __HAL_RAMECC_ENABLE_IT (hramecc, Notifications); + + /* Change RAMECC peripheral state */ + hramecc->State = HAL_RAMECC_STATE_READY; + } + else + { + /* Update the error code */ + hramecc->ErrorCode = HAL_RAMECC_ERROR_BUSY; + + /* Return HAL status */ + return HAL_ERROR; + } + + /* Return HAL status */ + return HAL_OK; +} + +/** + * @brief Disable the RAMECC error interrupts. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that + * contains the configuration information for the + * specified RAMECC Monitor. + * @param Notifications Select the notification. + * @retval HAL status. + */ +HAL_StatusTypeDef HAL_RAMECC_DisableNotification(RAMECC_HandleTypeDef *hramecc, uint32_t Notifications) +{ + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + assert_param (IS_RAMECC_INTERRUPT (Notifications)); + + /* Check RAMECC state */ + if (hramecc->State == HAL_RAMECC_STATE_READY) + { + /* Change RAMECC peripheral state */ + hramecc->State = HAL_RAMECC_STATE_BUSY; + + /* Disable RAMECC interrupts */ + __HAL_RAMECC_DISABLE_IT (hramecc, Notifications); + + /* Change RAMECC peripheral state */ + hramecc->State = HAL_RAMECC_STATE_READY; + } + else + { + /* Update the error code */ + hramecc->ErrorCode = HAL_RAMECC_ERROR_BUSY; + + /* Return HAL status */ + return HAL_ERROR; + } + + /* Return HAL status */ + return HAL_OK; +} + +/** + * @} + */ + +/** @addtogroup RAMECC_Exported_Functions_Group3 + * +@verbatim + =============================================================================== + ##### Handle Interrupt and Callbacks Functions ##### + =============================================================================== + [..] + This section provides functions to handle RAMECC interrupts and + Register / UnRegister the different callbacks. + [..] + The HAL_RAMECC_IRQHandler() function allows the user to handle the active RAMECC + interrupt request. + The HAL_RAMECC_RegisterCallback() function allows the user to register the selected + RAMECC callbacks. + The HAL_RAMECC_UnRegisterCallback() function allows the user to unregister the + selected RAMECC callbacks. +@endverbatim + * @{ + */ + +#if (USE_HAL_RAMECC_REGISTER_CALLBACKS == 1) +/** + * @brief Register callbacks. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @param pCallback pointer to private callbacsk function which has pointer to + * a RAMECC_HandleTypeDef structure as parameter. + * @retval HAL status. + */ +HAL_StatusTypeDef HAL_RAMECC_RegisterCallback (RAMECC_HandleTypeDef *hramecc, void (* pCallback)(RAMECC_HandleTypeDef *_hramecc)) +{ + HAL_StatusTypeDef status = HAL_OK; + + if (pCallback == NULL) + { + /* Update the error code */ + hramecc->ErrorCode |= HAL_RAMECC_ERROR_INVALID_CALLBACK; + + /* Return HAL status */ + return HAL_ERROR; + } + + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Check RAMECC state */ + if (hramecc->State == HAL_RAMECC_STATE_READY) + { + hramecc->DetectErrorCallback = pCallback; + } + else + { + /* Update the error code */ + hramecc->ErrorCode = HAL_RAMECC_ERROR_INVALID_CALLBACK; + + /* Update HAL status */ + status = HAL_ERROR; + } + + /* Return HAL status */ + return status; +} + +/** + * @brief UnRegister callbacks. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval HAL status. + */ +HAL_StatusTypeDef HAL_RAMECC_UnRegisterCallback(RAMECC_HandleTypeDef *hramecc) +{ + HAL_StatusTypeDef status = HAL_OK; + + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Check RAMECC state */ + if (hramecc->State == HAL_RAMECC_STATE_READY) + { + hramecc->DetectErrorCallback = NULL; + } + else + { + /* Update the error code */ + hramecc->ErrorCode = HAL_RAMECC_ERROR_INVALID_CALLBACK; + + /* Update HAL status */ + status = HAL_ERROR; + } + + /* Return HAL status */ + return status; +} +#endif /* USE_HAL_RAMECC_REGISTER_CALLBACKS */ + +/** + * @brief Handles RAMECC interrupt request. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval None. + */ +void HAL_RAMECC_IRQHandler(RAMECC_HandleTypeDef *hramecc) +{ + uint32_t ier_reg = ((RAMECC_TypeDef *)((uint32_t)hramecc->Instance & 0xFFFFFF00U))->IER; + uint32_t cr_reg = hramecc->Instance->CR >> 1U; + uint32_t sr_reg = hramecc->Instance->SR; + + /* Update global interrupt variables */ + if ((ier_reg & RAMECC_IER_GIE) == RAMECC_IER_GIE) + { + ier_reg = RAMECC_IT_GLOBAL_ALL; + } + + /* Store the ECC Single error detected */ + if ((sr_reg & RAMECC_SR_SEDCF) == RAMECC_SR_SEDCF) + { + hramecc->RAMECCErrorCode |= HAL_RAMECC_SINGLEERROR_DETECTED; + } + + /* Store the ECC double error detected */ + if ((sr_reg & (RAMECC_SR_DEDF | RAMECC_SR_DEBWDF)) != 0U) + { + hramecc->RAMECCErrorCode |= HAL_RAMECC_DOUBLEERROR_DETECTED; + } + + /* Clear active flags */ + __HAL_RAMECC_CLEAR_FLAG (hramecc, (((ier_reg | cr_reg) & (sr_reg << 1U)) >> 1U)); + + /* Check if a valid double error callback is registered */ +#if (USE_HAL_RAMECC_REGISTER_CALLBACKS == 1) + /* Check if a valid error callback is registered */ + if (hramecc->DetectErrorCallback != NULL) + { + /* Error detection callback */ + hramecc->DetectErrorCallback(hramecc); + } +#else + HAL_RAMECC_DetectErrorCallback(hramecc); +#endif /* USE_HAL_RAMECC_REGISTER_CALLBACKS */ +} + +/** + * @brief RAMECC error detection callback. + * @param hramecc : Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC. + * @retval None. + */ +__weak void HAL_RAMECC_DetectErrorCallback(RAMECC_HandleTypeDef *hramecc) +{ + /* Prevent unused argument(s) compilation warning */ + UNUSED(hramecc); + + /* NOTE : This function should not be modified, when the callback is needed, + the HAL_RAMECC_DetectDoubleErrorCallback can be implemented in + the user file. */ +} + +/** + * @} + */ + +/** @addtogroup RAMECC_Exported_Functions_Group4 + * +@verbatim + =============================================================================== + ##### Error information functions ##### + =============================================================================== + [..] This section provides functions allowing to: + (+) Get failing address. + (+) Get failing data low. + (+) Get failing data high. + (+) Get hamming bits injected. + (+) Check single error flag. + (+) Check double error flag. + +@endverbatim + * @{ + */ + +/** + * @brief Return the RAMECC failing address. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval Failing address offset. + */ +uint32_t HAL_RAMECC_GetFailingAddress(const RAMECC_HandleTypeDef *hramecc) +{ + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Return failing address */ + return hramecc->Instance->FAR; +} + +/** + * @brief Return the RAMECC data low. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval Failing data low. + */ +uint32_t HAL_RAMECC_GetFailingDataLow(const RAMECC_HandleTypeDef *hramecc) +{ + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Return failing data low */ + return hramecc->Instance->FDRL; +} + +/** + * @brief Return the RAMECC data high. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval Failing data high. + */ +uint32_t HAL_RAMECC_GetFailingDataHigh(const RAMECC_HandleTypeDef *hramecc) +{ + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Return failing data high */ + return hramecc->Instance->FDRH; +} + +/** + * @brief Return the RAMECC Hamming bits injected. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval Hamming bits injected. + */ +uint32_t HAL_RAMECC_GetHammingErrorCode(const RAMECC_HandleTypeDef *hramecc) +{ + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Return hamming bits injected */ + return hramecc->Instance->FECR; +} + +/** + * @brief Check if an ECC single error was occurred. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval State of bit (1 or 0). + */ +uint32_t HAL_RAMECC_IsECCSingleErrorDetected(const RAMECC_HandleTypeDef *hramecc) +{ + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Return the state of SEDC flag */ + return ((READ_BIT(hramecc->Instance->SR, RAMECC_SR_SEDCF) == (RAMECC_SR_SEDCF)) ? 1UL : 0UL); +} + +/** + * @brief Check if an ECC double error was occurred. + * @param hramecc Pointer to a RAMECC_HandleTypeDef structure that contains + * the configuration information for the specified RAMECC + * Monitor. + * @retval State of bit (1 or 0). + */ +uint32_t HAL_RAMECC_IsECCDoubleErrorDetected(const RAMECC_HandleTypeDef *hramecc) +{ + /* Check the parameters */ + assert_param (IS_RAMECC_MONITOR_ALL_INSTANCE (hramecc->Instance)); + + /* Return the state of DEDF | DEBWDF flags */ + return ((READ_BIT(hramecc->Instance->SR, (RAMECC_SR_DEDF | RAMECC_SR_DEBWDF)) != 0U) ? 1UL : 0UL); +} + +/** + * @} + */ + +/** @addtogroup RAMECC_Exported_Functions_Group5 + * +@verbatim + =============================================================================== + ##### State and Error Functions ##### + =============================================================================== + [..] + This section provides functions allowing to check and get the RAMECC state + and the error code . + [..] + The HAL_RAMECC_GetState() function allows to get the RAMECC peripheral + state. + The HAL_RAMECC_GetError() function allows to Get the RAMECC peripheral error + code. + The HAL_RAMECC_GetRAMECCError() function allows to Get the RAMECC error code + detected. + +@endverbatim + * @{ + */ + +/** + * @brief Get the RAMECC peripheral state. + * @param hramecc : Pointer to a RAMECC_HandleTypeDef structure that + * contains the configuration information for the + * specified RAMECC instance. + * @retval RAMECC state. + */ +HAL_RAMECC_StateTypeDef HAL_RAMECC_GetState(const RAMECC_HandleTypeDef *hramecc) +{ + /* Return the RAMECC state */ + return hramecc->State; +} + +/** + * @brief Get the RAMECC peripheral error code. + * @param hramecc : Pointer to a RAMECC_HandleTypeDef structure that + * contains the configuration information for the + * specified RAMECC instance. + * @retval RAMECC error code. + */ +uint32_t HAL_RAMECC_GetError(const RAMECC_HandleTypeDef *hramecc) +{ + /* Return the RAMECC error code */ + return hramecc->ErrorCode; +} + +/** + * @brief Get the RAMECC error code detected. + * @param hramecc : Pointer to a RAMECC_HandleTypeDef structure that + * contains the configuration information for the + * specified RAMECC instance. + * @retval RAMECC error code detected. + */ +uint32_t HAL_RAMECC_GetRAMECCError(const RAMECC_HandleTypeDef *hramecc) +{ + /* Return the RAMECC error code detected*/ + return hramecc->RAMECCErrorCode; +} + +/** + * @} + */ +#endif /* HAL_RAMECC_MODULE_ENABLED */ +/** + * @} + */ + +/** + * @} + */ diff --git a/STM32H743VIHX_FLASH.ld b/STM32H743VIHX_FLASH.ld index 05911f3..58d2e1b 100644 --- a/STM32H743VIHX_FLASH.ld +++ b/STM32H743VIHX_FLASH.ld @@ -155,6 +155,15 @@ SECTIONS __bss_end__ = _ebss; } >RAM_D1 + /* Persistent across soft/IWDG reset; not zeroed by startup */ + .noinit (NOLOAD) : + { + . = ALIGN(4); + *(.noinit) + *(.noinit*) + . = ALIGN(4); + } >RAM_D1 + /* User_heap_stack section, used to check that there is enough RAM left */ ._user_heap_stack : { diff --git a/cmake/stm32cubemx/CMakeLists.txt b/cmake/stm32cubemx/CMakeLists.txt index ad877c3..28a47ac 100644 --- a/cmake/stm32cubemx/CMakeLists.txt +++ b/cmake/stm32cubemx/CMakeLists.txt @@ -52,6 +52,8 @@ set(STM32_Drivers_Src ${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_i2c.c ${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_i2c_ex.c ${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_exti.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_iwdg.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_ramecc.c ${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rng.c ${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rng_ex.c ${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_spi.c diff --git a/docs/captures/nvcm-rowdrop-debug/test_paced_burn.py b/docs/captures/nvcm-rowdrop-debug/test_paced_burn.py new file mode 100644 index 0000000..beb290b --- /dev/null +++ b/docs/captures/nvcm-rowdrop-debug/test_paced_burn.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Hypothesis test for the NVCM dropped-row bug. + +Re-runs the burn on sacrificial camera 2 with a driver that sleeps 5 ms +after every LSC_PROG_INCR_NV (0x70) row-program transaction, emulating +the busy-wait the i2c_parser polling loop fails to perform. + +Expected if hypothesis is correct: the row-1 data (01 2C 00 43 ...) now +lands at NVCM addr 1 and ORs over the previously mis-burned bits: + 82 91 15 F8 | 01 2C 00 43 = 83 BD 15 FB +""" +import sys +import time + +sys.path.insert(0, r"C:\Users\ethan\Projects\openmotion-sdk") +sys.path.insert(0, r"C:\Users\ethan\Projects\openmotion-sdk\scripts") + +from omotion import MotionInterface +from omotion.i2c_parser import isp_entry_point, ERR_MESSAGES +from test_factory_prog import HardwareDriver + +ALGO = r"C:\Users\ethan\Projects\openmotion-camera-fpga\HistoFPGAFw\impl1\impl1_algo.iea" +DATA = r"C:\Users\ethan\Projects\openmotion-camera-fpga\HistoFPGAFw\impl1\impl1_data.ied" +CAM = 2 # sacrificial - NVCM already mis-burned, unrecoverable + + +class PacedDriver(HardwareDriver): + """HardwareDriver that respects the NVCM row-program busy window.""" + + prog_count = 0 + + def stop(self) -> None: + buf = bytes(self._write_buf) + super().stop() + if buf[:1] == b"\x70": + PacedDriver.prog_count += 1 + time.sleep(0.005) + + +def main() -> int: + iface = MotionInterface() + iface.start(wait=False) + time.sleep(3) + sensor = iface.left + if not sensor.is_connected(): + print("no sensor") + iface.stop() + return 1 + + sensor.enable_camera_power(1 << (CAM - 1)) + time.sleep(0.5) + + driver = PacedDriver(sensor) + driver.select_camera(CAM) + + t0 = time.monotonic() + try: + ret = isp_entry_point(ALGO, DATA, driver=driver) + finally: + elapsed = time.monotonic() - t0 + iface.stop() + + print(f"\nresult={ret} ({ERR_MESSAGES.get(ret, 'OK')})") + print(f"row programs paced: {PacedDriver.prog_count}, elapsed {elapsed:.1f}s") + return 0 if ret >= 0 else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/docs/fpga-nvcm-autodetect.md b/docs/fpga-nvcm-autodetect.md new file mode 100644 index 0000000..873842f --- /dev/null +++ b/docs/fpga-nvcm-autodetect.md @@ -0,0 +1,417 @@ +# FPGA NVCM Auto-Detection — Engineering Notebook + +**Branch:** `feature/fpga-autodetect` +**Goal:** When a CrossLink FPGA has been permanently programmed via NVCM, +detect this at runtime and skip SRAM programming — returning OK immediately. + +--- + +## ⮕ HANDOFF — NEXT AGENT START HERE (you have the `logic2` MCP) + +You are picking up a hardware bring-up that was paced as: edit firmware → write +SDK test script → flash → power-cycle → run script → interpret → repeat. A +Saleae **Logic 2** analyzer is now wired to the sensor and exposed via the +`logic2` MCP (the previous session couldn't see it — it was added after start). + +### What's already established (don't re-litigate) +- **Camera 8's NVCM is confirmed PROGRAMMED** (valid, bootable). Proven by the + auto-boot test: with the config port FORCED (activation key) `IDCODE` reads + `01 2C 00 43`; when CRESETB is released WITHOUT the activation key the config + port at **0x40 disappears** (`boot_0x40_responds=0`) — i.e. the FPGA booted a + user design from NVCM. Reproduced 5×, TCA-safe. +- The **direct content reads return floating `0xFF`** (feature row `0xE7`, feabits + `0xFB`, NVCM array `0x73`) while STATUS (`0x3C`) and USERCODE (`0xC0`) return + real data. Leading explanation: a bare `ISC_ENABLE 0x08` does **not + read-enable** the NVCM array — NVCM-mode STATUS=`0x00000208` lacks the + Read-Enable bit (bit 11) that SRAM-mode STATUS=`0x00000E00` has. + +### Your immediate, highest-value task +Put Logic 2 on cam8's config **SCL/SDA** and capture during one +`OW_FACTORY_NVCM_CHECK` run. Decode I2C and answer: +1. For the `0xE7`/`0x73` reads — do the correct command bytes go out, and does + the **FPGA drive data** or does SDA **float high (no drive)**? That tells us + NAK-vs-not-read-enabled vs wrong-framing. +2. Confirm the activation-key + CRESETB timing matches intent. +3. Use it as the **negative-control reference** in place of a blank camera. + +Then iterate the NVCM read-enable (try ISC_ENABLE operand variants / an explicit +read-enable / trim per the PDFs) until `0x73` returns real, varied bytes — that +both fixes the content read and lets you byte-compare the stored image +(addresses the user's "was the NVCM written correctly / corrupt?" question). + +**FIRST, confirm with the user which Logic 2 channels map to cam8 SCL and SDA** +(and trigger setup) — I don't have that mapping. + +### Exact repro (slow loop ~1–2 min/flash; user said don't get discouraged) +``` +# build: cmake --build build/Debug (in openmotion-sensor-fw) +# flash: python openmotion-sensor-fw/scripts/deploy.py --device left --no-confirm +# (run from C:/Users/ethan/Projects; dfu-util exit 74 is benign) +# boot: python openmotion-bloodflow-app/tests/shelly.py --host 192.168.1.81 cycle --off-time 5.0 +# probe: python openmotion-sdk/scripts/nvcm_probe.py --camera 8 [--operand 0x08] [--rows N] [--no-power] +``` +Zed tasks "Deploy Sensor Left" and "Toggle Outlet" do the flash and power-cycle. +Firmware printf comes back over USB as `[LEFT-COMM PRINTF]` log lines once +`set_debug_flags(USB_PRINTF|CMD_VERBOSE)` is sent (the script does this). + +### ⚠ POWER-SAFETY RULES (the TCA wedges if violated) +- **Only ONE camera powered at a time.** The cam1 lock-up earlier was almost + certainly caused by powering cam1 while **cam8 was still powered** (cam1 IS + populated — not an empty slot). Power off others first, then power the target. +- Never run the boot/power steps in a tight loop across cameras. +- If `0x70` starts timing out (`err 0x20`, repeated "TCA reset"), **recover with + a Toggle-Outlet power cycle** — it always clears it. +- The probe path itself (mux select + CRESETB + I2C) is TCA-safe; it's *camera + power* transitions that are dangerous. + +### Firmware command under test — `OW_FACTORY_NVCM_CHECK = 0x6C` +`if_factory_prog.c` → `fpga_nvcm_probe()` in `crosslink.c`. Payload +`[isc_operand(=0x08), num_rows(=1), do_boot_test(=1)]`. Reads use +`xi2c_write_and_read()` = repeated-START (`Seq_Transmit FIRST_FRAME` → +`Seq_Receive LAST_FRAME`). Wire sequence: +``` +CRESETB low(1ms); write 0x40 ; CRESETB high(10ms) +IDCODE wr[E0 00 00 00] rd4 +ISC_ENABLE wr[C6 00 00] (op 0x08=NVCM / 0x00=SRAM); 5ms +STATUS wr[3C 00 00 00] rd4 <- real +FEATROW wr[E7 00 00 00] rd8 <- 0xFF (suspect read-not-enabled) +FEABITS wr[FB 00 00 00] rd2 <- 0xFF +USERCODE wr[C0 00 00 00] rd4 <- real (0x00000000) +NVCM rows wr[46 00 00 00]; per row wr[73 21 00 00] rd16 <- 0xFF +ISC_DISABLE wr[26 00 00 00] +boot test (if on): CRESETB low(5ms); CRESETB high(150ms, NO key); + IsDeviceReady(0x40); CRESETB low +``` +Response blob (parsed by `nvcm_probe.py`): `[0:4]idcode [4]idcode_ok +[5]step_status [6:10]status [10:18]featrow [18:20]feabits [20:24]usercode +[24]boot_probe_done [25]boot_0x40_responds [26]num_rows_read [27:]rows(16B ea)`. + +### Tip for FAST iteration without reflashing +There's a known **off-by-one in `OW_FACTORY_I2C_WRRD`**: firmware reads +`write_data = &cmd->data[5]` but the SDK `i2c_write_read()` packs it at index 4. +If you fix that (index 5→4 in `if_factory_prog.c`) — one flash — you can then +drive arbitrary repeated-START reads from Python (`sensor.i2c_write` / +`i2c_write_read` / `creset`) and iterate the NVCM read framing at Python speed +with Logic 2 watching, instead of reflashing per attempt. (Verify nothing in +the i2cem/isp path depends on index 5 first.) + +### Key files / state +- FW: `openmotion-sensor-fw` @ `feature/fpga-autodetect` — `crosslink.c` + (`fpga_nvcm_probe`), `crosslink.h` (`fpga_nvcm_probe_t`), `if_factory_prog.c` + (`OW_FACTORY_NVCM_CHECK`), `common.h` (enum `0x6C`). Iteration-2 build is on + the LEFT sensor now. +- SDK: `openmotion-sdk` @ `next` — `omotion/MotionSensor.py` (`nvcm_check`), + `omotion/config.py` (`OW_FACTORY_NVCM_CHECK`), `scripts/nvcm_probe.py`. +- Reference PDFs: `C:\Users\ethan\Downloads\C175812-012925_I2C_Crosslink NVCM + Programming 1.0.pdf` (authoritative I2C byte sequences) and + `C:\Users\ethan\Projects\CrossLink-Programming-Config-User-Guide.pdf` (OTP / + Feature-Row / blank-default semantics). Opcode table + read-back flow are + summarized in the "Authoritative discriminators" section below. +- All work committed (FW + SDK). Nothing of substance uncommitted. + +--- + +## 2026-06-08 + +### Background + +Each sensor module has 8 OV2312 cameras, each with its own Lattice CrossLink +FPGA behind a TCA9548A I2C mux at address 0x70. The FPGAs compute histograms +from MIPI CSI-2 camera data. + +Normally the host sends `OW_FPGA_PROG_SRAM` and the firmware loads a bitstream +from flash into FPGA SRAM on every boot. Once an FPGA is permanently +programmed via NVCM (one-time fuse), it boots its user design autonomously on +power-up and SRAM programming is unnecessary. + +### Approach 1: CDONE pin (FAILED) + +Tried reading the CDONE/GPIO1 pin after releasing CRESETB. Expected it to go +HIGH when the FPGA boots from NVCM. + +**Result:** CDONE didn't budge at all during hardware test. + +**Root cause (from CrossLink Programming & Config User Guide, p13):** +- `CDONE_PORT` in the Feature Row defaults to `CDONE_USER_IO` (general-purpose I/O) +- For CDONE to indicate configuration status, the FPGA design must set + `CDONE_PORT = CDONE_ONLY` in the Diamond Spreadsheet View +- Confirmed by screenshot of the actual Feature Row config from the FPGA design + +**Verdict:** Dead end unless the FPGA design is rebuilt with CDONE_ONLY. +Not worth blocking on. + +### Approach 2: I2C probe (CURRENT) + +**Theory:** After CRESETB release without the I2C activation key, the device +attempts master auto-boot (NVCM first per `BOOT_UP_SEQUENCE = NVCM`). + +- If NVCM is programmed, user design boots. Since `I2C_PORT = DISABLE` + (Feature Row default, confirmed from screenshot), the configuration I2C + port at address 0x40 is disconnected in User Mode. +- If NVCM is blank, device stays unconfigured, 0x40 remains responsive. + +Detection: `HAL_I2C_IsDeviceReady(0x40)` — no ACK means NVCM booted. + +**Implementation:** `fpga_detect_nvcm()` in `camera_manager.c`. Called from: +1. `program_fpga()` — when `force_update == false`, checks cache then probes +2. `program_sram_fpga()` — same pattern +3. ~~`init_camera_sensors()` — probed all 8 at boot~~ (reverted, see below) + +### Issue: TCA9548A mux upset during init + +**Symptom:** TCA9548A "wigging out" when NVCM detection runs during +`init_camera_sensors()`. + +**Root cause (v1):** Original code toggled CRESETB and waited 100ms *before* +selecting the mux channel. During that window, the previously-selected +channel's FPGA (which may have just booted from NVCM) could drive its user +design's I2C signals onto the shared bus, corrupting TCA communication. + +**Fix applied:** Moved `TCA9548A_SelectChannel()` before CRESETB toggle, added +`TCA9548A_DisableChannel()` after detecting NVCM boot. + +**Symptom (v2):** TCA still unhappy even after the mux-ordering fix. + +**Analysis:** Running NVCM detection on all 8 cameras at init is aggressive — +it power-cycles and resets every FPGA, toggles the mux 8 times, and interleaves +with `init_camera()` GPIO reconfigurations. The init path was never designed +for this much I2C traffic. + +**Decision:** Revert init-time detection entirely. Return `init_camera_sensors()` +to its original form (struct init + `power_off_all_cameras()` at the end). +Keep NVCM detection only in `program_fpga()` / `program_sram_fpga()` where it +runs on-demand for a single camera with the bus in a known state. + +This is safer because: +- `program_fpga()` already selects the mux channel as its first step +- Only one camera is being touched at a time +- The bus is quiescent (not in the middle of init) +- `power_off_all_cameras()` runs unmodified, no save/restore dance + +### Changes this session + +| Commit | Description | +|--------|-------------| +| 0eb5a92 | Revert init to stock; keep detection in program paths only | + +--- + +## 2026-06-08 (afternoon) — Pivot to DIRECT NVCM read-back + +### Why pivot + +The boot-inference approach (let CRESETB go HIGH without the activation key, see +whether 0x40 disappears) is **indirect** and, worse, it relies on power/reset +toggling near the TCA9548A — which is what keeps upsetting the mux. We're +abandoning it as the primary signal. + +After re-reading both Lattice PDFs in depth, there is a **direct, authoritative** +method that (a) never lets the FPGA boot, (b) never touches camera power, and +(c) reads the actual NVCM fuse state instead of inferring it from runtime +behavior: + +> Hold the configuration port in **slave mode** (send the activation key around +> the CRESETB transition, exactly as `fpga_configure()` already does for SRAM), +> enter **NVCM access mode** with `ISC_ENABLE = C6 08 00 00` (operand `0x08` +> selects NVCM; `0x00` = SRAM), then read fuse-backed registers over I2C. + +### Authoritative discriminators (from the I2C NVCM Programming spec) + +| Read | Opcode | Bytes | Blank | Programmed | +|---|---|---:|---|---| +| IDCODE | `E0 00 00 00` | 4 | `01 2C 00 43` | `01 2C 00 43` (sanity only) | +| Status Register | `3C 00 00 00` | 4 | bit8 Done=0 | bit8 Done=1; bit6 = "NVCM blank check" | +| **Feature Row** | `E7 00 00 00` | 8 | all `0x00` | non-zero (UNAMBIGUOUS per User Guide p26-27) | +| Feature Bits | `FB 00 00 00` | 2 | `0x0000` | non-zero | +| USERCODE | `C0 00 00 00` | 4 | `0x00000000` | programmed value (only if build sets one) | +| NVCM array row | `46…` then `73 21 00 00` | 16/row | all `0x00` | non-zero | + +Key spec facts: **blank reads as 0x00, NOT 0xFF** on this part. NVCM is OTP, so +"programmed" is permanent. `xi2c_write_and_read()` already does a proper +repeated-START (`I2C_FIRST_FRAME` → `I2C_LAST_FRAME`), which is exactly what the +CrossLink read transactions require. + +### Boundary bug found (NOT fixed — noted) + +The SDK `MotionSensor.i2c_write_read()` packs the write-data at payload **index 4** +(`[wlen_hi,wlen_lo,rlen_hi,rlen_lo, data…]`), but the firmware +`OW_FACTORY_I2C_WRRD` handler reads `write_data = &cmd->data[5]` (index **5**) — +an off-by-one that corrupts the SDK's combined write-then-read passthrough. +`i2c_write` (index 2) and `i2c_read` (index 0-1) are correct. Left alone for now +to avoid touching shared code other scripts may compensate for; the new dedicated +command sidesteps it entirely. + +### Iteration 1 design — one parameterized firmware command, dump everything + +To minimize slow flash cycles, the firmware command is a **thin, parameterized +NVCM-read primitive** so the parameters that carry the most uncertainty (which +ISC_ENABLE operand, how many NVCM rows) can be varied **from Python without +reflashing**: + +- **`OW_FACTORY_NVCM_CHECK = 0x6C`** (factory dispatch, `if_factory_prog.c`). + Payload: `data[0]` = ISC_ENABLE operand1 (default `0x08`=NVCM), `data[1]` = + number of 16-byte NVCM rows to read (default 1). +- **`fpga_nvcm_probe()`** in `crosslink.c`: CRESETB low → activation key → + CRESETB high → IDCODE → `ISC_ENABLE ` → read status, feature row, + feature bits, usercode, N NVCM rows → `ISC_DISABLE`. Every step is + best-effort and records a `step_status` bitmask so one failed read doesn't + abort the rest. Verbose `printf` at each step (gated by `DEBUG_FLAG_USB_PRINTF`). +- Returns a fixed-layout byte blob to the host (idcode, ok, step_status, status, + feature_row, feabits, usercode, num_rows, rows…). +- **Never touches camera power.** The script powers camera 8 once via the + standard `OW_CAMERA_POWER_ON`, then `switch_camera(7)` (selects active cam + + TCA channel 7), then `OW_FACTORY_NVCM_CHECK`. + +SDK side: `MotionSensor.nvcm_check(operand, rows)` + `scripts/nvcm_probe.py` +(connect left → debug flags `USB_PRINTF|CMD_VERBOSE` → power cam8 → switch cam8 → +probe → parse + interpret). Only camera 8 is touched. + +**Hypothesis under test:** on the known-programmed camera-8 part, at least one of +{Feature Row ≠ 0, Status bit8 Done=1, NVCM row0 ≠ 0, USERCODE ≠ 0} will read as +"programmed", giving an absolute signal that needs no blank reference. + +### Iteration log + +| Iter | FW change | Result | Next | +|---|---|---|---| +| 1 | add `OW_FACTORY_NVCM_CHECK` dump-everything probe | infra works, content reads ambiguous | add boot-behavior test | + +#### Iteration 1 — hardware result (camera 8, believed programmed) + +End-to-end flow worked with **zero TCA complaints**: deploy → toggle outlet → +power cam8 → switch (mux ch7) → probe. `IDCODE = 01 2C 00 43, ok=1` — we +successfully held the FPGA in slave config mode (it did NOT boot from NVCM), so +0x40 stayed alive. + +Raw reads, operand `0x08` (NVCM) vs `0x00` (SRAM): + +| Read | 0x08 (NVCM) | 0x00 (SRAM) | Verdict | +|---|---|---|---| +| STATUS | `00 00 02 08` | `00 00 0E 00` | **real, mode-sensitive** | +| FEATROW | `FF…` | `FF…` | untrustworthy (floating) | +| FEABITS | `FF FF` | `FF FF` | untrustworthy | +| USERCODE | `00 00 00 00` | `00 00 00 00` | real, zero | +| NVCM rows | `FF…` | `FF…` | untrustworthy | + +Interpretation: +- STATUS is genuine and changes with the ISC operand → our read path works. +- In NVCM mode STATUS=0x208 has **no Read-Enable bit (bit 11)**; in SRAM mode + STATUS=0xE00 has read+write enable. The bare `ISC_ENABLE 0x08` does **not** + read-enable the NVCM array, which explains the floating `0xFF` on the + feature-row / array reads. +- The opcodes proven by the existing SRAM code (E0/3C/C0) return real data; the + spec-only opcodes (E7/FB/73) return 0xFF. No working reference exists for + them and the agent flagged bit-reversal/framing/trim uncertainty. + +**Conclusion:** the all-`0xFF` reads are NOT real NVCM contents (blank should be +`0x00`). Don't trust them. The naive "feature_row != 0 ⇒ programmed" verdict +is wrong. Pivot the *primary* signal to the behaviorally-definitive boot test; +keep dumping content for the record in case a later framing fix makes it usable. + +| 2 | add boot-behavior test: release CRESETB w/o activation key, check if 0x40 still ACKs | **DEFINITIVE: cam8 = PROGRAMMED** | build production skip-logic on boot test | + +Boot test logic: a programmed NVCM auto-boots its user design on CRESETB +release → `I2C_PORT=DISABLE` reassigns the config pins → **0x40 stops ACKing**. +A blank part has nothing to boot → 0x40 keeps ACKing. So `0x40 responds ⇒ +blank`, `0x40 gone ⇒ programmed`. Done on cam8 only, ends by re-asserting +CRESETB low to halt the booted design and free the bus (TCA-safe). + +#### Iteration 2 — hardware result (camera 8) + +``` +NVCM IDCODE: 01 2C 00 43 ok=1 <- forced config mode: 0x40 PRESENT +NVCM STATUS: 00 00 02 08 (NVCM-mode, no read-enable) +NVCM FEATROW/FEABITS/ROWS: FF... (read artifact, not enabled) +NVCM boot-test: 0x40 responds=0 <- auto-boot: 0x40 GONE +``` + +**VERDICT: camera 8 NVCM is PROGRAMMED with a valid, bootable image.** + +Why this is robust (self-validating within one run): the config port at 0x40 +is **present** during the forced-config phase (activation key → IDCODE ok) and +**absent** during the auto-boot phase (no activation key). The port *flips* +with the mode: +- blank part → 0x40 present in BOTH phases (nothing boots) +- programmed part → 0x40 present when forced, GONE after auto-boot ← observed + +This rules out "0x40 is just always gone" and "always present." It also +addresses the corrupt-write hypothesis: entering User Mode (which reassigns the +config pins) only happens when configuration **completes and asserts DONE**. A +corrupt/garbled NVCM image fails the config CRC and never hands off, so 0x40 +would have stayed. It didn't → the image is structurally valid and bootable +(this proves the *write took*, not that the user *logic* is functionally +perfect). + +The direct content reads (feature row / NVCM array) stay `0xFF` because a bare +`ISC_ENABLE 0x08` doesn't read-enable the NVCM array (STATUS 0x208 lacks the +read-enable bit that SRAM-mode 0xE00 has). This is now **moot for detection** — +the boot test is the reliable, power-safe, framing-independent detector. + +### Recommended detector (production) + +``` +power on camera; select mux channel +CRESETB low; send activation key; CRESETB high # force config mode +read IDCODE -> must be 01 2C 00 43 (config port reachable) +CRESETB low; CRESETB high (NO activation key); wait ~150ms # allow auto-boot +probe 0x40: + no ACK -> NVCM PROGRAMMED -> skip SRAM load; leave CRESETB high (running) + ACK -> NOT programmed -> proceed with SRAM load +``` + +Never touches camera power. Open validation item: confirm a **known-blank** +camera shows `0x40 ACKs` (0x40 present after auto-boot) as the negative control. + +| 3 | (none — ran existing probe on camera 1 as blank control) | **TCA wedged** | cam1 not usable; need a populated-but-blank slot | + +#### Iteration 3 — blank-camera negative control attempt (camera 1) + +Powering on camera 1 locked up the I2C bus: + +``` +Failed to enable mux channel for Camera 1 +Failed to power on camera 0 +TCA9548A control write failed ... addr: 0x70 ret: 1 err: 32 (err 0x20 = I2C TIMEOUT) +TCA reset (x3, then gave up); nvcm_check -> OW_ERROR +``` + +**CORRECTION (per user): camera 1 IS populated** — it has a camera in it. So +the wedge was NOT an empty slot. The real cause was almost certainly that +**cam8 was still powered when cam1 was powered on** (the prior cam8 probe left +cam8 powered; the probe script never powers others off). Two cameras powered at +once → I2C/TCA conflict → `0x70` times out (`err 0x20`). **Recovered cleanly +with a Toggle-Outlet power cycle**, after which cam8 re-probed as PROGRAMMED. + +Lesson → **power-safety rule: only one camera powered at a time.** To use cam1 +as the blank control: power OFF cam8 (and any others) first, then power cam1 +alone, then probe. See the HANDOFF power-safety rules at the top. + +--- + +## Session resume checkpoint (2026-06-08, end of session) + +**Status of the goal:** camera 8 NVCM is **confirmed PROGRAMMED** (valid, +bootable) via the auto-boot 0x40-disappearance test. Reproduced 5×, TCA-safe. + +**Firmware on device:** iteration-2 build (feature/fpga-autodetect, +`OW_FACTORY_NVCM_CHECK` with boot test). Flashed to the LEFT sensor. + +**How to reproduce:** `python openmotion-sdk/scripts/nvcm_probe.py --camera 8` +(left sensor). Deploy = "Deploy Sensor Left" task; boot = "Toggle Outlet". + +**Open threads (tasks):** +1. **logic2 MCP** — user attached a Saleae Logic 2 analyzer to cam8 SCL/SDA and + added a `logic2` MCP server, but its tools are NOT registered in this session + (added after session start). **RESTART the Claude Code session** to load it. + Then capture the I2C during `OW_FACTORY_NVCM_CHECK` to resolve the 0xFF reads. +2. **Blank-camera control** — camera 1 IS populated (per user) and is a valid + blank candidate. Earlier wedge was a power-sequencing mistake (cam8 left + powered while powering cam1). Retry: power OFF cam8 first, then power cam1 + alone, then probe; expect `0x40` to still ACK after auto-boot (BLANK). +3. **Read real NVCM contents** — feature-row (0xE7)/array (0x73) read floating + `0xFF`; NVCM-mode STATUS=0x208 lacks the Read-Enable bit (SRAM-mode=0xE00 has + it). Need the NVCM read-enable sequence; the logic2 capture should reveal + whether the bytes go out correctly and where the read stalls. + +**Commits this session:** firmware `0f3e54e`, ``, notebook updates; SDK +`1f1b72d`, `` + dash fix. Nothing uncommitted of substance. diff --git a/docs/fpga-nvcm-handoff.md b/docs/fpga-nvcm-handoff.md new file mode 100644 index 0000000..9499a32 --- /dev/null +++ b/docs/fpga-nvcm-handoff.md @@ -0,0 +1,340 @@ +# FPGA NVCM Investigation — Handoff Document + +**Date:** 2026-06-09 (updated) +**Branch:** `feature/nvcm-programming` (sensor-fw), `next` (SDK) +**Hardware:** Left sensor module, connected via USB +**Status:** Camera 8 NVCM fully programmed and Done fuse burned + +--- + +## Resolution: Off-By-One Bug in Firmware I2C Write-Read Handler + +**Root cause found and fixed.** The `OW_FACTORY_I2C_WRRD` handler in +`Core/Src/if_factory_prog.c:179` read `write_data` from `&cmd->data[5]` +but the SDK packs it at index 4: + +``` +SDK payload: [write_len_hi, write_len_lo, read_len_hi, read_len_lo, write_data...] + data[0] data[1] data[2] data[3] data[4] ← correct +Firmware read: data[5] ← was wrong +``` + +This shifted every byte sent via `i2c_write_read()` by one position. Every +STATUS register read, every busy-wait poll, and every verify step during NVCM +programming was sending garbage to the FPGA. The `i2c_write` and `i2c_read` +handlers were NOT affected — only the combined write-read path. + +**Fix:** `&cmd->data[5]` → `&cmd->data[4]` (commit `1cb7700`). + +### NVCM Programming Results (Camera 8, Post-Fix) + +After flashing the fix, NVCM programming succeeded: + +| Discriminator | Before Fix | After Fix | Expected (Programmed) | +|---|---|---|---| +| NVCM rows (0x73) | 00×16 per row | Non-zero (real bitstream data) | Non-zero | +| Feature Row (0xE7) | 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 01 80 | Non-zero | +| Done bit (STATUS bit 8) | 0 | **1** | 1 | +| Feature Bits (0xFB) | 00 00 | 00 00 | Possibly zero (design-dependent) | +| USERCODE (0xC0) | 00 00 00 00 | 00 00 00 00 | Design-dependent | +| STATUS (0x3C) | 0x00001E02 | **0x00081F02** | Done=1 | + +Done fuse was burned using `openmotion-sdk/scripts/nvcm_burn_done.py`. The +full `test_factory_prog.py` script programs NVCM rows and Feature Row but +its ISC_PROGRAM_DONE wait may be insufficient — the standalone +`nvcm_burn_done.py` with a 500ms post-command delay succeeded on first try. + +Done=1 persists across power cycles. Camera 8's NVCM is permanently programmed. + +### Notes + +- **FEABITS = 00 00** — may be intentionally zero in the algorithm, or may + need separate investigation. Not blocking auto-boot. +- **CDONE pin** didn't go high in Saleae captures after Done burn. The user + design may not drive CDONE, or the capture window missed the transition. + The STATUS register Done bit is the authoritative source. +- **Other cameras (4, 6, 7)** still have blank NVCM. Only camera 8 was programmed. + +--- + +## Background: The Boot Test Is Invalid + +We spent several sessions building an NVCM auto-detection mechanism based on +the assumption that a blank FPGA's I2C config port (0x40) would remain active +after CRESETB release, while a programmed FPGA's port would disappear (user +design boots and reassigns I2C pins). + +**This assumption is wrong.** The Lattice CrossLink spec (Section 4.3–4.4 of +the Programming & Config User Guide) states: + +> "If no Activation Key is received and CRESETB pin is released HIGH, the +> device enters Master Configuration Mode and boots from the location +> specified in the BOOT_UP_SEQUENCE preference." + +> "If the CrossLink device does not find any valid configuration data, only +> the Slave SPI (SSPI) or Slave I2C modes may be used to program the device. +> **An external SPI Master or I2C Master needs to write the Activation Key** +> to the FPGA to enter one of these modes." + +The I2C slave config port at 0x40 is **only active after the activation key +is sent**. Without the key, 0x40 does NOT respond — regardless of NVCM state. +Both blank and programmed FPGAs show identical behavior: 0x40 gone. + +Confirmed empirically: we probed 0x40 at 50ms, 100ms, 150ms, 250ms, 500ms, +1000ms, and 2000ms after CRESETB release (no activation key). **0x40 never +responded on any camera at any timepoint.** + +## All Available Evidence Points to Blank NVCM + +Every NVCM discriminator we can read (via forced slave config with activation +key) returns values consistent with a blank/unprogrammed device: + +| Discriminator | Cam8 Value | Blank Default (per spec) | Programmed Expected | +|---|---|---|---| +| Done bit (STATUS bit 8) | **0** | 0 | 1 | +| Feature Row (0xE7) | 00 00 00 00 00 00 00 00 | 00×8 | non-zero | +| Feature Bits (0xFB) | 00 00 | 00 00 | non-zero | +| USERCODE (0xC0) | 00 00 00 00 | 00 00 00 00 | design-dependent | +| NVCM rows (0x73) | 00×16 per row | 00×16 | non-zero | +| STATUS (0x3C) | 0x00001E02 | — | bit 8 Done=1 | + +The Done bit is the most important signal. It's the **last step** of NVCM +programming (opcode `ISC_PROGRAM_DONE = 0x5E`). If it's not set, the FPGA +won't try to auto-boot from NVCM. It's 0 on all cameras tested. + +## Full Left Sensor Module Camera Map + +| Camera | TCA Ch | FPGA Present | IDCODE | All Reads | +|--------|--------|-------------|--------|-----------| +| 1 | 0 | **NO** — activation fails, all 0xFF | — | bus idle | +| 2 | 1 | **NO** | — | bus idle | +| 3 | 2 | **NO** | — | bus idle | +| 4 | 3 | **YES** — IDCODE 01 2C 00 43 | ok | all zeros | +| 5 | 4 | **NO** | — | bus idle | +| 6 | 5 | **YES** | ok | all zeros | +| 7 | 6 | **YES** | ok | all zeros | +| 8 | 7 | **YES** | ok | all zeros | + +4 populated FPGAs (cams 4, 6, 7, 8), 4 depopulated positions (cams 1, 2, 3, 5). +All 4 present FPGAs show identical readings — all zeros, Done bit not set. + +## What `fpga_detect_nvcm()` Actually Does (and Why It's Wrong) + +**Note:** This function was added on `feature/fpga-autodetect` (commit +`0eb5a92`) and has never been merged to `main` or `next`. Production firmware +always SRAM-programs every FPGA unconditionally. No shipped systems are affected. + +The feature-branch detection function in `camera_manager.c:109` does: +1. Power on camera +2. Select TCA mux channel +3. CRESETB low (5ms) → CRESETB high (no activation key) → wait 150ms +4. `HAL_I2C_IsDeviceReady(0x40)` — check for ACK +5. No ACK → return `true` ("NVCM programmed, skip SRAM load") + +This returns `true` for **every FPGA** (blank or programmed) because 0x40 +never responds without the activation key. It also returns `true` for +depopulated camera positions (no FPGA = no ACK). The function will +incorrectly skip SRAM loading on ALL cameras, causing the system to not work. + +**Both `program_fpga()` and `program_sram_fpga()` call this function** when +`force_update=false`. If merged as-is, no camera would ever get SRAM loaded. + +## Correct Detection Approach (For Future Implementation) + +To reliably detect NVCM programming, use the activation key to enter forced +slave config mode and read discriminators: + +``` +CRESETB low → activation key (FF A4 C6 F4 8A) → CRESETB high → 10ms delay +IDCODE (0xE0) → must be 01 2C 00 43 (FPGA present) +ISC_ENABLE (0xC6 0x02) → NVCM mode with read-enable +STATUS (0x3C) → check bit 8 (Done) + Done=1 → NVCM is programmed → can skip SRAM load + Done=0 → NVCM is blank → needs SRAM load +ISC_DISABLE (0x26) +``` + +The Done bit is the definitive signal. It's the last thing programmed during +NVCM programming and controls whether the FPGA attempts auto-boot from NVCM. +**Do not use the boot test (CRESETB release without activation key).** + +If the Done bit can't be trusted alone, a secondary check is whether the +Feature Row is non-zero (it's programmed before Done and contains boot +config). Both should be non-zero on a properly programmed device. + +## Resolved: Cam8's NVCM Was Blank (Not Previously Programmed) + +The NVCM had never been successfully programmed. All prior attempts via the +SDK's `test_factory_prog.py` were corrupted by the off-by-one bug — every +`i2c_write_read` STATUS poll sent shifted bytes, so the FPGA never received +correct commands during the programming flow. The script reported "PASSED" +because its verify reads were also broken (garbage in, garbage out). + +After fixing the firmware, NVCM programming succeeded on first attempt and +the Done fuse was burned successfully. + +--- + +## Tools and Environment + +### Hardware Setup + +- **Sensor module:** Left sensor, connected via USB (composite device: + COMMS on IF0, HISTO on IF1, IMU on IF2) +- **USB IDs:** Sensor VID `0x0483` PID `0x5A5A`, DFU bootloader PID `0xDF11` +- **Power control:** Shelly smart plug at `192.168.1.81` — controls power to + the console (which power-cycles the sensors) +- **Logic analyzer:** Saleae Logic Pro 16, device ID `F7E3BA8F5A116A9A` + - Channel mapping: D0=CRESETB, D3=**SCL**, D6=**SDA**, D7=GPIO1/CDONE + - **Labels in Logic 2 may be backwards** — always configure I2C analyzer + as SCL=Ch3, SDA=Ch6 + - 1.8V logic threshold, 50 MHz sample rate for I2C captures + +### Software / Scripts + +| Tool | Location | Purpose | +|---|---|---| +| `nvcm_probe.py` | `openmotion-sdk/scripts/nvcm_probe.py` | Main NVCM probe script. Connects to left sensor, powers camera, runs `OW_FACTORY_NVCM_CHECK`, parses + displays results. | +| `deploy.py` | `openmotion-sensor-fw/scripts/deploy.py` | Flash firmware via DFU. `--device left --no-confirm` for left sensor. | +| `shelly.py` | `openmotion-bloodflow-app/tests/shelly.py` | Power cycle via Shelly smart plug. `--host 192.168.1.81 cycle --off-time 5.0` | +| `dfu-util` | `openmotion-sdk/omotion/dfu-util/win64/dfu-util.exe` | Low-level DFU flash tool (called by deploy.py). | +| Logic 2 MCP | Available as `logic2` MCP server tools | Control Saleae Logic 2 from Claude Code. | + +### Build Commands + +```powershell +# Configure (downloads FPGA bitstream from GitHub — needs internet) +cmake --preset Debug + +# Build +cmake --build build/Debug + +# Output: build/Debug/motion-sensor-fw.{elf,hex,bin,map} +``` + +### Flash + Power Cycle + Probe Workflow + +```powershell +# 1. Build +cmake --build build/Debug + +# 2. Flash (from repo root or parent) +python openmotion-sensor-fw/scripts/deploy.py --device left --no-confirm +# Exit code 74 from dfu-util is BENIGN — sensor needs power cycle after DFU + +# 3. Power cycle (mandatory after DFU flash) +python openmotion-bloodflow-app/tests/shelly.py --host 192.168.1.81 cycle --off-time 5.0 + +# 4. Wait ~8 seconds for sensor to boot and USB to enumerate + +# 5. Probe +python openmotion-sdk/scripts/nvcm_probe.py --camera 8 --operand 0x02 --rows 4 +# --camera N : which camera to probe (1-8) +# --operand 0xNN : ISC_ENABLE operand (0x02=NVCM read-enable, 0x08=NVCM no read-enable) +# --rows N : number of 16-byte NVCM rows to read (0-8) +# --no-power : skip powering on the camera (if already powered) +``` + +### Power Safety Rules + +- **Only ONE camera powered at a time.** The probe script powers on the + target but does NOT power off others. Always power cycle between cameras. +- If `0x70` (TCA9548A mux) starts timing out (`err 0x20`), recover with a + full power cycle via Shelly. +- The probe path (mux select + CRESETB + I2C) is TCA-safe; camera power + transitions are dangerous. + +### Key Firmware Files + +| File | Purpose | +|---|---| +| `Core/Src/crosslink.c` | FPGA I2C functions. `fpga_nvcm_probe()` is the diagnostic probe, `fpga_configure()` is the SRAM programming flow. | +| `Core/Inc/crosslink.h` | Probe result struct `fpga_nvcm_probe_t`, step bitmask defines. | +| `Core/Src/camera_manager.c` | `fpga_detect_nvcm()` (broken production detector at line 109), `program_fpga()`, `program_sram_fpga()`. Camera array init with per-camera CRESETB/power/TCA assignments. | +| `Core/Src/if_commands.c` | Command dispatcher. `OW_FPGA_PROG_SRAM` at line 378, `OW_FACTORY_NVCM_CHECK` handled in `if_factory_prog.c`. | +| `Core/Inc/common.h` | Command enum definitions. | + +### Key SDK Files + +| File | Purpose | +|---|---| +| `omotion/MotionSensor.py` | `nvcm_check(operand, rows)` — sends `OW_FACTORY_NVCM_CHECK` and returns raw response bytes. | +| `omotion/config.py` | Enum `OW_FACTORY_NVCM_CHECK = 0x6C`. | +| `scripts/nvcm_probe.py` | CLI probe script — parses the `fpga_nvcm_probe_t` binary blob and displays a human-readable report. | + +### Reference Documents + +| Document | Location | +|---|---| +| CrossLink I2C NVCM Programming Spec | `C:\Users\ethan\Projects\C175812-012925_I2C_Crosslink NVCM Programming 1.0.pdf` | +| CrossLink Programming & Config User Guide | `C:\Users\ethan\Projects\CrossLink-Programming-Config-User-Guide.pdf` | +| Engineering notebook | `openmotion-sensor-fw/docs/fpga-nvcm-autodetect.md` | + +### Logic 2 Capture Files + +All in `openmotion-sensor-fw/docs/captures/`: + +| File | Contents | +|---|---| +| `nvcm-probe-capture3-1v8.sal` | Clean capture of full probe (1.8V, 50MHz) | +| `nvcm-probe-i2c-corrected.csv` | I2C decode with correct SCL/SDA assignment | +| `nvcm-trim-probe.sal` | Capture of probe with PROG_TRIM step | +| `nvcm-trim-probe-i2c.csv` | I2C decode of trim probe | + +### Relevant Spec Sections + +- **Section 4.3** (Config User Guide): Activation key required for slave port access +- **Section 4.4**: Boot sequence — NVCM-EXT default, activation key needed after failed boot +- **Section 5.5**: I2C Configuration Mode +- **Table C.1** (Appendix C): Status register bit definitions — bit 8 = Done, bit 12 = Busy, bit 13 = Fail +- **Table 7.1**: Feature Row definition — all zeros = HW default (blank) +- **NVCM Programming Spec, Figure 2**: NVCM Program flow — Done bit is last step +- **NVCM Programming Spec, Figure 3**: NVCM Verify flow — PROG_TRIM required before reads +- **NVCM Programming Spec, Table 9**: ISC_ENABLE operand definitions — bit 1 = NVCM read-enable +- **NVCM Programming Spec, Table 24**: Extended status register — bit 7 = Done, bit 6 = NVCM blank check + +### PROG_TRIM Experiment Summary + +The Lattice Verify flow requires `PROG_TRIM (0xD1)` before NVCM read-back. +We implemented this: + +``` +ISC_ENABLE_X (0x74, operand 0x04) — transparent mode, SRAM write +PROG_TRIM (0xD1, operands 00 20 00, data 14 F2 F0 44 00 00 00 00) +ISC_DISABLE (0x26) +``` + +Results: +- First run after power-on: NVCM rows read `14 F2 F0 44 00×12` — the trim + parameter bytes echoing through the sense amplifier. Not real NVCM content. +- Second run (no power cycle): all zeros. +- **PROG_TRIM's volatile state survives CRESETB toggle** and prevents the FPGA + from auto-booting. This poisoned the boot test (caused a programmed FPGA to + appear blank). We removed the trim step from the probe and restructured to + run the boot test first. +- On a truly blank device, the trim echo is expected — the sense amplifiers + output the programmed trim values when reading empty cells. + +### Committed Fix + +- **`Core/Src/if_factory_prog.c`** — off-by-one fix in `OW_FACTORY_I2C_WRRD` + handler: `&cmd->data[5]` → `&cmd->data[4]` (commit `1cb7700` on + `feature/nvcm-programming`) + +### Firmware on Device + +The left sensor is flashed with the fixed firmware (version +`1.5.4-dev.1-12-gb47f75c-dirty` at time of NVCM burn — rebuild with the +committed fix for a clean version string). + +### Remaining Work + +- `fpga_detect_nvcm()` in `camera_manager.c` still uses the invalid boot + test approach. It was rewritten on `feature/fpga-autodetect` (commit + `c40a6b4`) to use the Done-bit approach but may need further validation + now that NVCM programming is confirmed working. +- Cameras 4, 6, 7 still have blank NVCM — can be programmed using the same + flow now that the I2C bug is fixed. +- `test_factory_prog.py` may need a longer wait after ISC_PROGRAM_DONE + (the standalone `nvcm_burn_done.py` uses 500ms and succeeded; the algo + script's built-in wait may be shorter). diff --git a/docs/nvcm-rowdrop-incident.md b/docs/nvcm-rowdrop-incident.md new file mode 100644 index 0000000..44c6686 --- /dev/null +++ b/docs/nvcm-rowdrop-incident.md @@ -0,0 +1,102 @@ +# NVCM Row-Drop Incident — 2026-06-09 + +## Summary + +Cameras 1–7 on the left sensor module were NVCM-burned with a row-shifted +image and **permanently cannot auto-boot from NVCM** (NVCM is one-time +programmable). They remain fully functional via SRAM programming — the +production path — so the module is not degraded for normal use. Camera 8 +(burned the same morning) is correct and auto-boots. + +## Root cause (proven on hardware) + +`openmotion-sdk/omotion/i2c_parser.py` — the Python port of the Lattice +ispVME I2C engine — never implemented TDO/DTDO readback comparison: + +1. **Polling loops didn't poll.** The `LSC_CHECK_BUSY` (0xF0) read between + NVCM row programs returned its result into a comparison that didn't + exist, so every polling loop exited on its first iteration. The only + inter-row pacing was USB round-trip latency. +2. **VERIFY verified nothing.** The verify phase read every row back and + discarded the data. "PASSED" carried no information about content. + +The first NVCM row program is the slowest (charge-pump ramp-up). With no +busy-wait, the row-1 write arrived while the FPGA was still programming +row 0 and was silently ignored; the dropped row gave the device time to +catch up, so rows 2+ landed — each one address early. Result: the +device-check row (`01 2C 00 43 ...`) missing, boot ROM rejects the image. + +Camera 8's burn survived on incidental latency, not different code. +**The sensor firmware factory path (`if_factory_prog.c`) was verified +correct and was never at fault** — including the firmware changes on +`feature/nvcm-autodetect`, which do not touch the factory path. + +## Proof + +Re-burning sacrificial camera 2 with a 5 ms pace after each row-program +transaction produced the predicted OTP-OR signature at the correct +address: + +``` +before: addr1 = 82 91 15 F8 ... (shifted: file row 2) +after: addr1 = 83 BD 15 FB 22 ... 3C 46 (= row2 | row1, all 11,970 rows landed) +``` + +Artifacts in `docs/captures/nvcm-rowdrop-debug/`: simulated transaction +stream, paced-burn test script, failing-verify log. + +## Fix + +`openmotion-sdk` branch `fix/i2c-parser-verify`: + +- `ispVMRead` performs real masked comparison against expected TDO/DTDO + bytes (`ERR_VERIFY_FAIL` on mismatch); simulation drivers keep the old + pass-through. +- `ispVMLoop` returns the last failure when the loop count is exhausted + instead of success. + +Verified on hardware with the fixed parser: the IDCODE comparison passes, +busy polls observably retry, and the algorithm's blank-check correctly +**refuses to program a Done-set device** (it failed fast on camera 3, as +it should). Unit tests: `tests/test_i2c_parser_verify.py`. + +## Hardware state after incident + +| Camera | NVCM state | Auto-boot | Usable via SRAM | +|---|---|---|---| +| 1–7 (left) | row-shifted + OR'd garbage (cams 2, 3 additionally OR'd by diagnostic re-burns) | **never** | yes | +| 8 (left) | correct image + Done | yes | yes | + +## Positive validation (2026-06-09, virgin module in slot 8) + +A brand-new camera module was installed in left slot 8 and burned with +the fixed parser: + +1. `test_factory_prog.py` PASSED with full readback verification. +2. Probe verdict: **NVCM PROGRAMMED** — 0x40 disappears after CRESETB + release, i.e. the FPGA auto-boots its user design. +3. The Done fuse took in the same pass — no separate + `nvcm_burn_done.py` retry needed (the real busy-polling also fixed + the ISC_PROGRAM_DONE wait that bit the first camera-8 burn). +4. Firmware pin-sampling detect: `C8: NVCM detect clk=0 data=0` → + "NVCM programmed, skipping SRAM load". + +The same chain was repeated on the right sensor module (reflashed to the +current firmware) with camera 1 — a **USART**-connected FPGA (USART2, +detect pins PA4/PD6): burn PASSED with verification, auto-boots, +`C1: NVCM detect clk=0 data=0` → SRAM load skipped. Combined with the +blank-camera results, both bus types are validated in both NVCM states: + +| Camera | Bus | NVCM | Detect | +|---|---|---|---| +| left 8 (new module) | SPI4 | programmed | clk=0 data=0 → skip | +| right 1 | USART2 | programmed | clk=0 data=0 → skip | +| left 4 | USART6 | blank | clk=1 data=1 → SRAM | +| left 7 | SPI2 | blank | clk=1 data=1 → SRAM | + +## Outstanding + +- Burns are slower with real polling (real busy-waits + full verify). +- Unexplained: one USB drop (errno 32) during an all-8-cameras + `OW_FPGA_PROG_SRAM` detect loop earlier the same day. Not reproduced or + investigated yet; tracked separately from this incident. diff --git a/docs/superpowers/plans/2026-05-20-firmware-dfu-deploy.md b/docs/superpowers/plans/2026-05-20-firmware-dfu-deploy.md new file mode 100644 index 0000000..3b4222e --- /dev/null +++ b/docs/superpowers/plans/2026-05-20-firmware-dfu-deploy.md @@ -0,0 +1,1038 @@ +# Firmware DFU Deploy Script Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a `scripts/deploy.py` and CMake `flash`/`flash-left`/`flash-right` targets to both `openmotion-console-fw` and `openmotion-sensor-fw` so one command can build, DFU-flash, and recover the device. + +**Architecture:** Per-repo Python script (~200 lines) that drives: cmake build → omotion `enter_dfu()` → `dfu-util` flash with `:leave` (auto-resets MCU) → wait for re-enumeration → optional soft-reset recovery. Pure-logic helpers get pytest unit tests; hardware paths are covered by manual verification only. + +**Tech Stack:** Python 3.12, `omotion` SDK (already on PATH after `pip install -e openmotion-sdk`), `dfu-util` CLI, CMake 3.22+. + +**Spec:** `docs/superpowers/specs/2026-05-20-firmware-dfu-deploy-design.md`. + +--- + +## File Structure + +``` +openmotion-console-fw/ +├── scripts/ +│ ├── deploy.py # NEW — main entry point +│ └── _deploy_helpers.py # NEW — pure helpers (testable) +├── tests/ +│ └── test_deploy_helpers.py # NEW — pytest for pure helpers +├── CMakeLists.txt # MODIFY — add `flash` target +└── ... + +openmotion-sensor-fw/ +├── scripts/ +│ ├── deploy.py # NEW — same shape as console, sensor-specific +│ └── _deploy_helpers.py # NEW — copy of console's helpers +├── tests/ +│ └── test_deploy_helpers.py # NEW — same tests, sensor project name +├── CMakeLists.txt # MODIFY — add `flash-left`, `flash-right` targets +└── ... +``` + +**Why the split:** `deploy.py` is orchestration with side effects (cmake, subprocess, omotion, dfu-util). `_deploy_helpers.py` holds the pure functions (path resolution, project-name parsing, argv validation) — these are unit-testable without hardware. Putting them in their own module keeps `deploy.py` focused on the workflow. + +**Repository convention:** This plan is duplicated identically in both `openmotion-console-fw/docs/superpowers/plans/` and `openmotion-sensor-fw/docs/superpowers/plans/`. Execute the console-fw tasks first; sensor-fw tasks reuse code from console-fw. + +--- + +## Task 1: Console — scaffold scripts/ and tests/ directories + +**Repo:** `openmotion-console-fw` + +**Files:** +- Create: `openmotion-console-fw/scripts/deploy.py` (skeleton) +- Create: `openmotion-console-fw/scripts/_deploy_helpers.py` (empty) +- Create: `openmotion-console-fw/tests/test_deploy_helpers.py` (empty) +- Create: `openmotion-console-fw/tests/conftest.py` (sys.path shim) + +- [ ] **Step 1: Create `scripts/_deploy_helpers.py` with placeholder docstring** + +```python +"""Pure helper functions for scripts/deploy.py. + +All side-effect-free logic lives here so it can be unit tested +without hardware. Imported by deploy.py. +""" +``` + +- [ ] **Step 2: Create `scripts/deploy.py` skeleton (CLI surface only)** + +```python +#!/usr/bin/env python3 +"""deploy.py — Build, DFU-flash, and recover the MOTION console firmware. + +Usage: + python scripts/deploy.py [--config Debug|Release] [--no-build] + [--dfu-util PATH] [--post-reset] [--no-confirm] +""" +from __future__ import annotations + +import argparse +import sys + + +def parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Build, enter DFU, flash, and reset the MOTION console." + ) + p.add_argument("--config", choices=("Debug", "Release"), default="Debug", + help="CMake build config (default: Debug)") + p.add_argument("--no-build", action="store_true", + help="Skip 'cmake --build'; flash whatever is already built") + p.add_argument("--dfu-util", default=None, + help="Path to dfu-util binary (default: search PATH)") + p.add_argument("--post-reset", action="store_true", + help="If device doesn't come back after leaveDFU, attempt soft_reset") + p.add_argument("--no-confirm", action="store_true", + help="Skip the y/N confirmation prompt") + return p.parse_args() + + +def main() -> int: + args = parse_args() + print(f"[deploy] args = {args}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) +``` + +- [ ] **Step 3: Create `tests/conftest.py` so pytest can import the script package** + +```python +"""Add the scripts/ directory to sys.path for tests.""" +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +SCRIPTS = ROOT / "scripts" +if str(SCRIPTS) not in sys.path: + sys.path.insert(0, str(SCRIPTS)) +``` + +- [ ] **Step 4: Verify the skeleton runs** + +Run: `python scripts/deploy.py --config Debug` +Expected: prints `[deploy] args = Namespace(config='Debug', no_build=False, dfu_util=None, post_reset=False, no_confirm=False)` and exits 0. + +- [ ] **Step 5: Commit** + +```bash +git add scripts/deploy.py scripts/_deploy_helpers.py tests/conftest.py tests/test_deploy_helpers.py +git commit -m "chore: scaffold scripts/deploy.py and tests/" +``` + +--- + +## Task 2: Console — pure helpers (project name, bin path, dfu-util resolve) with tests + +**Repo:** `openmotion-console-fw` + +**Files:** +- Modify: `openmotion-console-fw/scripts/_deploy_helpers.py` +- Modify: `openmotion-console-fw/tests/test_deploy_helpers.py` + +- [ ] **Step 1: Write the failing tests in `tests/test_deploy_helpers.py`** + +```python +import shutil +import textwrap +from pathlib import Path + +import pytest + +from _deploy_helpers import ( + read_project_name, + bin_path_for, + resolve_dfu_util, +) + + +def test_read_project_name_extracts_from_set_directive(tmp_path: Path): + cmake = tmp_path / "CMakeLists.txt" + cmake.write_text(textwrap.dedent(""" + cmake_minimum_required(VERSION 3.22) + set(CMAKE_PROJECT_NAME motion-console-fw) + project(${CMAKE_PROJECT_NAME}) + """)) + assert read_project_name(cmake) == "motion-console-fw" + + +def test_read_project_name_raises_when_missing(tmp_path: Path): + cmake = tmp_path / "CMakeLists.txt" + cmake.write_text("# nothing here\n") + with pytest.raises(RuntimeError, match="CMAKE_PROJECT_NAME"): + read_project_name(cmake) + + +def test_bin_path_for_returns_repo_relative(tmp_path: Path): + repo = tmp_path + result = bin_path_for(repo, "Debug", "motion-console-fw") + assert result == repo / "build" / "Debug" / "motion-console-fw.bin" + + +def test_resolve_dfu_util_uses_override_when_given(tmp_path: Path): + fake = tmp_path / "dfu-util" + fake.write_text("#!/bin/sh\necho dfu-util fake\n") + fake.chmod(0o755) + assert resolve_dfu_util(str(fake)) == str(fake) + + +def test_resolve_dfu_util_raises_when_missing(monkeypatch): + monkeypatch.setattr(shutil, "which", lambda _: None) + with pytest.raises(RuntimeError, match="dfu-util not found"): + resolve_dfu_util(None) +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `pytest tests/test_deploy_helpers.py -v` +Expected: All tests fail with `ImportError: cannot import name 'read_project_name'`. + +- [ ] **Step 3: Implement the helpers in `scripts/_deploy_helpers.py`** + +```python +"""Pure helper functions for scripts/deploy.py.""" +from __future__ import annotations + +import re +import shutil +from pathlib import Path + +_PROJECT_NAME_RE = re.compile( + r"set\s*\(\s*CMAKE_PROJECT_NAME\s+([^\s\)]+)\s*\)" +) + + +def read_project_name(cmake_path: Path) -> str: + """Extract CMAKE_PROJECT_NAME from a CMakeLists.txt. + + Mirrors the regex used by generate_vscode_files.py so VS Code and the + deploy script always agree on the binary name. + """ + text = cmake_path.read_text(encoding="utf-8") + m = _PROJECT_NAME_RE.search(text) + if not m: + raise RuntimeError( + f"Could not find CMAKE_PROJECT_NAME set(...) directive in {cmake_path}" + ) + return m.group(1) + + +def bin_path_for(repo_root: Path, config: str, project: str) -> Path: + """Return the absolute path to the produced .bin for a given config.""" + return repo_root / "build" / config / f"{project}.bin" + + +def resolve_dfu_util(override: str | None) -> str: + """Return an absolute path to dfu-util, or raise if not found.""" + if override: + return override + found = shutil.which("dfu-util") + if not found: + raise RuntimeError( + "dfu-util not found on PATH. Install it (Windows: scoop install dfu-util, " + "or download from sourceforge.net/projects/dfu-util) or pass --dfu-util PATH." + ) + return found +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `pytest tests/test_deploy_helpers.py -v` +Expected: 5 passed. + +- [ ] **Step 5: Commit** + +```bash +git add scripts/_deploy_helpers.py tests/test_deploy_helpers.py +git commit -m "feat(deploy): add pure helpers for path/config resolution" +``` + +--- + +## Task 3: Console — DFU enumeration polling helper with test + +**Repo:** `openmotion-console-fw` + +**Files:** +- Modify: `openmotion-console-fw/scripts/_deploy_helpers.py` +- Modify: `openmotion-console-fw/tests/test_deploy_helpers.py` + +We poll `dfu-util --list` to detect DFU enumeration rather than depending on pyusb directly. Output of `dfu-util --list` contains substrings like `Found DFU: [0483:df11]` when the STM32 ROM bootloader is enumerated. + +- [ ] **Step 1: Write the failing test** + +Add to `tests/test_deploy_helpers.py`: + +```python +import subprocess +from unittest.mock import patch + +from _deploy_helpers import wait_for_dfu_device + + +def _fake_run_returning(stdout: str, returncode: int = 0): + def _run(cmd, **kw): + return subprocess.CompletedProcess(cmd, returncode, stdout=stdout, stderr="") + return _run + + +def test_wait_for_dfu_device_succeeds_when_listed(): + with patch("_deploy_helpers.subprocess.run", + side_effect=_fake_run_returning("Found DFU: [0483:df11] ...\n")): + assert wait_for_dfu_device("dfu-util", timeout=0.5, poll_interval=0.05) is True + + +def test_wait_for_dfu_device_times_out_when_absent(): + with patch("_deploy_helpers.subprocess.run", + side_effect=_fake_run_returning("No DFU capable USB device available\n")): + assert wait_for_dfu_device("dfu-util", timeout=0.2, poll_interval=0.05) is False +``` + +- [ ] **Step 2: Run tests to verify the new ones fail** + +Run: `pytest tests/test_deploy_helpers.py -v` +Expected: 2 new tests fail with `ImportError: cannot import name 'wait_for_dfu_device'`. + +- [ ] **Step 3: Implement `wait_for_dfu_device` in `scripts/_deploy_helpers.py`** + +Add at the bottom of `_deploy_helpers.py`: + +```python +import subprocess +import time + +DFU_VID_PID_STR = "0483:df11" + + +def wait_for_dfu_device(dfu_util: str, timeout: float = 10.0, + poll_interval: float = 0.3) -> bool: + """Poll `dfu-util --list` until the STM32 DFU device appears or timeout. + + Returns True if the device was found, False on timeout. + """ + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + try: + result = subprocess.run( + [dfu_util, "--list"], + capture_output=True, text=True, check=False, + ) + if DFU_VID_PID_STR in result.stdout.lower(): + return True + except FileNotFoundError: + # dfu-util went missing between resolve and now — fail fast + return False + time.sleep(poll_interval) + return False +``` + +- [ ] **Step 4: Run tests to verify all pass** + +Run: `pytest tests/test_deploy_helpers.py -v` +Expected: 7 passed. + +- [ ] **Step 5: Commit** + +```bash +git add scripts/_deploy_helpers.py tests/test_deploy_helpers.py +git commit -m "feat(deploy): add wait_for_dfu_device helper" +``` + +--- + +## Task 4: Console — wire main() to drive the full workflow + +**Repo:** `openmotion-console-fw` + +**Files:** +- Modify: `openmotion-console-fw/scripts/deploy.py` + +This is orchestration only — the helpers from Tasks 2-3 do the testable work. Hardware paths (omotion, dfu-util invocation, post-flash) are exercised by manual verification (Task 8), not unit tests, matching the SDK pattern where `enter_dfu.py` and `soft_reset_console.py` have no unit tests. + +- [ ] **Step 1: Replace the entire body of `scripts/deploy.py`** + +```python +#!/usr/bin/env python3 +"""deploy.py — Build, DFU-flash, and recover the MOTION console firmware. + +Usage: + python scripts/deploy.py [--config Debug|Release] [--no-build] + [--dfu-util PATH] [--post-reset] [--no-confirm] +""" +from __future__ import annotations + +import argparse +import subprocess +import sys +import time +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(REPO_ROOT / "scripts")) + +from _deploy_helpers import ( # noqa: E402 + bin_path_for, + read_project_name, + resolve_dfu_util, + wait_for_dfu_device, + DFU_VID_PID_STR, +) + +DEVICE_LABEL = "console" +COMEBACK_TIMEOUT_S = 10.0 +DFU_ENUM_TIMEOUT_S = 10.0 +FLASH_BASE = 0x08000000 + + +def parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Build, enter DFU, flash, and reset the MOTION console." + ) + p.add_argument("--config", choices=("Debug", "Release"), default="Debug") + p.add_argument("--no-build", action="store_true") + p.add_argument("--dfu-util", default=None) + p.add_argument("--post-reset", action="store_true") + p.add_argument("--no-confirm", action="store_true") + return p.parse_args() + + +def _run(cmd: list[str], **kw) -> int: + print(f"+ {' '.join(cmd)}") + return subprocess.call(cmd, **kw) + + +def _git_describe() -> str: + try: + out = subprocess.check_output( + ["git", "describe", "--tags", "--dirty", "--always"], + cwd=REPO_ROOT, text=True, + ) + return out.strip() + except Exception: + return "unknown" + + +def _confirm(bin_file: Path) -> bool: + print() + print(f" Device : {DEVICE_LABEL}") + print(f" Binary : {bin_file} ({bin_file.stat().st_size} bytes)") + print(f" Source : {_git_describe()}") + print() + answer = input(f"Deploy {DEVICE_LABEL}? (y/N): ").strip().lower() + return answer == "y" + + +def _build(config: str) -> None: + build_dir = REPO_ROOT / "build" / config + if not build_dir.exists(): + raise RuntimeError( + f"Build dir {build_dir} does not exist. Run cmake configure first " + f"(e.g. cmake --preset {config})." + ) + rc = _run(["cmake", "--build", str(build_dir), "--config", config]) + if rc != 0: + raise RuntimeError(f"cmake --build failed with exit code {rc}") + + +def _enter_dfu_console(timeout: float) -> bool: + """Trigger the console into DFU using the same omotion API as + openmotion-sdk/scripts/soft_reset_console.py + scripts/enter_dfu.py.""" + from omotion.Interface import MOTIONInterface + + interface, console_connected, _, _ = MOTIONInterface.acquire_motion_interface() + if not console_connected: + print("❌ Console not connected — cannot trigger DFU.") + if interface is not None: + interface.disconnect() + return False + + # Stop telemetry poller before tearing down the serial port, mirroring + # soft_reset_console.py — avoids a cascade of ClearCommError logs. + try: + interface.console_module.telemetry.stop() + except Exception: + pass + + print("[*] Requesting DFU mode …") + try: + ok = interface.console_module.enter_dfu() + except Exception as exc: + print(f"❌ enter_dfu raised: {exc}") + ok = False + finally: + interface.disconnect() + return bool(ok) + + +def _wait_for_console_comeback(timeout: float) -> bool: + """Poll the omotion interface until the console reports connected.""" + from omotion.Interface import MOTIONInterface + + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + interface, console_connected, _, _ = MOTIONInterface.acquire_motion_interface() + if interface is not None: + interface.disconnect() + if console_connected: + return True + time.sleep(0.5) + return False + + +def _soft_reset_console() -> bool: + """Best-effort soft reset via omotion. Returns True if the call succeeded.""" + from omotion.Interface import MOTIONInterface + + interface, console_connected, _, _ = MOTIONInterface.acquire_motion_interface() + try: + if not console_connected: + return False + try: + interface.console_module.telemetry.stop() + except Exception: + pass + return bool(interface.console_module.soft_reset()) + finally: + if interface is not None: + interface.disconnect() + + +def main() -> int: + args = parse_args() + + # Resolve everything that can fail without touching the device. + try: + project = read_project_name(REPO_ROOT / "CMakeLists.txt") + bin_file = bin_path_for(REPO_ROOT, args.config, project) + dfu_util = resolve_dfu_util(args.dfu_util) + except RuntimeError as e: + print(f"❌ {e}") + return 1 + + try: + import omotion # noqa: F401 + except ImportError: + print("❌ omotion not installed in this Python env. " + "Run `pip install -e ../openmotion-sdk` (path may differ).") + return 1 + + if not args.no_build: + try: + _build(args.config) + except RuntimeError as e: + print(f"❌ {e}") + return 1 + + if not bin_file.exists(): + print(f"❌ Binary not found: {bin_file}") + print(" Run `cmake --build build/{cfg}` first or drop --no-build.".format(cfg=args.config)) + return 1 + + if not args.no_confirm: + if not _confirm(bin_file): + print("Aborted.") + return 0 + + if not _enter_dfu_console(timeout=DFU_ENUM_TIMEOUT_S): + return 1 + + print(f"[*] Waiting for DFU device ({DFU_VID_PID_STR}) to enumerate …") + if not wait_for_dfu_device(dfu_util, timeout=DFU_ENUM_TIMEOUT_S): + print("❌ DFU device did not appear within " + f"{DFU_ENUM_TIMEOUT_S:.0f}s.") + print(" On Windows, bind WinUSB to the STM32 DFU interface " + "(PID 0xDF11) via Zadig.") + return 1 + + print(f"[*] Flashing {bin_file.name} via dfu-util …") + cmd = [ + dfu_util, + "-d", DFU_VID_PID_STR, + "-a", "0", + "-s", f"0x{FLASH_BASE:08x}:leave", + "-D", str(bin_file), + ] + rc = _run(cmd) + if rc != 0: + print(f"❌ dfu-util exited with {rc}. Device is still in DFU; rerun deploy.py to retry.") + return 1 + + print("[*] leaveDFU sent. Waiting for console to come back …") + if _wait_for_console_comeback(timeout=COMEBACK_TIMEOUT_S): + print("✅ Console is back online.") + return 0 + + if args.post_reset: + print("[*] Console did not come back; attempting soft_reset …") + if _soft_reset_console() and _wait_for_console_comeback(timeout=COMEBACK_TIMEOUT_S): + print("✅ Console recovered via soft_reset.") + return 0 + print("⚠️ soft_reset did not bring the console back.") + + print("⚠️ Console did not re-enumerate. Please toggle power on the console.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) +``` + +- [ ] **Step 2: Verify the script still loads with --help (no hardware needed)** + +Run: `python scripts/deploy.py --help` +Expected: argparse help text prints, exits 0. If imports fail (e.g. `_deploy_helpers` not on sys.path), fix before commit. + +- [ ] **Step 3: Verify unit tests still pass** + +Run: `pytest tests/test_deploy_helpers.py -v` +Expected: 7 passed (unchanged from Task 3). + +- [ ] **Step 4: Commit** + +```bash +git add scripts/deploy.py +git commit -m "feat(deploy): wire main() for full build/DFU/flash/recover flow" +``` + +--- + +## Task 5: Console — add `flash` CMake target + +**Repo:** `openmotion-console-fw` + +**Files:** +- Modify: `openmotion-console-fw/CMakeLists.txt` + +- [ ] **Step 1: Append the flash target after the existing post-build block** + +Open `CMakeLists.txt` and append at the end (after the `add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD ...)` block at line 144-148): + +```cmake +# Build + DFU-flash target. Invokes scripts/deploy.py which: +# 1. enters DFU via the omotion SDK +# 2. waits for the STM32 ROM bootloader to enumerate +# 3. flashes the .bin via dfu-util with :leave for auto-reset +# Requires: dfu-util on PATH, omotion installed in the active Python env. +find_program(PYTHON_FOR_DEPLOY NAMES python3 python) +if(PYTHON_FOR_DEPLOY) + add_custom_target(flash + COMMAND ${PYTHON_FOR_DEPLOY} ${CMAKE_SOURCE_DIR}/scripts/deploy.py + --config $ --no-build + DEPENDS ${CMAKE_PROJECT_NAME} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + USES_TERMINAL + COMMENT "Flashing ${CMAKE_PROJECT_NAME} via DFU" + ) +else() + message(WARNING "Python not found — `flash` target will not be available") +endif() +``` + +- [ ] **Step 2: Re-configure CMake to pick up the new target** + +Run from repo root: `cmake -B build/Debug -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=cmake/gcc-arm-none-eabi.cmake` +Expected: configure succeeds; output contains `Configuring done`. + +- [ ] **Step 3: Verify the target was registered** + +Run: `cmake --build build/Debug --target help` +Expected: output lists `... flash` among available targets. + +- [ ] **Step 4: Commit** + +```bash +git add CMakeLists.txt +git commit -m "build: add `flash` CMake target invoking scripts/deploy.py" +``` + +--- + +## Task 6: Sensor — mirror Tasks 1-4 with sensor-specific bits + +**Repo:** `openmotion-sensor-fw` + +**Files:** +- Create: `openmotion-sensor-fw/scripts/deploy.py` +- Create: `openmotion-sensor-fw/scripts/_deploy_helpers.py` +- Create: `openmotion-sensor-fw/tests/test_deploy_helpers.py` +- Create: `openmotion-sensor-fw/tests/conftest.py` + +Sensor `deploy.py` differs from console in three places: +1. Adds required `--device left|right` argument. +2. `_enter_dfu_sensor()` uses `interface.left` / `interface.right` instead of `console_module`. +3. Post-flash recovery is a printed hint only (no `soft_reset()` equivalent for sensors). + +- [ ] **Step 1: Copy `_deploy_helpers.py` from console-fw verbatim** + +`scripts/_deploy_helpers.py` is identical to the console version. Copy it. The `read_project_name` regex still works for sensor's `set(CMAKE_PROJECT_NAME motion-sensor-fw)`. + +- [ ] **Step 2: Copy `tests/conftest.py` and `tests/test_deploy_helpers.py` verbatim** + +These tests use `tmp_path` and don't reference any project-specific name, so they pass unmodified in the sensor repo. + +- [ ] **Step 3: Run the tests to verify they pass in the new repo** + +Run from `openmotion-sensor-fw/`: `pytest tests/test_deploy_helpers.py -v` +Expected: 7 passed. + +- [ ] **Step 4: Write `scripts/deploy.py` for the sensor** + +```python +#!/usr/bin/env python3 +"""deploy.py — Build, DFU-flash, and recover a MOTION sensor module. + +Usage: + python scripts/deploy.py --device left|right + [--config Debug|Release] [--no-build] + [--dfu-util PATH] [--post-reset] [--no-confirm] +""" +from __future__ import annotations + +import argparse +import subprocess +import sys +import time +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(REPO_ROOT / "scripts")) + +from _deploy_helpers import ( # noqa: E402 + bin_path_for, + read_project_name, + resolve_dfu_util, + wait_for_dfu_device, + DFU_VID_PID_STR, +) + +COMEBACK_TIMEOUT_S = 10.0 +DFU_ENUM_TIMEOUT_S = 10.0 +FLASH_BASE = 0x08000000 + + +def parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Build, enter DFU, flash, and reset a MOTION sensor module." + ) + p.add_argument("--device", choices=("left", "right"), required=True, + help="Which sensor side to flash (required to avoid wrong-side mistakes)") + p.add_argument("--config", choices=("Debug", "Release"), default="Debug") + p.add_argument("--no-build", action="store_true") + p.add_argument("--dfu-util", default=None) + p.add_argument("--post-reset", action="store_true", + help="If sensor doesn't re-enumerate after leaveDFU, print extra hints") + p.add_argument("--no-confirm", action="store_true") + return p.parse_args() + + +def _run(cmd: list[str], **kw) -> int: + print(f"+ {' '.join(cmd)}") + return subprocess.call(cmd, **kw) + + +def _git_describe() -> str: + try: + out = subprocess.check_output( + ["git", "describe", "--tags", "--dirty", "--always"], + cwd=REPO_ROOT, text=True, + ) + return out.strip() + except Exception: + return "unknown" + + +def _confirm(device: str, bin_file: Path) -> bool: + print() + print(f" Device : {device} sensor") + print(f" Binary : {bin_file} ({bin_file.stat().st_size} bytes)") + print(f" Source : {_git_describe()}") + print() + answer = input(f"Deploy {device} sensor? (y/N): ").strip().lower() + return answer == "y" + + +def _build(config: str) -> None: + build_dir = REPO_ROOT / "build" / config + if not build_dir.exists(): + raise RuntimeError( + f"Build dir {build_dir} does not exist. Run cmake configure first." + ) + rc = _run(["cmake", "--build", str(build_dir), "--config", config]) + if rc != 0: + raise RuntimeError(f"cmake --build failed with exit code {rc}") + + +def _sensor_handle(interface, device: str): + return interface.left if device == "left" else interface.right + + +def _enter_dfu_sensor(device: str, timeout: float) -> bool: + """Trigger the named sensor into DFU mode via omotion.""" + from omotion.Interface import MOTIONInterface + + interface, _, left_connected, right_connected = MOTIONInterface.acquire_motion_interface() + connected = left_connected if device == "left" else right_connected + if not connected: + print(f"❌ {device} sensor not connected — cannot trigger DFU.") + if interface is not None: + interface.disconnect() + return False + + print(f"[*] Requesting DFU mode on {device} sensor …") + try: + sensor = _sensor_handle(interface, device) + ok = sensor.enter_dfu() + except Exception as exc: + print(f"❌ enter_dfu raised: {exc}") + ok = False + finally: + interface.disconnect() + return bool(ok) + + +def _wait_for_sensor_comeback(device: str, timeout: float) -> bool: + """Poll omotion until the named sensor reports connected.""" + from omotion.Interface import MOTIONInterface + + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + interface, _, left_connected, right_connected = MOTIONInterface.acquire_motion_interface() + if interface is not None: + interface.disconnect() + ok = left_connected if device == "left" else right_connected + if ok: + return True + time.sleep(0.5) + return False + + +def main() -> int: + args = parse_args() + + try: + project = read_project_name(REPO_ROOT / "CMakeLists.txt") + bin_file = bin_path_for(REPO_ROOT, args.config, project) + dfu_util = resolve_dfu_util(args.dfu_util) + except RuntimeError as e: + print(f"❌ {e}") + return 1 + + try: + import omotion # noqa: F401 + except ImportError: + print("❌ omotion not installed in this Python env. " + "Run `pip install -e ../openmotion-sdk` (path may differ).") + return 1 + + if not args.no_build: + try: + _build(args.config) + except RuntimeError as e: + print(f"❌ {e}") + return 1 + + if not bin_file.exists(): + print(f"❌ Binary not found: {bin_file}") + return 1 + + if not args.no_confirm: + if not _confirm(args.device, bin_file): + print("Aborted.") + return 0 + + if not _enter_dfu_sensor(args.device, timeout=DFU_ENUM_TIMEOUT_S): + return 1 + + print(f"[*] Waiting for DFU device ({DFU_VID_PID_STR}) to enumerate …") + if not wait_for_dfu_device(dfu_util, timeout=DFU_ENUM_TIMEOUT_S): + print("❌ DFU device did not appear within " + f"{DFU_ENUM_TIMEOUT_S:.0f}s.") + print(" On Windows, bind WinUSB to the STM32 DFU interface " + "(PID 0xDF11) via Zadig.") + return 1 + + print(f"[*] Flashing {bin_file.name} via dfu-util …") + cmd = [ + dfu_util, + "-d", DFU_VID_PID_STR, + "-a", "0", + "-s", f"0x{FLASH_BASE:08x}:leave", + "-D", str(bin_file), + ] + rc = _run(cmd) + if rc != 0: + print(f"❌ dfu-util exited with {rc}. Device is still in DFU; rerun deploy.py to retry.") + return 1 + + print(f"[*] leaveDFU sent. Waiting for {args.device} sensor to come back …") + if _wait_for_sensor_comeback(args.device, timeout=COMEBACK_TIMEOUT_S): + print(f"✅ {args.device.capitalize()} sensor is back online.") + return 0 + + # No software path to power-cycle a single sensor — surface a clear hint. + print(f"⚠️ {args.device} sensor did not re-enumerate.") + print(" Please toggle power on the console " + "(this power-cycles the sensors).") + if args.post_reset: + print(" --post-reset: no additional automatic recovery is available " + "for sensors; manual power cycle required.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) +``` + +- [ ] **Step 5: Verify the sensor script loads** + +Run: `python scripts/deploy.py --help` +Expected: argparse help prints with `--device {left,right}` listed; exits 0. + +- [ ] **Step 6: Verify --device is required** + +Run: `python scripts/deploy.py --config Debug` +Expected: argparse error `the following arguments are required: --device`, non-zero exit. + +- [ ] **Step 7: Commit** + +```bash +git add scripts/deploy.py scripts/_deploy_helpers.py tests/conftest.py tests/test_deploy_helpers.py +git commit -m "feat(deploy): add scripts/deploy.py for sensor module" +``` + +--- + +## Task 7: Sensor — add `flash-left` and `flash-right` CMake targets + +**Repo:** `openmotion-sensor-fw` + +**Files:** +- Modify: `openmotion-sensor-fw/CMakeLists.txt` + +- [ ] **Step 1: Append the flash targets after the existing post-build block** + +Open `CMakeLists.txt` and append at the end (after the `add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD ...)` block): + +```cmake +# Build + DFU-flash targets, one per sensor side. Invokes scripts/deploy.py. +# `--device` is required by the script, so we expose two separate targets. +find_program(PYTHON_FOR_DEPLOY NAMES python3 python) +if(PYTHON_FOR_DEPLOY) + foreach(SIDE left right) + add_custom_target(flash-${SIDE} + COMMAND ${PYTHON_FOR_DEPLOY} ${CMAKE_SOURCE_DIR}/scripts/deploy.py + --device ${SIDE} --config $ --no-build + DEPENDS ${CMAKE_PROJECT_NAME} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + USES_TERMINAL + COMMENT "Flashing ${CMAKE_PROJECT_NAME} (${SIDE} sensor) via DFU" + ) + endforeach() +else() + message(WARNING "Python not found — `flash-left`/`flash-right` targets will not be available") +endif() +``` + +- [ ] **Step 2: Re-configure CMake** + +Run: `cmake -B build/Debug -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=cmake/gcc-arm-none-eabi.cmake` +Expected: configure succeeds. + +- [ ] **Step 3: Verify both targets registered** + +Run: `cmake --build build/Debug --target help` +Expected: output lists both `... flash-left` and `... flash-right`. + +- [ ] **Step 4: Commit** + +```bash +git add CMakeLists.txt +git commit -m "build: add flash-left/flash-right CMake targets" +``` + +--- + +## Task 8: Manual hardware verification (both repos) + +**Prerequisites:** Bench setup with console + at least one sensor connected over USB. `dfu-util` on PATH. `omotion` installed in the active Python env. Windows: WinUSB bound to STM32 DFU interface (PID 0xDF11) via Zadig — bind it the first time the DFU device shows up if not already done. + +These are manual checks. Record observations in the PR description / commit notes. + +- [ ] **Step 1: Console happy path** + +From `openmotion-console-fw/`: +``` +cmake --build build/Debug --target flash +``` +Expected sequence: +1. Build runs (or no-op if up-to-date). +2. Prompt: `Deploy console? (y/N):` — answer `y`. +3. `[*] Requesting DFU mode …` — console becomes a DFU device. +4. `[*] Waiting for DFU device (0483:df11) to enumerate …` — succeeds within ~2s. +5. `[*] Flashing motion-console-fw.bin via dfu-util …` — dfu-util progress bar runs to completion. +6. `[*] leaveDFU sent. Waiting for console to come back …` +7. `✅ Console is back online.` — exit 0. + +Then verify the new firmware: `python -c "from omotion.Interface import MOTIONInterface; i,c,l,r = MOTIONInterface.acquire_motion_interface(); print(i.console_module.ping())"` — should print True. + +- [ ] **Step 2: Sensor happy path (both sides)** + +From `openmotion-sensor-fw/`, for each side: +``` +cmake --build build/Debug --target flash-left +# then +cmake --build build/Debug --target flash-right +``` +Expected: same sequence as console, but with sensor-specific messages and ending in `✅ Left sensor is back online.` / `✅ Right sensor is back online.`. + +- [ ] **Step 3: Pre-flight failures (negative tests)** + +Console repo: +1. Pass `--config Bogus` → argparse rejects, non-zero exit, no device touched. +2. Hide `dfu-util` (`PATH=...` without it, or rename) → script prints "dfu-util not found …" and exits 1 before any device interaction. +3. Disconnect console, run `flash` → "Console not connected — cannot trigger DFU." Non-zero exit. + +Sensor repo: +4. Run `python scripts/deploy.py` (no `--device`) → argparse error, non-zero exit. + +- [ ] **Step 4: Cancellation path** + +Run `cmake --build build/Debug --target flash`, answer `n` at the prompt. +Expected: `Aborted.`, exit 0, console still in normal operating mode (verify with a ping). + +- [ ] **Step 5: --post-reset path (console)** + +This is harder to trigger naturally. Optional smoke test: run `python scripts/deploy.py --post-reset --no-confirm` after a known-good flash and confirm the script still exits cleanly when the device comes back without needing the soft-reset. + +- [ ] **Step 6: Record verification in the PR** + +In the PR description, note: which steps were exercised, on what hardware (console serial number + sensor sides), and any observations. The CMake targets and Python helpers are CI-friendly but the device interaction is not — manual record is the only audit trail. + +--- + +## Self-Review Summary + +**Spec coverage:** +- Workflow (build → enter_dfu → wait → dfu-util `:leave` → comeback) — Tasks 4, 6. +- CMake `flash` target (console) — Task 5. +- CMake `flash-left`/`flash-right` (sensor) — Task 7. +- Confirmation prompt — Tasks 4, 6 (in `_confirm`). +- Pre-flight checks (dfu-util, omotion, bin existence) — Task 4 (lines in `main()`), Task 6. +- Post-flash recovery (console soft_reset, sensor power-toggle hint) — Tasks 4, 6. +- Edge cases (wrong-side flash, dfu-util missing, DFU never enumerates, mid-flash failure, wedged after leave) — covered across Tasks 4, 6, 8. +- Tests for pure helpers — Tasks 2, 3. + +**No-placeholder scan:** No "TBD"/"TODO"/"implement later" patterns. All code blocks are complete. + +**Type consistency:** All helpers use the names `read_project_name`, `bin_path_for`, `resolve_dfu_util`, `wait_for_dfu_device`, and constant `DFU_VID_PID_STR`. These names are referenced identically in `scripts/deploy.py` and `tests/test_deploy_helpers.py`. + +**Known small risks:** +- The `omotion` API surface (`interface.console`, `interface.left`, `interface.right` vs `interface.console_module`) varies between `enter_dfu.py` (which uses `interface.console.enter_dfu()`) and `soft_reset_console.py` (which uses `interface.console_module.soft_reset()`). The plan uses `console_module` and `left`/`right` directly per the matching existing scripts. If implementation reveals these don't exist, fall back to the API the working script uses, no design change needed. diff --git a/docs/superpowers/specs/2026-05-20-firmware-dfu-deploy-design.md b/docs/superpowers/specs/2026-05-20-firmware-dfu-deploy-design.md new file mode 100644 index 0000000..e40e592 --- /dev/null +++ b/docs/superpowers/specs/2026-05-20-firmware-dfu-deploy-design.md @@ -0,0 +1,201 @@ +# Firmware DFU Deploy Script — Design + +**Date:** 2026-05-20 +**Repos:** `openmotion-console-fw`, `openmotion-sensor-fw` (sibling spec in each) +**Status:** Approved + +## Goal + +Give engineers a one-command path to build firmware, push the target into DFU mode, flash it over USB with `dfu-util`, and trigger leaveDFU so the new firmware starts running. Hardware programmers (ST-Link/SWD) are not assumed to be connected — DFU-over-USB is the only deployment path. + +## Non-goals + +- ST-Link / OpenOCD / SWD flashing (already handled by VS Code tasks generated by `generate_vscode_files.py`; left untouched). +- Cross-repo shared library or pip package. +- Field/end-user deployment UX. This is for developers on the team. + +## Workflow + +``` +cmake --build # produce .bin + ↓ +omotion enter_dfu # device drops to STM32 ROM bootloader (VID:PID 0483:DF11) + ↓ +wait for DFU enumeration # poll USB for 0483:DF11, ~10s timeout + ↓ +dfu-util -d 0483:df11 -a 0 -s 0x08000000:leave -D .bin + ↓ +(optional) post-flash recovery +``` + +The `:leave` suffix in the dfu-util address tells the bootloader to jump to the application after the download completes — this is the "reset at the end" step. + +## Per-repo deliverables + +Each firmware repo gets: + +1. `scripts/deploy.py` — Python entry point. +2. CMake `flash` custom target(s) in `CMakeLists.txt`. + +The two scripts share ~80% of logic but live independently in each repo (no cross-repo dependency). Differences are confined to a small per-repo config block: project name, default device, post-flash strategy. + +## `scripts/deploy.py` — CLI + +**Console repo:** +``` +python scripts/deploy.py [--config Debug|Release] + [--no-build] + [--dfu-util PATH] + [--post-reset] + [--no-confirm] +``` + +**Sensor repo:** +``` +python scripts/deploy.py --device left|right + [--config Debug|Release] + [--no-build] + [--dfu-util PATH] + [--post-reset] + [--no-confirm] +``` + +`--device` is required on sensor (no default — prevents accidental wrong-side flash). + +### Behavior + +1. **Resolve paths** + - Read project name from `CMakeLists.txt` (same regex `generate_vscode_files.py` uses). + - Bin path: `build//.bin`. + - Sensor uses the merged FW + FPGA bitstream `.bin` produced by the existing post-build step in `CMakeLists.txt`. That merged bin is already a single contiguous payload starting at `0x08000000`, so a single `dfu-util` download writes both firmware and FPGA bitstream regions in one shot. + +2. **Build** (default on) + - `cmake --build build/ --config `. + - Skip if `--no-build` is passed (used by the CMake `flash` target so we don't recurse). + +3. **Pre-flight** + - Confirm `dfu-util` is on PATH (or `--dfu-util` was given). Print version. + - Confirm `omotion` import succeeds. + - Confirm `.bin` exists. Error with a clear hint if missing. + +4. **Confirmation prompt** (unless `--no-confirm`) + - Print: target device, bin path, bin size, git describe of current firmware tree. + - Prompt `Deploy ? (y/N):`. + +5. **Enter DFU** + - Import `omotion`, acquire interface, wait for target handle. + - For console: stop the telemetry poller first (mirrors `openmotion-sdk/scripts/soft_reset_console.py` to avoid the cascading-error log spew when the serial port goes away). + - Call `target.enter_dfu()`. The handle returns; the device re-enumerates as STM32 DFU. + +6. **Wait for DFU enumeration** + - Poll for a USB device with VID:PID `0483:DF11` using the same USB backend omotion uses (libusb1) for up to 10s. + - On timeout, abort with a clear error and a Windows-specific hint about binding WinUSB to the STM32 DFU interface via Zadig. + +7. **Flash** + - Invoke: + ``` + dfu-util -d 0483:df11 -a 0 -s 0x08000000:leave -D + ``` + - Stream stdout/stderr live so the user sees progress. + - Non-zero exit aborts with the dfu-util error preserved. + +8. **Post-flash recovery** — only if `--post-reset` was passed OR the device fails to re-enumerate after `:leave`: + - **Console**: wait up to 10s for the console's UART serial port to come back and `interface.console_module.is_connected()` to return True. If missing, attempt `interface.console_module.soft_reset()`. If still missing, print a clear "please toggle power on the console" hint. + - **Sensor**: wait up to 10s for USB re-enumeration at `0483:5A5A`. If missing, print "please toggle power on the console (no software path to power-cycle a single sensor)". + +Exit code: 0 on success, non-zero on any failure stage with the failing stage noted. + +## CMake additions + +Append to each repo's `CMakeLists.txt`: + +**Console (`openmotion-console-fw/CMakeLists.txt`):** +```cmake +find_program(PYTHON_FOR_DEPLOY NAMES python3 python) +if(PYTHON_FOR_DEPLOY) + add_custom_target(flash + COMMAND ${PYTHON_FOR_DEPLOY} ${CMAKE_SOURCE_DIR}/scripts/deploy.py + --config $ --no-build + DEPENDS ${CMAKE_PROJECT_NAME} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + USES_TERMINAL + COMMENT "Flashing ${CMAKE_PROJECT_NAME} via DFU" + ) +endif() +``` + +Usage: `cmake --build build/Debug --target flash`. + +**Sensor (`openmotion-sensor-fw/CMakeLists.txt`):** two targets, one per side, since `--device` is required: +```cmake +find_program(PYTHON_FOR_DEPLOY NAMES python3 python) +if(PYTHON_FOR_DEPLOY) + foreach(SIDE left right) + add_custom_target(flash-${SIDE} + COMMAND ${PYTHON_FOR_DEPLOY} ${CMAKE_SOURCE_DIR}/scripts/deploy.py + --device ${SIDE} --config $ --no-build + DEPENDS ${CMAKE_PROJECT_NAME} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + USES_TERMINAL + COMMENT "Flashing ${CMAKE_PROJECT_NAME} (${SIDE} sensor) via DFU" + ) + endforeach() +endif() +``` + +Usage: `cmake --build build/Debug --target flash-left` or `flash-right`. + +`DEPENDS ${CMAKE_PROJECT_NAME}` ensures the firmware target is built (or rebuilt-if-needed) before deploy runs. The script's `--no-build` flag prevents a redundant inner `cmake --build` invocation. + +## Dependencies + +- **dfu-util** — Must be installed and on PATH (or pointed at via `--dfu-util`). README will note: Windows installs from `sourceforge.net/projects/dfu-util/` or via scoop/choco. +- **omotion** — Must be `pip install -e openmotion-sdk` in the active Python env. The script imports it for the enter_dfu step and (console only) the optional soft_reset. +- **pyusb / libusb1** — already omotion dependencies; reused for the DFU-enumeration poll. +- **Zadig WinUSB binding** — Windows users must bind WinUSB to the STM32 DFU interface once. Existing sensor README covers Zadig for the running-app USB interfaces; we add a sibling note for the DFU bootloader interface (PID 0xDF11). + +## Edge cases & error handling + +- **No device connected before deploy** → enter_dfu step fails fast with the omotion timeout. Script reports which side failed. +- **dfu-util not installed** → pre-flight catches it before any device state changes. +- **DFU device never enumerates** → 10s timeout; print Zadig hint and exit 1. Device is now in DFU mode and needs manual recovery (power-cycle to get back to app). +- **dfu-util mid-flash error** → exit non-zero, device left in DFU. Script suggests "rerun deploy.py to retry" since DFU mode persists across the abort. +- **`:leave` leaves device wedged** → `--post-reset` path triggers soft_reset (console) or power-toggle hint (sensor). +- **Wrong-side sensor flash** → confirmation prompt + required `--device` flag are both defenses. Skipping confirmation requires explicit `--no-confirm`. + +## File layout + +``` +openmotion-console-fw/ +├── scripts/ +│ └── deploy.py +├── CMakeLists.txt # +flash target +└── docs/superpowers/specs/ + └── 2026-05-20-firmware-dfu-deploy-design.md + +openmotion-sensor-fw/ +├── scripts/ +│ └── deploy.py +├── CMakeLists.txt # +flash-left, +flash-right targets +└── docs/superpowers/specs/ + └── 2026-05-20-firmware-dfu-deploy-design.md +``` + +## Testing approach + +This touches real hardware; full coverage requires a console + at least one sensor on the bench. + +1. **Console deploy happy path** — `cmake --build build/Debug --target flash` from a clean tree. Confirm: build runs, prompt appears, DFU mode entered, dfu-util progress streams, leaveDFU completes, console comes back on UART, omotion can ping it. +2. **Sensor deploy happy path** (each side) — same flow with `--target flash-left` / `flash-right`. Confirm USB re-enumeration at `0483:5A5A` after `:leave`. +3. **--post-reset path** (console) — manually force a hang by flashing a build that doesn't auto-start (or skip leave). Confirm soft_reset path triggers. +4. **Pre-flight failures** — uninstall dfu-util temporarily, confirm clean error before any device state change. Pass a bad `--config` and confirm bin-missing error. +5. **Wrong device prompt** — answer N at the confirmation prompt, confirm no device state change. + +Manual verification only — no automated tests. The scripts wrap hardware operations that can't be meaningfully unit tested. + +## Out of scope + +- Field/customer deployment UX (this is dev-only). +- Recovery from a corrupted DFU bootloader (system-monitor-level issue). +- Programming the FPGA bitstream separately — the sensor's merged `.bin` covers it. +- Changes to `generate_vscode_files.py` or the existing VS Code OpenOCD flash tasks. diff --git a/docs/tca9548a-bus-issues.md b/docs/tca9548a-bus-issues.md new file mode 100644 index 0000000..1fdbe26 --- /dev/null +++ b/docs/tca9548a-bus-issues.md @@ -0,0 +1,123 @@ +# TCA9548A I2C Mux Bus Issues + +Observations from NVCM programming and FPGA autodetect work (June 2026). +**Root cause identified and fixed** — see "Root cause" and "Fixes" below. +Kept for history and in case a related wedge resurfaces. + +## Symptoms (historical) + +1. **TCA write failures with error 32 (HAL_I2C_ERROR_TIMEOUT)** + - `TCA9548A control write failed (attempt N)` with `ret: 1 err: 32` + - Happened after camera power transitions and CRESETB toggles + - Once wedged, persisted across DFU reflash (MCU reset doesn't clear + the TCA register or the STM32 I2C BUSY state) — only a full power + cycle recovered it + +2. **Stale channel selection** + - Failure logs showed `current: 0x88` / `current: 0xC0` (multiple + channels selected) — channels left connected after camera + power-off and re-selected at power-on + +3. **All-channel wedge during scan startup** + - When the bus was stuck, ALL 8 channel selections failed + - Confirmed the problem was bus-level, not per-channel + +## Root cause (confirmed by stress test, 2026-06-09) + +Three interacting bugs: + +1. **`enable_camera_power()` selected the camera's mux channel + immediately at power-on.** A freshly powered CrossLink with blank or + unloaded configuration drives its configuration pins during its boot + attempt — and those are the same physical pins as the mux channel's + SDA/SCL. Connecting the channel at power-on clamped the shared I2C + bus. The *next* I2C transaction (whatever it was) timed out with + err 32. The stress test showed this deterministically: the first TCA + write after every power-on failed on attempt 1. + +2. **`disable_camera_power()` originally cut power with the channel + still selected.** An unpowered FPGA clamps the channel through its + ESD diodes into the dead supply rail — same effect from the other + direction. (Fixed in 628f4e3.) + +3. **The recovery path only reset the TCA, not the STM32 I2C + peripheral.** The TCA hardware reset disconnects all channels and + frees the bus wires, but when a transaction was aborted mid-flight + the STM32 I2C peripheral stayed BUSY and every subsequent transaction + failed instantly. This was the "permanent wedge" that previously + required a power cycle. (Fixed in 628f4e3 with `I2C_BusRecovery()`.) + +## Fixes + +Three layers, defense in depth: + +1. **Prevention — never connect a mux channel to an FPGA in an unknown + state:** + - `enable_camera_power()` no longer selects the channel at power-on. + Every I2C consumer (temperature poll, FPGA programming, camera + config) already selects the channel right before transacting. + - `disable_camera_power()` / `power_off_all_cameras()` disconnect + the channel *before* cutting power. + - `fpga_detect_nvcm()` calls `TCA9548A_DisableAll()` before toggling + CRESETB so an FPGA reset can't glitch the bus through an open + channel. + +2. **Recovery — `I2C_BusRecovery()` (`i2c_master.c`):** on the 2nd + failed TCA write attempt, performs full bus recovery: `HAL_I2C_DeInit`, + bit-bang up to 9 SCL clocks as GPIO to free a slave holding SDA, + generate a STOP, restore AF pins, `HAL_I2C_Init`. Verified working + on hardware — recovered the bus in-place where a power cycle was + previously required. + +3. **Retry — `TCA9548A_WriteControl()`** retries 3× with a TCA hardware + reset between attempts (pre-existing) plus the bus recovery above. + +## Verification + +Stress test: 10× loop of {power off cam 4, power on, `program_fpga`} +exercising the full detect → TCA select → SRAM program path on the +USART camera. + +- **Before the power-on fix:** attempt-1 TCA timeout on every iteration + (deterministic), escalating to full bus recovery on ~30% of + iterations. All iterations still passed thanks to the recovery layers + (10/10), but each one burned ~100–150 ms in timeouts. +- **After the power-on fix:** expected zero TCA failures (see test + results in PR). + +## Remaining concerns (lower priority) + +- **`HAL_MAX_DELAY` in other I2C paths.** `send_buffer_to_slave()` + (`i2c_master.c:113`) and `read_data_register_of_slave()` (line ~163) + still use `HAL_MAX_DELAY`. If a downstream device holds the bus mid- + transaction these hang forever. Should move to bounded timeouts. +- **`isPresent` is set true merely by powering** (known gotcha, + `if_commands.c:880`), so the temperature poller will select channels + for cameras whose FPGA bridge isn't programmed yet. Reads fail + cleanly (NACK) once the bus itself is healthy, but a programmed-state + gate would reduce pointless bus traffic. +- **`TCA9548A_WriteControl` trusts its cache.** If `I2C_current_target` + is wrong (e.g. after an aborted write), a disable call can short- + circuit as a no-op. The hardware-reset path resyncs the cache, so + this is currently benign, but a periodic read-back of the TCA control + register would make it robust. + +## Hardware info + +- TCA9548A at address 0x70, active-low reset on `MUX_RESET` GPIO +- I2C1: PB7 = SDA, PB8 = SCL, AF4, external pull-ups on board +- 8 downstream CrossLink FPGAs, each at 0x40 on their mux channel +- CRESETB per camera: active-low, held low = FPGA held in reset +- CrossLink config pins are dual-purpose: I2C slave config port AND + the pins behind the mux channel — this overlap is why FPGA state + transitions can disturb the bus + +## Related code + +- `Core/Src/i2c_master.c` — `TCA9548A_WriteControl` (retry loop), + `TCA9548A_DisableAll`, `I2C_BusRecovery`, `TCA9548A_ResetHardware` +- `Core/Src/camera_manager.c` — `enable_camera_power` / + `disable_camera_power` (power-transition ordering), + `fpga_detect_nvcm()`, `program_fpga()`, `poll_camera_temperatures()` +- `Core/Src/crosslink.c` — FPGA I2C communication +- `Core/Src/stm32h7xx_hal_msp.c:151-180` — I2C1 GPIO/clock init diff --git a/generate_vscode_files.py b/generate_vscode_files.py index 8ff112d..78fd64f 100644 --- a/generate_vscode_files.py +++ b/generate_vscode_files.py @@ -258,6 +258,46 @@ "command": "${KILL_OPENOCD_CMD}", "args": [${KILL_OPENOCD_ARGS}], "problemMatcher": [] + }, + { + "label": "Deploy Sensor Left", + "type": "shell", + "command": "python", + "args": [ + "${workspaceFolder}/scripts/deploy.py", + "--device", + "left" + ], + "group": "build", + "problemMatcher": [], + "presentation": { + "reveal": "always", + "focus": true, + "panel": "dedicated" + }, + "options": { + "cwd": "${workspaceFolder}" + } + }, + { + "label": "Deploy Sensor Right", + "type": "shell", + "command": "python", + "args": [ + "${workspaceFolder}/scripts/deploy.py", + "--device", + "right" + ], + "group": "build", + "problemMatcher": [], + "presentation": { + "reveal": "always", + "focus": true, + "panel": "dedicated" + }, + "options": { + "cwd": "${workspaceFolder}" + } } ] } diff --git a/motion-sensor-fw.ioc b/motion-sensor-fw.ioc index 6c39b56..3ed6a7c 100644 --- a/motion-sensor-fw.ioc +++ b/motion-sensor-fw.ioc @@ -222,6 +222,10 @@ GPIO.groupedBy=Group By Peripherals I2C1.I2C_Speed_Mode=I2C_Fast I2C1.IPParameters=Timing,I2C_Speed_Mode I2C1.Timing=0x00B03FDB +IWDG1.IPParameters=Prescaler,Reload,Window +IWDG1.Prescaler=IWDG_PRESCALER_128 +IWDG1.Reload=4095 +IWDG1.Window=4095 KeepUserPlacement=false MMTAppReg1.MEMORYMAP.AppRegionName=DTCMRAM MMTAppReg1.MEMORYMAP.ContextName=Cortex-M7NS @@ -280,38 +284,46 @@ Mcu.CPN=STM32H743VIH6 Mcu.Family=STM32H7 Mcu.IP0=BDMA Mcu.IP1=CORTEX_M7 -Mcu.IP10=SPI2 -Mcu.IP11=SPI3 -Mcu.IP12=SPI4 -Mcu.IP13=SPI6 -Mcu.IP14=SYS -Mcu.IP15=TIM2 -Mcu.IP16=TIM4 -Mcu.IP17=TIM5 -Mcu.IP18=TIM8 -Mcu.IP19=TIM14 +Mcu.IP10=RCC +Mcu.IP11=RNG +Mcu.IP12=SPI2 +Mcu.IP13=SPI3 +Mcu.IP14=SPI4 +Mcu.IP15=SPI6 +Mcu.IP16=SYS +Mcu.IP17=TIM2 +Mcu.IP18=TIM4 +Mcu.IP19=TIM5 Mcu.IP2=CRC -Mcu.IP20=TIM15 -Mcu.IP21=TIM16 -Mcu.IP22=UART4 -Mcu.IP23=USART1 -Mcu.IP24=USART2 -Mcu.IP25=USART3 -Mcu.IP26=USART6 -Mcu.IP27=USB_OTG_HS +Mcu.IP20=TIM8 +Mcu.IP21=TIM14 +Mcu.IP22=TIM15 +Mcu.IP23=TIM16 +Mcu.IP24=UART4 +Mcu.IP25=USART1 +Mcu.IP26=USART2 +Mcu.IP27=USART3 +Mcu.IP28=USART6 +Mcu.IP29=USB_OTG_HS Mcu.IP3=DEBUG Mcu.IP4=DMA Mcu.IP5=I2C1 -Mcu.IP6=MEMORYMAP -Mcu.IP7=NVIC -Mcu.IP8=RCC -Mcu.IP9=RNG -Mcu.IPNb=28 +Mcu.IP6=IWDG1 +Mcu.IP7=MEMORYMAP +Mcu.IP8=NVIC +Mcu.IP9=RAMECC +Mcu.IPNb=30 Mcu.Name=STM32H743V(G-I)Hx Mcu.Package=TFBGA100 Mcu.Pin0=PC14-OSC32_IN (OSC32_IN) Mcu.Pin1=PC13 Mcu.Pin10=PC15-OSC32_OUT (OSC32_OUT) +Mcu.Pin100=VP_TIM5_VS_ClockSourceINT +Mcu.Pin101=VP_TIM8_VS_ClockSourceINT +Mcu.Pin102=VP_TIM14_VS_ClockSourceINT +Mcu.Pin103=VP_TIM15_VS_ClockSourceINT +Mcu.Pin104=VP_TIM16_VS_ClockSourceINT +Mcu.Pin105=VP_MEMORYMAP_VS_MEMORYMAP Mcu.Pin11=PE3 Mcu.Pin12=PB8 Mcu.Pin13=PB6 @@ -391,18 +403,25 @@ Mcu.Pin8=PA14 (JTCK/SWCLK) Mcu.Pin80=PD8 Mcu.Pin81=PD12 Mcu.Pin82=VP_CRC_VS_CRC -Mcu.Pin83=VP_RNG_VS_RNG -Mcu.Pin84=VP_SYS_VS_tim17 -Mcu.Pin85=VP_TIM2_VS_ClockSourceINT -Mcu.Pin86=VP_TIM4_VS_no_output3 -Mcu.Pin87=VP_TIM5_VS_ClockSourceINT -Mcu.Pin88=VP_TIM8_VS_ClockSourceINT -Mcu.Pin89=VP_TIM14_VS_ClockSourceINT +Mcu.Pin83=VP_IWDG1_VS_IWDG +Mcu.Pin84=VP_RAMECC_VS_D1_1 +Mcu.Pin85=VP_RAMECC_VS_D1_2 +Mcu.Pin86=VP_RAMECC_VS_D1_3 +Mcu.Pin87=VP_RAMECC_VS_D1_4 +Mcu.Pin88=VP_RAMECC_VS_D1_5 +Mcu.Pin89=VP_RAMECC_VS_D2_1 Mcu.Pin9=PA13 (JTMS/SWDIO) -Mcu.Pin90=VP_TIM15_VS_ClockSourceINT -Mcu.Pin91=VP_TIM16_VS_ClockSourceINT -Mcu.Pin92=VP_MEMORYMAP_VS_MEMORYMAP -Mcu.PinsNb=93 +Mcu.Pin90=VP_RAMECC_VS_D2_2 +Mcu.Pin91=VP_RAMECC_VS_D2_3 +Mcu.Pin92=VP_RAMECC_VS_D2_4 +Mcu.Pin93=VP_RAMECC_VS_D2_5 +Mcu.Pin94=VP_RAMECC_VS_D3_1 +Mcu.Pin95=VP_RAMECC_VS_D3_2 +Mcu.Pin96=VP_RNG_VS_RNG +Mcu.Pin97=VP_SYS_VS_tim17 +Mcu.Pin98=VP_TIM2_VS_ClockSourceINT +Mcu.Pin99=VP_TIM4_VS_no_output3 +Mcu.PinsNb=106 Mcu.ThirdPartyNb=0 Mcu.UserConstants= Mcu.UserName=STM32H743VIHx @@ -420,6 +439,7 @@ NVIC.DMA1_Stream6_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true NVIC.DMA1_Stream7_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true NVIC.DMA2_Stream0_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.ECC_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true NVIC.ForceEnableDMAVector=true NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.I2C1_ER_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true @@ -851,7 +871,20 @@ ProjectManager.ToolChainLocation= ProjectManager.UAScriptAfterPath= ProjectManager.UAScriptBeforePath= ProjectManager.UnderRoot=false -ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_BDMA_Init-BDMA-false-HAL-true,5-MX_CRC_Init-CRC-false-HAL-true,6-MX_I2C1_Init-I2C1-false-HAL-true,7-MX_RNG_Init-RNG-false-HAL-true,8-MX_SPI2_Init-SPI2-false-HAL-true,9-MX_SPI3_Init-SPI3-false-HAL-true,10-MX_SPI4_Init-SPI4-false-HAL-true,11-MX_SPI6_Init-SPI6-false-HAL-true,12-MX_TIM2_Init-TIM2-false-HAL-true,13-MX_UART4_Init-UART4-false-HAL-true,14-MX_USART6_Init-USART6-false-HAL-true,15-MX_TIM8_Init-TIM8-false-HAL-true,16-MX_TIM4_Init-TIM4-false-HAL-true,17-MX_USART1_Init-USART1-false-HAL-true,18-MX_USART2_Init-USART2-false-HAL-true,19-MX_USART3_Init-USART3-false-HAL-true,false-20-MX_USB_OTG_HS_PCD_Init-USB_OTG_HS-true-HAL-true,21-MX_TIM14_Init-TIM14-false-HAL-true,22-MX_TIM16_Init-TIM16-false-HAL-true,23-MX_TIM5_Init-TIM5-false-HAL-true,24-MX_TIM15_Init-TIM15-false-HAL-true,0-MX_CORTEX_M7_Init-CORTEX_M7-false-HAL-true +ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_BDMA_Init-BDMA-false-HAL-true,5-MX_CRC_Init-CRC-false-HAL-true,6-MX_I2C1_Init-I2C1-false-HAL-true,7-MX_RNG_Init-RNG-false-HAL-true,8-MX_SPI2_Init-SPI2-false-HAL-true,9-MX_SPI3_Init-SPI3-false-HAL-true,10-MX_SPI4_Init-SPI4-false-HAL-true,11-MX_SPI6_Init-SPI6-false-HAL-true,12-MX_TIM2_Init-TIM2-false-HAL-true,13-MX_UART4_Init-UART4-false-HAL-true,14-MX_USART6_Init-USART6-false-HAL-true,15-MX_TIM8_Init-TIM8-false-HAL-true,16-MX_TIM4_Init-TIM4-false-HAL-true,17-MX_USART1_Init-USART1-false-HAL-true,18-MX_USART2_Init-USART2-false-HAL-true,19-MX_USART3_Init-USART3-false-HAL-true,false-20-MX_USB_OTG_HS_PCD_Init-USB_OTG_HS-true-HAL-true,21-MX_TIM14_Init-TIM14-false-HAL-true,22-MX_TIM16_Init-TIM16-false-HAL-true,23-MX_TIM5_Init-TIM5-false-HAL-true,24-MX_TIM15_Init-TIM15-false-HAL-true,25-MX_IWDG1_Init-IWDG1-false-HAL-true,26-MX_RAMECC_Init-RAMECC-false-HAL-true,0-MX_CORTEX_M7_Init-CORTEX_M7-false-HAL-true +RAMECC.IPParameters=Instance-RAMECC1_Monitor1,Instance-RAMECC1_Monitor2,Instance-RAMECC1_Monitor3,Instance-RAMECC1_Monitor4,Instance-RAMECC1_Monitor5,Instance-RAMECC2_Monitor1,Instance-RAMECC2_Monitor2,Instance-RAMECC2_Monitor3,Instance-RAMECC2_Monitor4,Instance-RAMECC2_Monitor5,Instance-RAMECC3_Monitor1,Instance-RAMECC3_Monitor2 +RAMECC.Instance-RAMECC1_Monitor1=RAMECC1_Monitor1 +RAMECC.Instance-RAMECC1_Monitor2=RAMECC1_Monitor2 +RAMECC.Instance-RAMECC1_Monitor3=RAMECC1_Monitor3 +RAMECC.Instance-RAMECC1_Monitor4=RAMECC1_Monitor4 +RAMECC.Instance-RAMECC1_Monitor5=RAMECC1_Monitor5 +RAMECC.Instance-RAMECC2_Monitor1=RAMECC2_Monitor1 +RAMECC.Instance-RAMECC2_Monitor2=RAMECC2_Monitor2 +RAMECC.Instance-RAMECC2_Monitor3=RAMECC2_Monitor3 +RAMECC.Instance-RAMECC2_Monitor4=RAMECC2_Monitor4 +RAMECC.Instance-RAMECC2_Monitor5=RAMECC2_Monitor5 +RAMECC.Instance-RAMECC3_Monitor1=RAMECC3_Monitor1 +RAMECC.Instance-RAMECC3_Monitor2=RAMECC3_Monitor2 RCC.ADCFreq_Value=120000000 RCC.AHB12Freq_Value=240000000 RCC.AHB4Freq_Value=240000000 @@ -1036,8 +1069,34 @@ USB_OTG_HS.IPParameters=VirtualMode-Device_HS,DeviceSpeed-Device_HS USB_OTG_HS.VirtualMode-Device_HS=Device_HS VP_CRC_VS_CRC.Mode=CRC_Activate VP_CRC_VS_CRC.Signal=CRC_VS_CRC +VP_IWDG1_VS_IWDG.Mode=IWDG_Activate +VP_IWDG1_VS_IWDG.Signal=IWDG1_VS_IWDG VP_MEMORYMAP_VS_MEMORYMAP.Mode=CurAppReg VP_MEMORYMAP_VS_MEMORYMAP.Signal=MEMORYMAP_VS_MEMORYMAP +VP_RAMECC_VS_D1_1.Mode=RAMECC1_Monitor1 +VP_RAMECC_VS_D1_1.Signal=RAMECC_VS_D1_1 +VP_RAMECC_VS_D1_2.Mode=RAMECC1_Monitor2 +VP_RAMECC_VS_D1_2.Signal=RAMECC_VS_D1_2 +VP_RAMECC_VS_D1_3.Mode=RAMECC1_Monitor3 +VP_RAMECC_VS_D1_3.Signal=RAMECC_VS_D1_3 +VP_RAMECC_VS_D1_4.Mode=RAMECC1_Monitor4 +VP_RAMECC_VS_D1_4.Signal=RAMECC_VS_D1_4 +VP_RAMECC_VS_D1_5.Mode=RAMECC1_Monitor5 +VP_RAMECC_VS_D1_5.Signal=RAMECC_VS_D1_5 +VP_RAMECC_VS_D2_1.Mode=RAMECC2_Monitor1 +VP_RAMECC_VS_D2_1.Signal=RAMECC_VS_D2_1 +VP_RAMECC_VS_D2_2.Mode=RAMECC2_Monitor2 +VP_RAMECC_VS_D2_2.Signal=RAMECC_VS_D2_2 +VP_RAMECC_VS_D2_3.Mode=RAMECC2_Monitor3 +VP_RAMECC_VS_D2_3.Signal=RAMECC_VS_D2_3 +VP_RAMECC_VS_D2_4.Mode=RAMECC2_Monitor4 +VP_RAMECC_VS_D2_4.Signal=RAMECC_VS_D2_4 +VP_RAMECC_VS_D2_5.Mode=RAMECC2_Monitor5 +VP_RAMECC_VS_D2_5.Signal=RAMECC_VS_D2_5 +VP_RAMECC_VS_D3_1.Mode=RAMECC3_Monitor1 +VP_RAMECC_VS_D3_1.Signal=RAMECC_VS_D3_1 +VP_RAMECC_VS_D3_2.Mode=RAMECC3_Monitor2 +VP_RAMECC_VS_D3_2.Signal=RAMECC_VS_D3_2 VP_RNG_VS_RNG.Mode=RNG_Activate VP_RNG_VS_RNG.Signal=RNG_VS_RNG VP_SYS_VS_tim17.Mode=TIM17 diff --git a/scripts/_deploy_helpers.py b/scripts/_deploy_helpers.py new file mode 100644 index 0000000..da54462 --- /dev/null +++ b/scripts/_deploy_helpers.py @@ -0,0 +1,119 @@ +"""Pure helper functions for scripts/deploy.py.""" +from __future__ import annotations + +import platform +import re +import shutil +import subprocess +import time +from pathlib import Path +from typing import Optional + +_PROJECT_NAME_RE = re.compile( + r"set\s*\(\s*CMAKE_PROJECT_NAME\s+([^\s\)]+)\s*\)" +) + + +def read_project_name(cmake_path: Path) -> str: + """Extract CMAKE_PROJECT_NAME from a CMakeLists.txt. + + Mirrors the regex used by generate_vscode_files.py so VS Code and the + deploy script always agree on the binary name. + """ + text = cmake_path.read_text(encoding="utf-8") + m = _PROJECT_NAME_RE.search(text) + if not m: + raise RuntimeError( + f"Could not find CMAKE_PROJECT_NAME set(...) directive in {cmake_path}" + ) + return m.group(1) + + +def bin_path_for(repo_root: Path, config: str, project: str) -> Path: + """Return the absolute path to the produced .bin for a given config.""" + return repo_root / "build" / config / f"{project}.bin" + + +def _bundled_dfu_util() -> Optional[Path]: + """Look for the dfu-util binary that ships with the omotion package. + + Returns None if omotion isn't importable or no matching platform binary + is bundled. The SDK ships binaries for win32, win64, and darwin-x86_64; + Linux callers fall through to PATH lookup. + """ + try: + import omotion + except ImportError: + return None + + pkg_dir = Path(omotion.__file__).resolve().parent + + system = platform.system().lower() + if system.startswith("windows"): + machine = platform.machine().lower() + subdir = "win64" if "64" in machine else "win32" + exe = "dfu-util.exe" + elif system.startswith("darwin"): + subdir = "darwin-x86_64" + exe = "dfu-util" + else: + return None + + candidate = pkg_dir / "dfu-util" / subdir / exe + return candidate if candidate.is_file() else None + + +def resolve_dfu_util(override: str | None) -> str: + """Return an absolute path to dfu-util, or raise if not found. + + Search order: + 1. --dfu-util override (must exist if given) + 2. The binary bundled inside the installed omotion package + 3. dfu-util on PATH + """ + if override: + if not Path(override).is_file(): + raise RuntimeError( + f"dfu-util override path does not exist: {override}" + ) + return override + + bundled = _bundled_dfu_util() + if bundled is not None: + return str(bundled) + + found = shutil.which("dfu-util") + if not found: + raise RuntimeError( + "dfu-util not found: not bundled with the installed omotion " + "package, not on PATH, and no --dfu-util override given. " + "Install omotion (pip install -e ../openmotion-sdk), install " + "dfu-util on PATH (Windows: scoop install dfu-util), or pass " + "--dfu-util PATH." + ) + return found + + +DFU_VID_PID_STR = "0483:df11" + + +def wait_for_dfu_device(dfu_util: str, timeout: float = 10.0, + poll_interval: float = 0.3) -> bool: + """Poll `dfu-util --list` until the STM32 DFU device appears or timeout. + + Returns True if the device was found, False on timeout. + """ + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + try: + result = subprocess.run( + [dfu_util, "--list"], + capture_output=True, text=True, check=False, + ) + if DFU_VID_PID_STR in result.stdout.lower(): + return True + except FileNotFoundError: + # dfu-util went missing between resolve and now — fail fast + return False + time.sleep(poll_interval) + return False diff --git a/scripts/deploy.py b/scripts/deploy.py new file mode 100644 index 0000000..9d0d6a2 --- /dev/null +++ b/scripts/deploy.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +"""deploy.py — Build, DFU-flash, and recover a MOTION sensor module. + +Usage: + python scripts/deploy.py --device left|right + [--config Debug|Release] [--no-build] + [--dfu-util PATH] [--post-reset] [--no-confirm] +""" +from __future__ import annotations + +import argparse +import subprocess +import sys +import time +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(REPO_ROOT / "scripts")) + +from _deploy_helpers import ( # noqa: E402 + bin_path_for, + read_project_name, + resolve_dfu_util, + wait_for_dfu_device, + DFU_VID_PID_STR, +) + +COMEBACK_TIMEOUT_S = 10.0 +DFU_ENUM_TIMEOUT_S = 10.0 +FLASH_BASE = 0x08000000 + + +def parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Build, enter DFU, flash, and reset a MOTION sensor module." + ) + p.add_argument("--device", choices=("left", "right"), required=True, + help="Which sensor side to flash (required to avoid wrong-side mistakes)") + p.add_argument("--config", choices=("Debug", "Release"), default="Debug") + p.add_argument("--no-build", action="store_true") + p.add_argument("--dfu-util", default=None) + p.add_argument("--post-reset", action="store_true", + help="If sensor doesn't re-enumerate after leaveDFU, print extra hints") + p.add_argument("--no-confirm", action="store_true") + return p.parse_args() + + +def _run(cmd: list[str], **kw) -> int: + print(f"+ {' '.join(cmd)}") + return subprocess.call(cmd, **kw) + + +def _git_describe() -> str: + try: + out = subprocess.check_output( + ["git", "describe", "--tags", "--dirty", "--always"], + cwd=REPO_ROOT, text=True, + ) + return out.strip() + except Exception: + return "unknown" + + +def _confirm(device: str, bin_file: Path) -> bool: + print() + print(f" Device : {device} sensor") + print(f" Binary : {bin_file} ({bin_file.stat().st_size} bytes)") + print(f" Source : {_git_describe()}") + print() + answer = input(f"Deploy {device} sensor? (y/N): ").strip().lower() + return answer == "y" + + +def _build(config: str) -> None: + build_dir = REPO_ROOT / "build" / config + if not build_dir.exists(): + raise RuntimeError( + f"Build dir {build_dir} does not exist. Run cmake configure first." + ) + rc = _run(["cmake", "--build", str(build_dir), "--config", config]) + if rc != 0: + raise RuntimeError(f"cmake --build failed with exit code {rc}") + + +def _sensor_handle(interface, device: str): + return interface.left if device == "left" else interface.right + + +def _enter_dfu_sensor(device: str, timeout: float) -> bool: + """Trigger the named sensor into DFU mode via omotion.""" + from omotion import MotionInterface + + interface = MotionInterface() + interface.start(wait=False) + try: + sensor = _sensor_handle(interface, device) + # start(wait=True) only blocks on already-CONNECTING handles, so it + # races against the connection monitor's first sweep. Poll the + # specific handle until CONNECTED or timeout. + deadline = time.monotonic() + timeout + while time.monotonic() < deadline and not sensor.is_connected(): + time.sleep(0.2) + if not sensor.is_connected(): + print(f"❌ {device} sensor not connected — cannot trigger DFU.") + return False + + print(f"[*] Requesting DFU mode on {device} sensor …") + try: + return bool(sensor.enter_dfu()) + except Exception as exc: + print(f"❌ enter_dfu raised: {exc}") + return False + finally: + interface.stop() + + +def _wait_for_sensor_comeback(device: str, timeout: float) -> bool: + """Construct the interface ONCE and poll the sensor handle.""" + from omotion import MotionInterface + + interface = MotionInterface() + interface.start(wait=False) + try: + sensor = _sensor_handle(interface, device) + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + if sensor.is_connected(): + return True + time.sleep(0.5) + return False + finally: + interface.stop() + + +def _force_utf8_stdout() -> None: + """Windows default cp1252 stdout chokes on the emoji glyphs printed + below. Reconfigure to UTF-8 with replace-on-error so a missing-glyph + terminal degrades gracefully instead of crashing the script.""" + for stream in (sys.stdout, sys.stderr): + if hasattr(stream, "reconfigure"): + try: + stream.reconfigure(encoding="utf-8", errors="replace") + except Exception: + pass + + +def main() -> int: + _force_utf8_stdout() + args = parse_args() + + try: + project = read_project_name(REPO_ROOT / "CMakeLists.txt") + bin_file = bin_path_for(REPO_ROOT, args.config, project) + dfu_util = resolve_dfu_util(args.dfu_util) + except RuntimeError as e: + print(f"❌ {e}") + return 1 + + try: + import omotion # noqa: F401 + except ImportError: + print("❌ omotion not installed in this Python env. " + "Run `pip install -e ../openmotion-sdk` (path may differ).") + return 1 + + if not args.no_build: + try: + _build(args.config) + except RuntimeError as e: + print(f"❌ {e}") + return 1 + + if not bin_file.exists(): + print(f"❌ Binary not found: {bin_file}") + return 1 + + if not args.no_confirm: + if not _confirm(args.device, bin_file): + print("Aborted.") + return 0 + + if not _enter_dfu_sensor(args.device, timeout=DFU_ENUM_TIMEOUT_S): + return 1 + + print(f"[*] Waiting for DFU device ({DFU_VID_PID_STR}) to enumerate …") + if not wait_for_dfu_device(dfu_util, timeout=DFU_ENUM_TIMEOUT_S): + print("❌ DFU device did not appear within " + f"{DFU_ENUM_TIMEOUT_S:.0f}s.") + print(" On Windows, bind WinUSB to the STM32 DFU interface " + "(PID 0xDF11) via Zadig.") + return 1 + + print(f"[*] Flashing {bin_file.name} via dfu-util …") + cmd = [ + dfu_util, + "-d", DFU_VID_PID_STR, + "-a", "0", + "-s", f"0x{FLASH_BASE:08x}:leave", + "-D", str(bin_file), + ] + rc = _run(cmd) + # dfu-util exit 74 ("Error during download get_status") after a successful + # download is a known STM32 ROM bootloader quirk: the device jumps to user + # firmware on :leave before dfu-util can read its final status. Trust the + # device's comeback as the source of truth rather than the exit code. + if rc != 0: + print(f"[!] dfu-util exited {rc}; checking whether {args.device} sensor came back …") + + print(f"[*] leaveDFU sent. Waiting for {args.device} sensor to come back …") + if _wait_for_sensor_comeback(args.device, timeout=COMEBACK_TIMEOUT_S): + print(f"✅ {args.device.capitalize()} sensor is back online.") + return 0 + + # No software path to power-cycle a single sensor — surface a clear hint. + print(f"⚠️ {args.device} sensor did not re-enumerate.") + print(" Please toggle power on the console " + "(this power-cycles the sensors).") + if args.post_reset: + print(" --post-reset: no additional automatic recovery is available " + "for sensors; manual power cycle required.") + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/startup_stm32h743xx.s b/startup_stm32h743xx.s index 2663d53..9e8e160 100644 --- a/startup_stm32h743xx.s +++ b/startup_stm32h743xx.s @@ -65,6 +65,12 @@ Reset_Handler: /* Call the clock system initialization function.*/ bl SystemInit +/* Seed ECC syndromes on every SRAM bank before .data/.bss init so that + the first byte/halfword store to a never-written 64-bit line does not + latch spurious SEDCF+DEDF in the RAMECC controller. ram_scrub() uses + only its own stack frame and no global/static state. */ + bl ram_scrub + /* Copy the data segment initializers from flash to SRAM */ ldr r0, =_sdata ldr r1, =_edata diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..51d9484 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,8 @@ +"""Add the scripts/ directory to sys.path for tests.""" +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +SCRIPTS = ROOT / "scripts" +if str(SCRIPTS) not in sys.path: + sys.path.insert(0, str(SCRIPTS)) diff --git a/tests/test_deploy_helpers.py b/tests/test_deploy_helpers.py new file mode 100644 index 0000000..9558fc1 --- /dev/null +++ b/tests/test_deploy_helpers.py @@ -0,0 +1,91 @@ +import shutil +import subprocess +import textwrap +from pathlib import Path +from unittest.mock import patch + +import pytest + +from _deploy_helpers import ( + read_project_name, + bin_path_for, + resolve_dfu_util, + wait_for_dfu_device, +) + + +def test_read_project_name_extracts_from_set_directive(tmp_path: Path): + cmake = tmp_path / "CMakeLists.txt" + cmake.write_text(textwrap.dedent(""" + cmake_minimum_required(VERSION 3.22) + set(CMAKE_PROJECT_NAME motion-console-fw) + project(${CMAKE_PROJECT_NAME}) + """)) + assert read_project_name(cmake) == "motion-console-fw" + + +def test_read_project_name_raises_when_missing(tmp_path: Path): + cmake = tmp_path / "CMakeLists.txt" + cmake.write_text("# nothing here\n") + with pytest.raises(RuntimeError, match="CMAKE_PROJECT_NAME"): + read_project_name(cmake) + + +def test_bin_path_for_returns_repo_relative(tmp_path: Path): + repo = tmp_path + result = bin_path_for(repo, "Debug", "motion-console-fw") + assert result == repo / "build" / "Debug" / "motion-console-fw.bin" + + +def test_resolve_dfu_util_uses_override_when_given(tmp_path: Path): + fake = tmp_path / "dfu-util" + fake.write_text("#!/bin/sh\necho dfu-util fake\n") + fake.chmod(0o755) + assert resolve_dfu_util(str(fake)) == str(fake) + + +def test_resolve_dfu_util_raises_when_missing(monkeypatch): + monkeypatch.setattr(shutil, "which", lambda _: None) + monkeypatch.setattr("_deploy_helpers._bundled_dfu_util", lambda: None) + with pytest.raises(RuntimeError, match="dfu-util not found"): + resolve_dfu_util(None) + + +def test_resolve_dfu_util_raises_when_override_does_not_exist(tmp_path: Path): + nonexistent = tmp_path / "does-not-exist" / "dfu-util" + with pytest.raises(RuntimeError, match="dfu-util override path"): + resolve_dfu_util(str(nonexistent)) + + +def test_resolve_dfu_util_prefers_bundled_over_path(tmp_path: Path, monkeypatch): + bundled = tmp_path / "bundled" / "dfu-util.exe" + bundled.parent.mkdir(parents=True) + bundled.write_text("") + monkeypatch.setattr("_deploy_helpers._bundled_dfu_util", lambda: bundled) + monkeypatch.setattr(shutil, "which", lambda _: "C:/path/dfu-util.exe") + assert resolve_dfu_util(None) == str(bundled) + + +def _fake_run_returning(stdout: str, returncode: int = 0): + def _run(cmd, **kw): + return subprocess.CompletedProcess(cmd, returncode, stdout=stdout, stderr="") + return _run + + +def test_wait_for_dfu_device_succeeds_when_listed(): + with patch("_deploy_helpers.subprocess.run", + side_effect=_fake_run_returning("Found DFU: [0483:df11] ...\n")): + assert wait_for_dfu_device("dfu-util", timeout=0.5, poll_interval=0.05) is True + + +def test_wait_for_dfu_device_times_out_when_absent(): + with patch("_deploy_helpers.subprocess.run", + side_effect=_fake_run_returning("No DFU capable USB device available\n")): + assert wait_for_dfu_device("dfu-util", timeout=0.2, poll_interval=0.05) is False + + +def test_wait_for_dfu_device_returns_false_when_dfu_util_missing(): + with patch("_deploy_helpers.subprocess.run", + side_effect=FileNotFoundError("dfu-util gone")): + assert wait_for_dfu_device("dfu-util", timeout=0.5, + poll_interval=0.05) is False