diff --git a/.mxproject b/.mxproject index c8d7e28..3b8618e 100644 --- a/.mxproject +++ b/.mxproject @@ -1,3 +1,11 @@ +[PreviousLibFiles] +LibFiles=Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_tim.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_tim.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_tim_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_def.h;Drivers/STM32L0xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_i2c.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_i2c_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_rcc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_rcc_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_bus.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_rcc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_crs.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_system.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_utils.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash_ramfunc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_gpio.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_gpio_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_gpio.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_dma.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_dma_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_dma.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_pwr.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_pwr_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_pwr.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_cortex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_cortex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_exti.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_exti.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_uart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_usart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_lpuart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_uart_ex.h;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ramfunc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_gpio.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_dma.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_cortex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_exti.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart_ex.c;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_tim.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_tim.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_tim_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_def.h;Drivers/STM32L0xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_i2c.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_i2c_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_rcc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_rcc_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_bus.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_rcc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_crs.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_system.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_utils.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash_ramfunc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_gpio.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_gpio_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_gpio.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_dma.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_dma_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_dma.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_pwr.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_pwr_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_pwr.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_cortex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_cortex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_exti.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_exti.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_uart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_usart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_lpuart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_uart_ex.h;Drivers/CMSIS/Device/ST/STM32L0xx/Include/stm32l073xx.h;Drivers/CMSIS/Device/ST/STM32L0xx/Include/stm32l0xx.h;Drivers/CMSIS/Device/ST/STM32L0xx/Include/system_stm32l0xx.h;Drivers/CMSIS/Device/ST/STM32L0xx/Include/system_stm32l0xx.h;Drivers/CMSIS/Device/ST/STM32L0xx/Source/Templates/system_stm32l0xx.c;Drivers/CMSIS/Include/core_cm7.h;Drivers/CMSIS/Include/tz_context.h;Drivers/CMSIS/Include/core_cm3.h;Drivers/CMSIS/Include/cmsis_compiler.h;Drivers/CMSIS/Include/cmsis_armclang.h;Drivers/CMSIS/Include/mpu_armv7.h;Drivers/CMSIS/Include/cmsis_armcc.h;Drivers/CMSIS/Include/core_cm4.h;Drivers/CMSIS/Include/core_cm0.h;Drivers/CMSIS/Include/cmsis_iccarm.h;Drivers/CMSIS/Include/core_armv8mml.h;Drivers/CMSIS/Include/core_sc000.h;Drivers/CMSIS/Include/core_cm1.h;Drivers/CMSIS/Include/mpu_armv8.h;Drivers/CMSIS/Include/core_sc300.h;Drivers/CMSIS/Include/cmsis_gcc.h;Drivers/CMSIS/Include/cmsis_version.h;Drivers/CMSIS/Include/core_cm23.h;Drivers/CMSIS/Include/core_cm33.h;Drivers/CMSIS/Include/core_cm0plus.h;Drivers/CMSIS/Include/core_armv8mbl.h; + +[PreviousUsedCMakes] +SourceFiles=Core/Src/main.c;Core/Src/gpio.c;Core/Src/tim.c;Core/Src/usart.c;Core/Src/stm32l0xx_it.c;Core/Src/stm32l0xx_hal_msp.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ramfunc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_gpio.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_dma.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_cortex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_exti.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart_ex.c;Drivers/CMSIS/Device/ST/STM32L0xx/Source/Templates/system_stm32l0xx.c;Core/Src/system_stm32l0xx.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ramfunc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_gpio.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_dma.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_cortex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_exti.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart_ex.c;Drivers/CMSIS/Device/ST/STM32L0xx/Source/Templates/system_stm32l0xx.c;Core/Src/system_stm32l0xx.c;;; +HeaderPath=Drivers/STM32L0xx_HAL_Driver/Inc;Drivers/STM32L0xx_HAL_Driver/Inc/Legacy;Drivers/CMSIS/Device/ST/STM32L0xx/Include;Drivers/CMSIS/Include;Core/Inc; +CDefines=USE_HAL_DRIVER;STM32L073xx;USE_HAL_DRIVER;USE_HAL_DRIVER; + [PreviousGenFiles] AdvancedFolderStructure=true HeaderFileListSize=6 @@ -21,11 +29,3 @@ SourceFolderListSize=1 SourcePath#0=../Core/Src SourceFiles=; -[PreviousLibFiles] -LibFiles=Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_tim.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_tim.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_tim_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_def.h;Drivers/STM32L0xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_i2c.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_i2c_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_rcc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_rcc_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_bus.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_rcc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_crs.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_system.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_utils.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash_ramfunc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_gpio.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_gpio_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_gpio.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_dma.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_dma_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_dma.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_pwr.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_pwr_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_pwr.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_cortex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_cortex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_exti.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_exti.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_uart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_usart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_lpuart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_uart_ex.h;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ramfunc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_gpio.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_dma.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_cortex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_exti.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart_ex.c;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_tim.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_tim.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_tim_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_def.h;Drivers/STM32L0xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_i2c.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_i2c_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_rcc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_rcc_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_bus.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_rcc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_crs.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_system.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_utils.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash_ramfunc.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_flash_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_gpio.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_gpio_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_gpio.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_dma.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_dma_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_dma.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_pwr.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_pwr_ex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_pwr.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_cortex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_cortex.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_exti.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_exti.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_uart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_usart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_ll_lpuart.h;Drivers/STM32L0xx_HAL_Driver/Inc/stm32l0xx_hal_uart_ex.h;Drivers/CMSIS/Device/ST/STM32L0xx/Include/stm32l073xx.h;Drivers/CMSIS/Device/ST/STM32L0xx/Include/stm32l0xx.h;Drivers/CMSIS/Device/ST/STM32L0xx/Include/system_stm32l0xx.h;Drivers/CMSIS/Device/ST/STM32L0xx/Include/system_stm32l0xx.h;Drivers/CMSIS/Device/ST/STM32L0xx/Source/Templates/system_stm32l0xx.c;Drivers/CMSIS/Include/core_cm7.h;Drivers/CMSIS/Include/tz_context.h;Drivers/CMSIS/Include/core_cm3.h;Drivers/CMSIS/Include/cmsis_compiler.h;Drivers/CMSIS/Include/cmsis_armclang.h;Drivers/CMSIS/Include/mpu_armv7.h;Drivers/CMSIS/Include/cmsis_armcc.h;Drivers/CMSIS/Include/core_cm4.h;Drivers/CMSIS/Include/core_cm0.h;Drivers/CMSIS/Include/cmsis_iccarm.h;Drivers/CMSIS/Include/core_armv8mml.h;Drivers/CMSIS/Include/core_sc000.h;Drivers/CMSIS/Include/core_cm1.h;Drivers/CMSIS/Include/mpu_armv8.h;Drivers/CMSIS/Include/core_sc300.h;Drivers/CMSIS/Include/cmsis_gcc.h;Drivers/CMSIS/Include/cmsis_version.h;Drivers/CMSIS/Include/core_cm23.h;Drivers/CMSIS/Include/core_cm33.h;Drivers/CMSIS/Include/core_cm0plus.h;Drivers/CMSIS/Include/core_armv8mbl.h; - -[PreviousUsedCMakes] -SourceFiles=Core/Src/main.c;Core/Src/gpio.c;Core/Src/tim.c;Core/Src/usart.c;Core/Src/stm32l0xx_it.c;Core/Src/stm32l0xx_hal_msp.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ramfunc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_gpio.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_dma.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_cortex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_exti.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart_ex.c;Drivers/CMSIS/Device/ST/STM32L0xx/Source/Templates/system_stm32l0xx.c;Core/Src/system_stm32l0xx.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_tim_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_i2c_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ramfunc.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_flash_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_gpio.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_dma.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_pwr_ex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_cortex.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_exti.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart.c;Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_uart_ex.c;Drivers/CMSIS/Device/ST/STM32L0xx/Source/Templates/system_stm32l0xx.c;Core/Src/system_stm32l0xx.c;;; -HeaderPath=Drivers/STM32L0xx_HAL_Driver/Inc;Drivers/STM32L0xx_HAL_Driver/Inc/Legacy;Drivers/CMSIS/Device/ST/STM32L0xx/Include;Drivers/CMSIS/Include;Core/Inc; -CDefines=USE_HAL_DRIVER;STM32L073xx;USE_HAL_DRIVER;USE_HAL_DRIVER; - diff --git a/Core/Inc/main.h b/Core/Inc/main.h index f51d01c..dd74f5b 100644 --- a/Core/Inc/main.h +++ b/Core/Inc/main.h @@ -61,28 +61,30 @@ void Error_Handler(void); #define B1_GPIO_Port GPIOC #define MCO_Pin GPIO_PIN_0 #define MCO_GPIO_Port GPIOH -#define CLK_Pin GPIO_PIN_0 -#define CLK_GPIO_Port GPIOC -#define T3_Pin GPIO_PIN_1 -#define T3_GPIO_Port GPIOC -#define T2_Pin GPIO_PIN_3 -#define T2_GPIO_Port GPIOC +#define G1_Pin GPIO_PIN_0 +#define G1_GPIO_Port GPIOC +#define G2_Pin GPIO_PIN_1 +#define G2_GPIO_Port GPIOC +#define G9_Pin GPIO_PIN_2 +#define G9_GPIO_Port GPIOC +#define G3_Pin GPIO_PIN_3 +#define G3_GPIO_Port GPIOC #define USART_TX_Pin GPIO_PIN_2 #define USART_TX_GPIO_Port GPIOA #define USART_RX_Pin GPIO_PIN_3 #define USART_RX_GPIO_Port GPIOA #define LD2_Pin GPIO_PIN_5 #define LD2_GPIO_Port GPIOA -#define T1_Pin GPIO_PIN_4 -#define T1_GPIO_Port GPIOC -#define H3_Pin GPIO_PIN_5 -#define H3_GPIO_Port GPIOC -#define H2_Pin GPIO_PIN_6 -#define H2_GPIO_Port GPIOC -#define H1_Pin GPIO_PIN_7 -#define H1_GPIO_Port GPIOC -#define H0_Pin GPIO_PIN_8 -#define H0_GPIO_Port GPIOC +#define G4_Pin GPIO_PIN_4 +#define G4_GPIO_Port GPIOC +#define G5_Pin GPIO_PIN_5 +#define G5_GPIO_Port GPIOC +#define G6_Pin GPIO_PIN_6 +#define G6_GPIO_Port GPIOC +#define G7_Pin GPIO_PIN_7 +#define G7_GPIO_Port GPIOC +#define G8_Pin GPIO_PIN_8 +#define G8_GPIO_Port GPIOC #define TMS_Pin GPIO_PIN_13 #define TMS_GPIO_Port GPIOA #define TCK_Pin GPIO_PIN_14 diff --git a/Core/Src/gpio.c b/Core/Src/gpio.c index 933d89c..00019a3 100644 --- a/Core/Src/gpio.c +++ b/Core/Src/gpio.c @@ -50,8 +50,8 @@ void MX_GPIO_Init(void) __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(GPIOC, CLK_Pin|T3_Pin|T2_Pin|T1_Pin - |H3_Pin|H2_Pin|H1_Pin|H0_Pin, GPIO_PIN_RESET); + HAL_GPIO_WritePin(GPIOC, G1_Pin|G2_Pin|G3_Pin|G4_Pin + |G5_Pin|G6_Pin|G7_Pin|G8_Pin|G9_Pin, GPIO_PIN_SET); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET); @@ -62,10 +62,10 @@ void MX_GPIO_Init(void) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct); - /*Configure GPIO pins : CLK_Pin T3_Pin T2_Pin T1_Pin - H3_Pin H2_Pin H1_Pin H0_Pin */ - GPIO_InitStruct.Pin = CLK_Pin|T3_Pin|T2_Pin|T1_Pin - |H3_Pin|H2_Pin|H1_Pin|H0_Pin; + /*Configure GPIO pins : G1_Pin G2_Pin G3_Pin G4_Pin + G5_Pin G6_Pin G7_Pin G8_Pin G9_Pin */ + GPIO_InitStruct.Pin = G1_Pin|G2_Pin|G3_Pin|G4_Pin + |G5_Pin|G6_Pin|G7_Pin|G8_Pin|G9_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; diff --git a/Core/Src/main.c b/Core/Src/main.c index e3ab923..8117ed3 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -24,7 +24,7 @@ /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ - +#include /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -45,7 +45,17 @@ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ +#define LED_MASK 0x01FFu /* PC0..PC8 */ +#define N_POINTS 9 +#define COUNT_MIN 1 +#define COUNT_MAX 15 +#define TICKS_BURST 30 /* 15회 x 2틱 = 6.0 s */ +#define TICKS_IDLE 10 /* 2.0 s */ +#define TICKS_CYCLE (TICKS_BURST + TICKS_IDLE) +static uint16_t tick = 0; +static uint16_t cycle = 0; +static uint8_t cnt[N_POINTS]; /* 지점별 목표 블링크 횟수 */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ @@ -56,7 +66,27 @@ void SystemClock_Config(void); /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ +/* printf -> USART2 (ST-LINK VCP) */ +int __io_putchar(int ch) +{ + HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY); + return ch; +} +/* 결정적 회전 패턴: 지점마다 다른 값, 사이클마다 1씩 이동 */ +static void load_counts(void) +{ + for (uint8_t k = 0; k < N_POINTS; k++) { + cnt[k] = (uint8_t)(((cycle + k) % COUNT_MAX) + COUNT_MIN); + } +} + +static void drive(uint16_t bits) +{ + /* 오픈 드레인: 0 = 점등이므로 반전. 9핀 원자적 동시 전환 */ + GPIOC->BSRR = ((uint32_t)( bits & LED_MASK) << 16) + | ((uint32_t)(~bits & LED_MASK)); +} /* USER CODE END 0 */ /** @@ -91,7 +121,9 @@ int main(void) MX_TIM6_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ - + load_counts(); + drive(0); /* 전 지점 소등 */ + HAL_TIM_Base_Start_IT(&htim6); /* USER CODE END 2 */ /* Infinite loop */ @@ -153,7 +185,33 @@ void SystemClock_Config(void) } /* USER CODE BEGIN 4 */ +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) +{ + if (htim->Instance != TIM6) return; + uint16_t bits = 0; + + /* 버스트 구간의 짝수 틱에서만 점등. + 지점 k의 j번째 블링크는 틱 2j(점등) / 2j+1(소등)을 점유 */ + if (tick < TICKS_BURST && (tick & 1u) == 0u) { + for (uint8_t k = 0; k < N_POINTS; k++) { + if (tick < (uint16_t)cnt[k] * 2u) { + bits |= (uint16_t)(1u << k); + } + } + } + drive(bits); + + if (++tick >= TICKS_CYCLE) { + tick = 0; + cycle++; + load_counts(); + + printf("CYCLE=%u G:", cycle); + for (uint8_t k = 0; k < N_POINTS; k++) printf(" %2u", cnt[k]); + printf("\r\n"); + } +} /* USER CODE END 4 */ /** diff --git a/IR_test_firmware.ioc b/IR_test_firmware.ioc index e9e662d..a2430f6 100644 --- a/IR_test_firmware.ioc +++ b/IR_test_firmware.ioc @@ -26,6 +26,7 @@ Mcu.Pin15=PA13 Mcu.Pin16=PA14 Mcu.Pin17=VP_SYS_VS_Systick Mcu.Pin18=VP_TIM6_VS_ClockSourceINT +Mcu.Pin19=PC2 Mcu.Pin2=PC15-OSC32_OUT Mcu.Pin3=PH0-OSC_IN Mcu.Pin4=PC0 @@ -34,7 +35,7 @@ Mcu.Pin6=PC3 Mcu.Pin7=PA2 Mcu.Pin8=PA3 Mcu.Pin9=PA5 -Mcu.PinsNb=19 +Mcu.PinsNb=20 Mcu.ThirdPartyNb=0 Mcu.UserConstants= Mcu.UserName=STM32L073RZTx @@ -73,17 +74,19 @@ PA5.GPIO_ModeDefaultOutputPP=GPIO_MODE_OUTPUT_PP PA5.GPIO_PuPd=GPIO_NOPULL PA5.Locked=true PA5.Signal=GPIO_Output -PC0.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP -PC0.GPIO_Label=CLK +PC0.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP,PinState +PC0.GPIO_Label=G1 PC0.GPIO_ModeDefaultOutputPP=GPIO_MODE_OUTPUT_OD PC0.GPIO_PuPd=GPIO_NOPULL PC0.Locked=true +PC0.PinState=GPIO_PIN_SET PC0.Signal=GPIO_Output -PC1.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP -PC1.GPIO_Label=T3 +PC1.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP,PinState +PC1.GPIO_Label=G2 PC1.GPIO_ModeDefaultOutputPP=GPIO_MODE_OUTPUT_OD PC1.GPIO_PuPd=GPIO_NOPULL PC1.Locked=true +PC1.PinState=GPIO_PIN_SET PC1.Signal=GPIO_Output PC13.GPIOParameters=GPIO_Label,GPIO_ModeDefaultEXTI PC13.GPIO_Label=B1 [Blue PushButton] @@ -96,41 +99,54 @@ PC14-OSC32_IN.Signal=RCC_OSC32_IN PC15-OSC32_OUT.Locked=true PC15-OSC32_OUT.Mode=LSE-External-Oscillator PC15-OSC32_OUT.Signal=RCC_OSC32_OUT -PC3.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP -PC3.GPIO_Label=T2 +PC2.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP,PinState +PC2.GPIO_Label=G9 +PC2.GPIO_ModeDefaultOutputPP=GPIO_MODE_OUTPUT_OD +PC2.GPIO_PuPd=GPIO_NOPULL +PC2.Locked=true +PC2.PinState=GPIO_PIN_SET +PC2.Signal=GPIO_Output +PC3.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP,PinState +PC3.GPIO_Label=G3 PC3.GPIO_ModeDefaultOutputPP=GPIO_MODE_OUTPUT_OD PC3.GPIO_PuPd=GPIO_NOPULL PC3.Locked=true +PC3.PinState=GPIO_PIN_SET PC3.Signal=GPIO_Output -PC4.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP -PC4.GPIO_Label=T1 +PC4.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP,PinState +PC4.GPIO_Label=G4 PC4.GPIO_ModeDefaultOutputPP=GPIO_MODE_OUTPUT_OD PC4.GPIO_PuPd=GPIO_NOPULL PC4.Locked=true +PC4.PinState=GPIO_PIN_SET PC4.Signal=GPIO_Output -PC5.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP -PC5.GPIO_Label=H3 +PC5.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP,PinState +PC5.GPIO_Label=G5 PC5.GPIO_ModeDefaultOutputPP=GPIO_MODE_OUTPUT_OD PC5.GPIO_PuPd=GPIO_NOPULL PC5.Locked=true +PC5.PinState=GPIO_PIN_SET PC5.Signal=GPIO_Output -PC6.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP -PC6.GPIO_Label=H2 +PC6.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP,PinState +PC6.GPIO_Label=G6 PC6.GPIO_ModeDefaultOutputPP=GPIO_MODE_OUTPUT_OD PC6.GPIO_PuPd=GPIO_NOPULL PC6.Locked=true +PC6.PinState=GPIO_PIN_SET PC6.Signal=GPIO_Output -PC7.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP -PC7.GPIO_Label=H1 +PC7.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP,PinState +PC7.GPIO_Label=G7 PC7.GPIO_ModeDefaultOutputPP=GPIO_MODE_OUTPUT_OD PC7.GPIO_PuPd=GPIO_NOPULL PC7.Locked=true +PC7.PinState=GPIO_PIN_SET PC7.Signal=GPIO_Output -PC8.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP -PC8.GPIO_Label=H0 +PC8.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultOutputPP,PinState +PC8.GPIO_Label=G8 PC8.GPIO_ModeDefaultOutputPP=GPIO_MODE_OUTPUT_OD PC8.GPIO_PuPd=GPIO_NOPULL PC8.Locked=true +PC8.PinState=GPIO_PIN_SET PC8.Signal=GPIO_Output PCC.Checker=false PCC.Line=STM32L0x3 diff --git a/doc/UM1724.PDF b/doc/UM1724.PDF new file mode 100644 index 0000000..eb56ab4 Binary files /dev/null and b/doc/UM1724.PDF differ diff --git a/doc/image.png b/doc/image.png new file mode 100644 index 0000000..71cc117 Binary files /dev/null and b/doc/image.png differ diff --git a/doc/model.md b/doc/model.md new file mode 100644 index 0000000..c9ee143 --- /dev/null +++ b/doc/model.md @@ -0,0 +1 @@ +![alt text](image.png) \ No newline at end of file diff --git a/doc/readme.md b/doc/readme.md index c0e136b..a70f984 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -1,16 +1,39 @@ # IR 광신호 Mock 프로토타입 설계 정리 **대상 과제:** IR 광신호 기반 무선 토양수분 센서 (농촌진흥청 국립원예특작과학원) -**본 문서 범위:** 센서 미개발 상태에서 온도·습도 mock 신호를 IR LED로 송출하고 카메라로 인식하는 벤치 프로토타입 +**본 문서 범위:** 격자 지점별 LED 블링크 횟수를 카메라로 카운트하는 벤치 프로토타입 **작성 기준일:** 2026-08-22 +**개정:** rev.2 — 온·습도 비트 인코딩 → **습도 단일 값, 블링크 카운트 방식**으로 변경 +**구현 상태:** 7장의 펌웨어가 저장소에 구현되고 빌드 검증까지 완료됨(2026-08-22). CubeMX 생성 결과와 설계 간 불일치 2건을 구현 중 발견·수정 — [6.7절](#67-cubemx-생성-결과-검증-중-발견된-오차) 참조. --- ## 1. 프로토타입 목표 -각 식물 팟에 온·습도 센서가 내장되고, 그 값을 IR LED 광신호로 변환해 외부 카메라가 판독하는 시스템을 검증한다. 실제 센서가 아직 없으므로 **MCU가 결정적 패턴의 mock 값을 생성**하여 송출하고, 카메라 측 디코더가 이를 정확히 복원하는지 확인하는 것이 이번 단계의 목적이다. +각 식물 팟에 습도 센서가 내장되고, 그 값을 IR LED 광신호로 변환해 외부 카메라가 판독하는 시스템을 검증한다. -배터리 수명, 방수, 양산성은 **본 단계에서 고려하지 않는다.** +**본 단계에서 검증하는 것은 값의 의미가 아니라 계수(計數)의 신뢰성이다.** + +- 실제 습도값은 사용하지 않는다 +- 온도는 다루지 않는다 +- 카메라가 **격자 각 지점의 깜박임 횟수를 정확히 세는지**만 확인한다 + +배터리 수명, 방수, 양산성은 본 단계에서 고려하지 않는다. + +### 1.1 카운트 방식을 채택한 의의 + +비트 인코딩 대비 이점이 분명하다. + +| 항목 | 비트 인코딩 | 블링크 카운트 | +|---|---|---| +| 프레임 정렬 | 심볼 경계 판정 필요 | **불필요** | +| 임계값 | 비트마다 판정 | 지점마다 1회 | +| 프레임 누락 | 심볼 전체 손실 | 상태 유지 구간이 길어 영향 미미 | +| 검증 대상 | 광학계 + 프로토콜 혼재 | **광학계만 순수 분리** | + +이번 단계에서 알고 싶은 것은 "카메라가 9개 지점을 안정적으로 분리 해상하고 각 지점의 시간적 상태를 추적할 수 있는가"이다. 카운트 방식은 프로토콜 계층을 걷어내고 이 질문에만 답한다. + +또한 **고정 창(window) 내의 카운트는 곧 주파수**이므로, 제안서의 1~6Hz 주파수 변조 방식으로 자연스럽게 확장된다. 버린 설계가 아니라 하위 단계다. --- @@ -22,7 +45,8 @@ | LED | Siemens LD274 (950nm) × 9 | 기 확보, 추후 변경 예정 | | 카메라 필터 | 950nm, C 마운트 | LD274 피크 파장과 정합 | | 셔터 방식 | 글로벌 셔터 | 확인 완료 | -| 인코딩 | 공간 병렬 (3×3 동시) | 벤치 환경이라 분리 해상 가능 | +| 격자 구성 | 3×3, 9개 지점 | 각 지점이 팟 1개에 대응 | +| 전송 방식 | **지점별 블링크 카운트** | 본 개정 | | 구동 방식 | GPIO 오픈 드레인 직결 | 근거리라 저전류로 충분 | | 테스트 거리 | 1 ~ 1.5 m | 블롭 분리 여유 확보 | @@ -47,6 +71,8 @@ | 패키지 | 5 mm (T-1 3/4), 지름 5.1 mm | | 스위칭 시간 t_r, t_f | 1 µs | +스위칭 시간 1 µs는 본 설계의 200 ms 상태 유지 시간 대비 5자리수 여유이므로 전혀 제약이 되지 않는다. + ### 3.2 파장 정합 — 양호 950nm 필터와 LD274의 950nm 피크가 일치한다. 스펙트럼 폭 55nm를 감안해도 에너지가 대략 920~990nm에 몰려 있어 필터 통과에 문제가 없다. @@ -81,7 +107,7 @@ LD274는 TV 리모컨용으로 설계된 협각 부품이다. 방사 특성상 ### 4.1 구동 전류 및 저항 산출 -벤치 1m 환경에서는 오히려 **과다 광량이 문제**가 된다. 포화된 블롭이 번지면(blooming) 인접 LED와 뭉쳐 공간 병렬 인코딩이 깨진다. +벤치 1m 환경에서는 오히려 **과다 광량이 문제**가 된다. 포화된 블롭이 번지면(blooming) 인접 지점과 뭉쳐 격자 분리가 깨진다. ``` 목표 전류: 6 mA @@ -110,6 +136,8 @@ STM32L073 GPIO는 정규 스펙에서 핀당 ±8 mA, 비정규 V_OL/V_OH를 감 전체 I/O 합산 전류(ΣI_VSS) 한계는 데이터시트 절대최대정격 표에서 확인할 것. +**카운트 방식의 부수적 이점:** 지점마다 카운트가 다르므로 9개가 동시에 켜져 있는 순간은 첫 번째 블링크뿐이고, 이후로는 켜진 개수가 점차 줄어든다. 평균 전류가 최대치보다 낮다. + ### 4.3 전원 경로 LED 전류를 Nucleo의 3.3V 핀에서 뽑는 것은 무방하나(54mA 수준), 향후 전류를 크게 올릴 경우 주의가 필요하다. @@ -148,67 +176,129 @@ VDD 3.3V ──┬── LED(애노드) ... LED(캐소드) ── 330Ω ── P --- -## 5. 프로토콜 설계 +## 5. 프로토콜 설계 — 블링크 카운트 -### 5.1 비트 배치 +### 5.1 격자 지점 매핑 -| 위치 | 핀 | 역할 | +각 핀은 격자 한 지점(팟 1개)에 대응한다. 비트 자리가 아니라 **독립된 측정 지점**이다. + +| 격자 위치 | 핀 | 지점 번호 | |---|---|---| -| 중앙 | PC0 | **토글 클록** | -| 상단 좌 | PC1 | 온도 b3 (MSB) | -| 상단 중 | PC2 | 온도 b2 | -| 상단 우 | PC3 | 온도 b1 | -| 중단 좌 | PC4 | 온도 b0 (LSB) | -| 중단 우 | PC5 | 습도 b3 (MSB) | -| 하단 좌 | PC6 | 습도 b2 | -| 하단 중 | PC7 | 습도 b1 | -| 하단 우 | PC8 | 습도 b0 (LSB) | +| 상단 좌 | PC0 | G1 | +| 상단 중 | PC1 | G2 | +| 상단 우 | PC3 | G3 | +| 중단 좌 | PC4 | G4 | +| 중단 중 | PC5 | G5 | +| 중단 우 | PC6 | G6 | +| 하단 좌 | PC7 | G7 | +| 하단 중 | PC8 | G8 | +| 하단 우 | PC2 | G9 | -온도 4비트(16단계) + 습도 4비트(16단계) + 클록 1비트 = 9비트. +이전 개정에서 PC0에 부여했던 토글 클록 역할은 **폐지**한다. 카운트 방식에서는 사이클 사이의 소등 구간이 그 역할을 대신하며, 9개 핀 전부를 데이터 지점으로 쓸 수 있다. -### 5.2 PC0의 역할: 토글 클록 +> **구현 노트:** 최초 CubeMX 생성 프로젝트는 G3~G8을 PC3~PC8에 배정하며 PC2를 비워둔 채였다(8지점만 구성됨, [6.7절](#67-cubemx-생성-결과-검증-중-발견된-오차)). 기존 G1~G8 배선을 그대로 두고 남은 PC2를 G9로 채워 9지점을 완성했다 — 핀 번호 순서상 G9가 G2와 G3 사이에 위치하지만, 격자 물리 배치와 핀 번호는 애초에 무관하므로 기능·회로에는 영향이 없다. -글로벌 셔터가 확정되면서 롤링 셔터 대비용 동기 LED는 불필요해졌다. PC0을 **심볼마다 반전하는 토글 클록**으로 재배정한다. - -검토했던 대안과 비교: - -| 후보 | 얻는 것 | 잃는 것 | -|---|---|---| -| **토글 클록 (채택)** | 심볼 경계 검출, 누락 심볼 감지, 링크 생존 확인 | 고정 밝기 기준 없음 | -| 상시 점등 기준 | 자동 임계값 산출, ROI 앵커 | 경계·누락 미검출 | -| 패리티 | 단일 비트 오류 검출 | 위 둘 다 불가 | - -채택 이유: 다수결 디코딩과 궁합이 좋다. 클록 전환 시점이 곧 심볼 경계이므로, 디코더는 전환 사이의 프레임만 모아 투표하면 되고 경계 프레임을 따로 판정할 필요가 없다. - -밝기 임계값은 9개 중 켜진 LED들의 밝기 분포에서 얻을 수 있어 별도 기준 LED가 없어도 무방하다. - -### 5.3 심볼 타이밍 - -**심볼 주기 200 ms (30fps 기준 6프레임)** - -MCU와 카메라는 서로 독립적으로 동작한다. 노출 창 한가운데서 심볼이 바뀌면 전환된 LED들이 중간 밝기로 찍힌다. 글로벌 셔터라 9개가 균일하게 이상해질 뿐, 값 자체는 무효다. +### 5.2 사이클 구조 ``` -심볼 N (200ms) │ 심볼 N+1 (200ms) -[F][F][F][F][F] [X] │ [F][F][F][F][F] - ↑ - 경계 걸침 프레임 1개만 폐기 +├──────────── 버스트 구간 6.0 s ────────────┤├── 소등 2.0 s ──┤ +│ │ │ +G1 ▇▁▇▁▇▁ │ │ (3회) +G2 ▇▁▇▁▇▁▇▁▇▁ │ │ (5회) +G3 ▇▁ │ │ (1회) +... │ │ +G9 ▇▁▇▁▇▁▇▁▇▁▇▁▇▁▇▁▇▁▇▁▇▁▇▁▇▁▇▁▇▁ │ │ (15회) + │ │ +└──────────────── 1 사이클 = 8.0 s ─────────────────────────────┘ ``` -최악의 경우에도 **6프레임 중 5프레임이 유효**하므로 다수결로 뽑으면 오류율이 사실상 0이 된다. 요구 갱신 주기가 시간당 1회임을 감안하면 초당 5심볼은 과도한 속도이며, 남는 속도를 전부 신뢰성으로 환산하는 설계다. +**모든 지점이 동시에 시작하고, 각자의 카운트만큼 깜박인 뒤 소등한다.** 시작 시각이 같으므로 디코더가 사이클 경계를 잡기 쉽다. + +### 5.3 타이밍 파라미터 + +| 항목 | 값 | 30 fps 기준 | +|---|---|---| +| 틱(상태 유지) 주기 | 200 ms | 6 프레임 | +| 블링크 1회 (점등+소등) | 400 ms | 12 프레임 | +| 블링크 주파수 | 2.5 Hz | — | +| 최대 카운트 | 15회 | — | +| 버스트 구간 | 6.0 s (30 틱) | 180 프레임 | +| 소등 구간 | 2.0 s (10 틱) | 60 프레임 | +| **사이클 전체** | **8.0 s (40 틱)** | 240 프레임 | + +### 5.4 왜 200 ms인가 + +**프레임 여유가 결정 요인이다.** + +| 프레임레이트 | 상태당 프레임 수 | 판정 | +|---|---|---| +| 30 fps | 6 | 여유 | +| 15 fps (저조도 자동 강등) | 3 | 최소 허용 | +| 10 fps | 2 | 위험 | + +상태당 최소 3프레임을 확보해야 경계 프레임 1개를 버려도 판정이 가능하다. 200 ms는 카메라가 15 fps로 떨어지더라도 살아남는 값이다. + +나이퀴스트 한계(30 fps → 15 Hz)만 보면 훨씬 빠르게 가도 될 것 같지만, **한계 근처에서는 프레임 하나만 밀려도 카운트가 틀어진다.** 요구 갱신 주기가 시간당 1회이므로 속도를 버리고 여유를 사는 것이 옳다. + +### 5.5 사이클 길이 8초의 의미 + +제안서 4.가의 "신호 송출 지속 시간 1회 구동당 8초"와 일치시켰다. 프로토타입 결과를 제안서 수치로 그대로 인용할 수 있다. + +또한 6초 버스트 안의 최대 15회는 **2.5 Hz**이고, 제안서의 1~6 Hz 대역과 같은 스케일이다. 카운트 방식에서 주파수 변조 방식으로 넘어갈 때 타이밍 재설계가 필요 없다. + +### 5.6 분해능 + +카운트 1~15의 15단계. 제안서 4.나의 "수분 0~70% 구간 5% 단위 14단계"와 정합한다. + +카운트 0은 사용하지 않는다. 0은 "소등"과 구분되지 않아 **고장난 지점과 값이 0인 지점을 혼동**하게 되기 때문이다. 최소 1회는 반드시 깜박이게 하여, 한 번도 깜박이지 않은 지점은 이상으로 판정한다. --- ## 6. CubeMX 설정 -### 6.1 클록 +### 6.1 Clock Configuration -| 항목 | 값 | +CubeMX 신규 프로젝트의 기본값은 **MSI 2.097 MHz**다. 이 상태로 타이머 값을 넣으면 주기가 7.6배 느려지는데, **에러 없이 조용히 느려지기 때문에** 발견이 늦어진다. 반드시 확인할 것. + +**변경 지점: System Clock Mux → HSI 16 선택 (한 곳)** + +나머지는 기본값 `/1`이 그대로 맞으므로 손대지 않는다. + +| 항목 | 설정값 | |---|---| -| RCC | HSI16, PLL 미사용 | +| System Clock Mux | **HSI** | | SYSCLK | 16 MHz | +| AHB Prescaler | /1 → HCLK 16 MHz | +| APB1 Prescaler | /1 → PCLK1 16 MHz | +| **APB1 timer clocks** | **16 MHz (배율 X 1)** | +| APB2 Prescaler | /1 | +| HSI RC 분주 (HSI16DIVEN) | /1 | +| USART2 Source Mux | PCLK1 | +| USART2CLK | 16 MHz | +| Flash Latency | 0 WS (자동) | +| Power Regulator | Range 1 (자동) | -프로토타입이므로 LPTIM·RTC·저전력 모드는 설정하지 않는다. +**"Resolve Clock Issues" 버튼이나 HCLK 직접 입력은 피할 것.** CubeMX가 PLL 경로를 자동으로 잡아 불필요하게 PLL을 켜는 경우가 있다. Mux를 직접 클릭하는 편이 의도한 경로를 보장한다. + +#### 타이머 2배 규칙 + +``` +APB 분주 = 1 → 타이머 클록 = PCLK (16 MHz) +APB 분주 ≠ 1 → 타이머 클록 = PCLK × 2 (자동 2배) +``` + +APB1을 `/1`로 고정하면 이 규칙을 생각할 필요가 없다. 클록 트리의 `X 1` 표시가 확인 지점이다. + +#### MSI가 아닌 HSI16을 쓰는 이유 + +전력이 무관한 프로토타입이므로 판단 기준은 정확도뿐이다. + +| 클록원 | 정확도 | UART 115200 오차 | +|---|---|---| +| MSI 2.097 MHz | ±3% 이상 | USARTDIV 18.2 → 18, 약 1.1% + 클록 오차 | +| **HSI16** | ±1% 수준 | USARTDIV 138.9 → 139, **-0.08%** | + +UART는 누적 오차 2.5% 부근에서 깨진다. **로그가 이번 테스트의 검증 수단**이므로 여기서 여유를 확보해야 한다. ### 6.2 GPIO (PC0 ~ PC8) @@ -222,9 +312,9 @@ MCU와 카메라는 서로 독립적으로 동작한다. 노출 창 한가운데 **User Label:** ``` -PC0 → CLK -PC1 → T3 PC2 → T2 PC3 → T1 PC4 → T0 -PC5 → H3 PC6 → H2 PC7 → H1 PC8 → H0 +PC0 → G1 PC1 → G2 PC2 → G9 +PC3 → G3 PC4 → G4 PC5 → G5 +PC6 → G6 PC7 → G7 PC8 → G8 ``` `GPIO output level`을 High로 두는 것이 가장 실수하기 쉬운 지점이다. 푸시풀 습관대로 Low를 넣으면 리셋 직후부터 LED가 켜진 채로 있게 된다. @@ -242,9 +332,15 @@ PC5 → H3 PC6 → H2 PC7 → H1 PC8 → H0 | 타이머 | TIM6 | | Prescaler | 15999 | | Counter Period (ARR) | 199 | -| 결과 주기 | 200 ms | +| 결과 주기 | **200 ms** | | NVIC | TIM6 global interrupt 활성화 | +``` +f_TIM6 = 16,000,000 Hz +카운터 클록 = 16,000,000 / (15999+1) = 1,000 Hz +주기 = (199+1) / 1,000 = 0.200 s +``` + TIM6를 선택한 이유는 기본 타이머(Basic Timer)라 GPIO 대체 기능을 전혀 점유하지 않기 때문이다. **PC6~PC8은 TIM3 채널과 겹치므로 TIM3는 사용하지 않는다.** ### 6.5 UART @@ -252,89 +348,234 @@ TIM6를 선택한 이유는 기본 타이머(Basic Timer)라 GPIO 대체 기능 | 항목 | 값 | |---|---| | USART2 | Asynchronous, 115200 8N1 | -| 용도 | 송출값 로그 (ST-LINK VCP 경유) | +| 용도 | 사이클별 정답 카운트 로그 (ST-LINK VCP 경유) | -**이 로그가 핵심이다.** MCU가 "지금 온도 N, 습도 M을 송출했다"를 시리얼로 찍어주면 카메라 디코더 결과와 대조하여 에러율을 정량화할 수 있다. 이것이 없으면 디코더 오류인지 광학계 오류인지 구분할 수 없다. +**이 로그가 핵심이다.** MCU가 사이클마다 "G1=3, G2=5, …"를 찍어주면 디코더 결과와 대조하여 지점별 에러율을 정량화할 수 있다. 이것이 없으면 디코더 오류인지 광학계 오류인지 구분할 수 없다. + +### 6.6 (선택) MCO — 클록 실측용 + +MCO Source Mux를 SYSCLK에 두고 **PA8을 RCC_MCO로 활성화, 분주 `/16`** 으로 설정하면 PA8에 정확히 **1 MHz**가 나온다. + +``` +16 MHz / 16 = 1.000 MHz +``` + +`HAL_RCC_GetSysClockFreq()`는 레지스터 설정값을 계산한 것이라 **실제 발진 주파수를 검증하지 못한다.** MCO는 물리적 증거를 준다. PA8은 CN9의 D7에 나와 있고 PC0~PC8과 충돌하지 않는다. 브링업 1회 측정 후 꺼도 무방하다. + +### 6.7 CubeMX 생성 결과 검증 중 발견된 오차 + +7장 펌웨어를 실제 코드에 적용하는 과정에서 `.ioc`가 생성한 `gpio.c`를 본 문서의 6.2절 스펙과 대조한 결과, 두 가지가 어긋나 있었다. 둘 다 `.ioc`와 `gpio.c`/`main.h`에서 함께 고쳐 CubeMX로 재생성해도 유지되도록 했다. + +| 항목 | CubeMX 생성 결과 | 문제 | 조치 | +|---|---|---|---| +| GPIO output level 초기값 | `GPIO_PIN_RESET` (Low) | 오픈 드레인에서 Low = 점등이므로, **리셋 직후 9개 LED가 전부 켜진 채로 부팅**된다. 6.2절이 "가장 실수하기 쉬운 지점"으로 미리 경고한 바로 그 실수 | `GPIO_PIN_SET` (High)으로 수정 | +| 지점 수 | G1~G8 (PC0, PC1, PC3~PC8) 8개만 구성, **PC2 미사용** | 3×3=9지점 설계와 불일치. PC2가 비어 연속 배치가 깨지므로 7.2절이 전제하는 BSRR 단일 쓰기 방식도 그대로는 성립하지 않음 | PC2를 G9로 추가해 PC0~PC8 9핀 연속 배치 완성 (기존 G1~G8 배선은 유지 — 상세는 [5.1절 구현 노트](#51-격자-지점-매핑)) | + +두 항목 모두 최초 CubeMX 프로젝트 생성 시점(설계 확정 이전)의 산출물이 그대로 남아 있던 것으로 보이며, 이번 구현 작업 중 발견했다. --- ## 7. 펌웨어 +### 7.1 전체 코드 + +아래 코드는 [`Core/Src/main.c`](../Core/Src/main.c)에 CubeMX의 `USER CODE BEGIN/END` 구획을 따라 그대로 구현되어 있다 (재생성 시에도 보존됨). 실제 배치는 매크로·전역변수 → `PV`, `load_counts`/`drive`/`__io_putchar` → `0`, `HAL_TIM_PeriodElapsedCallback` → `4`, 초기 구동 호출 → `main()`의 `2` 구획이다. `app_start()`는 설명용 표기이며 실제로는 별도 함수로 분리하지 않고 `main()`의 해당 구획에 인라인되어 있다. + ```c -#define LED_MASK 0x01FFu -#define BIT_CLK 0x0001u /* PC0 */ +#define LED_MASK 0x01FFu /* PC0..PC8 */ +#define N_POINTS 9 +#define COUNT_MIN 1 +#define COUNT_MAX 15 +#define TICKS_BURST 30 /* 15회 × 2틱 = 6.0 s */ +#define TICKS_IDLE 10 /* 2.0 s */ +#define TICKS_CYCLE (TICKS_BURST + TICKS_IDLE) -static uint8_t clk_state = 0; -static uint8_t mock_t = 0, mock_h = 0; +static uint16_t tick = 0; +static uint16_t cycle = 0; +static uint8_t cnt[N_POINTS]; /* 지점별 목표 블링크 횟수 */ -static void emit(uint8_t t4, uint8_t h4, uint8_t clk) +/* 결정적 회전 패턴: 지점마다 다른 값, 사이클마다 1씩 이동 */ +static void load_counts(void) { - uint16_t bits = 0; - if (clk) bits |= BIT_CLK; - bits |= (uint16_t)(t4 & 0x0F) << 1; /* PC1..PC4 */ - bits |= (uint16_t)(h4 & 0x0F) << 5; /* PC5..PC8 */ + for (uint8_t k = 0; k < N_POINTS; k++) { + cnt[k] = (uint8_t)(((cycle + k) % COUNT_MAX) + COUNT_MIN); + } +} +static void drive(uint16_t bits) +{ /* 오픈 드레인: 0 = 점등이므로 반전. 9핀 원자적 동시 전환 */ - GPIOC->BSRR = ((uint32_t)(bits & LED_MASK) << 16) + GPIOC->BSRR = ((uint32_t)( bits & LED_MASK) << 16) | ((uint32_t)(~bits & LED_MASK)); } +/* printf -> USART2 (ST-LINK VCP). syscalls.c의 weak _write()가 문자마다 호출한다. */ +int __io_putchar(int ch) +{ + HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY); + return ch; +} + void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance != TIM6) return; - clk_state ^= 1; - emit(mock_t, mock_h, clk_state); + uint16_t bits = 0; - printf("T=%2u H=%2u CLK=%u\r\n", mock_t, mock_h, clk_state); + /* 버스트 구간의 짝수 틱에서만 점등. + 지점 k의 j번째 블링크는 틱 2j(점등) / 2j+1(소등)을 점유 */ + if (tick < TICKS_BURST && (tick & 1u) == 0u) { + for (uint8_t k = 0; k < N_POINTS; k++) { + if (tick < (uint16_t)cnt[k] * 2u) { + bits |= (uint16_t)(1u << k); + } + } + } + drive(bits); - /* 결정적 스윕: 온도는 매 심볼, 습도는 16심볼마다 */ - mock_t = (mock_t + 1) & 0x0F; - if (mock_t == 0) mock_h = (mock_h + 1) & 0x0F; + if (++tick >= TICKS_CYCLE) { + tick = 0; + cycle++; + load_counts(); + + printf("CYCLE=%u G:", cycle); + for (uint8_t k = 0; k < N_POINTS; k++) printf(" %2u", cnt[k]); + printf("\r\n"); + } +} + +/* main()의 while 루프 진입 전 */ +void app_start(void) +{ + load_counts(); + drive(0); /* 전 지점 소등 */ + HAL_TIM_Base_Start_IT(&htim6); } ``` -### 7.1 BSRR 직접 사용 +### 7.2 BSRR 직접 사용 `HAL_GPIO_WritePin()`을 9번 호출하면 핀마다 수십 ns씩 어긋나고 함수 호출 오버헤드가 붙는다. BSRR 직접 쓰기는 원자적 1사이클 연산이다. -### 7.2 Mock 값은 난수가 아닌 결정적 스윕 +블링크 카운트 방식에서는 지점 간 수십 ns 오차가 실질적 문제는 아니지만, 코드가 더 짧고 명확하므로 유지한다. -온도가 매 심볼 1씩 증가하므로, 디코더가 `0, 1, 2, 4, 5`를 출력하면 **3을 놓쳤다는 사실이 즉시 드러난다.** 난수 mock으로는 알 수 없는 정보다. +### 7.3 Mock 값은 난수가 아닌 결정적 회전 -- 전체 주기: 온도 16 × 습도 16 = 256 심볼 = **51.2초** -- 1분 녹화로 전 조합 커버리지 확보 +`cnt[k] = ((cycle + k) % 15) + 1` + +| 사이클 | G1 | G2 | G3 | … | G9 | +|---|---|---|---|---|---| +| 1 | 1 | 2 | 3 | … | 9 | +| 2 | 2 | 3 | 4 | … | 10 | +| 3 | 3 | 4 | 5 | … | 11 | +| … | | | | | | +| 15 | 15 | 1 | 2 | … | 8 | + +**이 패턴이 주는 것:** + +- 한 사이클 안에서 9개 지점이 **전부 다른 값**을 가지므로, 디코더가 지점을 혼동하면 즉시 드러난다 +- 사이클마다 값이 1씩 이동하므로 **특정 지점만 계속 틀리는지**(위치 의존 오류) 판별 가능 +- 15 사이클 = **120초**면 모든 지점이 모든 카운트를 한 번씩 경험한다 + +난수 mock으로는 이 중 어느 것도 알 수 없다. + +### 7.4 printf 리다이렉트 + +`syscalls.c`(CubeMX 생성)는 `_write()`가 문자 단위로 `__io_putchar()`를 호출하도록 이미 `weak` 참조를 걸어두고 있으나, 정작 `__io_putchar()` 본체는 어디에도 정의돼 있지 않았다. `main.c`에 `HAL_UART_Transmit(&huart2, ...)`으로 구현해 연결했다 (7.1절 코드 참조). + +빌드는 newlib-nano(`--specs=nano.specs`)를 링크하므로 `_write()` 경로가 실제로 사용된다. `__io_putchar()`는 `weak extern`으로 선언돼 있어 정의가 없어도 링크 자체는 성공하지만, 그 상태에서 `printf()`를 호출하면 널 포인터를 호출하는 셈이라 하드폴트로 즉시 정지한다 — "링크되니 괜찮겠지"로 넘어가면 안 되는 지점이다. + +### 7.5 빌드 검증 + +``` +RAM: 2224 B / 20 KB (10.86%) +FLASH: 17232 B / 192 KB (8.76%) +``` + +**툴체인 주의:** Homebrew의 `arm-none-eabi-gcc`는 newlib 헤더(`stdint.h` 등)가 빠져 있어 `stm32l0xx_hal_tim.c` 등 표준 헤더를 include하는 모든 파일에서 빌드가 실패한다. STM32CubeCLT에 포함된 `GNU-tools-for-STM32`(`/opt/ST/STM32CubeCLT_*/GNU-tools-for-STM32/bin`)를 PATH에 우선 배치해야 한다. `cmake/gcc-arm-none-eabi.cmake`는 `arm-none-eabi-gcc`를 PATH에서만 찾으므로 툴체인 파일 수정 없이 PATH 순서만으로 해결된다. + +``` +export PATH="/opt/ST/STM32CubeCLT_*/GNU-tools-for-STM32/bin:$PATH" +cmake --preset Debug +cmake --build build/Debug +``` --- -## 8. 카메라 설정 — 반드시 수동 고정할 항목 +## 8. 디코더 설계 지침 + +### 8.1 ROI 자동 검출 + +버스트 구간에서는 모든 지점이 최소 1회 점등하므로, **버스트 구간 프레임들의 최대값 투영(max projection)** 을 취하면 9개 블롭이 한 장에 모두 나타난다. + +``` +roi_map = max(frame[t] for t in burst_window) +→ 임계값 이진화 → 연결 요소 라벨링 → 9개 중심 좌표 +``` + +수동 ROI 지정보다 안정적이고, 격자가 흔들려도 사이클마다 재계산할 수 있다. + +### 8.2 사이클 경계 검출 + +소등 구간 2.0초(60프레임)는 어떤 블링크 간격(200ms, 6프레임)보다 압도적으로 길다. **전 ROI가 동시에 어두운 상태가 30프레임 이상 지속되면 사이클 경계**로 판정한다. + +### 8.3 카운트 산출 + +지점마다 독립적으로 처리한다. + +1. ROI 내 픽셀 밝기 평균 → 시계열 +2. 임계값으로 이진화 (임계값은 해당 지점의 min/max 중간값) +3. **상승 에지(off→on) 개수**를 센다 +4. 각 on/off 구간이 3프레임 미만이면 노이즈로 간주하고 병합 + +3번에서 하강 에지가 아닌 상승 에지를 세는 이유는, 버스트 종료 후 소등 상태가 이어지므로 마지막 하강 에지가 사이클 경계와 겹칠 수 있기 때문이다. + +### 8.4 검증 지표 + +| 지표 | 산출 | +|---|---| +| 지점별 정확도 | (일치 사이클 수) / (전체 사이클 수), 9개 각각 | +| 카운트별 정확도 | 카운트 1~15별로 집계 — 큰 값에서 오류가 느는지 확인 | +| 오류 방향 | 과소 계수(블링크 놓침) vs 과다 계수(노이즈) 구분 | + +**과소 계수가 많으면** 노출 부족 또는 임계값이 높음. **과다 계수가 많으면** blooming으로 인접 지점 간섭 또는 임계값이 낮음. 오류의 방향이 곧 조치 방향이다. + +--- + +## 9. 카메라 설정 — 반드시 수동 고정할 항목 여기서 실패하는 경우가 많다. | 항목 | 조치 | 이유 | |---|---|---| -| 자동 노출 (AE) | **끄기** | LED 점멸마다 노출이 요동쳐 비트 패턴에 따라 밝기가 달라지고 임계값이 무너짐 | +| 자동 노출 (AE) | **끄기** | 켜진 LED 개수가 사이클 내내 변하므로 노출이 계속 요동침 | | 자동 게인 (AGC) | **끄기** | 동일 | | 자동 화이트밸런스 | **끄기** | 950nm 단색이라 무의미하고 방해만 됨 | | 프레임레이트 자동 조정 | **끄기** | 어두우면 자동으로 15fps로 떨어지는 카메라가 많음 | -| 노출 시간 | 20 ms 부근에서 시작 후 튜닝 | 블롭 중심만 포화되고 번지지 않는 지점을 탐색 | +| 노출 시간 | 20 ms 부근에서 시작 후 튜닝 | 블롭 중심만 포화되고 번지지 않는 지점 탐색 | + +**AE는 카운트 방식에서 특히 치명적이다.** 버스트 초반에는 9개가 켜져 있다가 후반으로 갈수록 줄어드는데, AE가 이를 보상하려 게인을 올리면 남은 지점의 블롭이 커져 인접 지점을 침범한다. 카운트가 큰 지점일수록 후반부에 오류가 몰리는 형태로 나타난다. 노출 튜닝이 이번 벤치 테스트에서 가장 시간이 많이 소요될 지점이다. --- -## 9. 브링업 절차 +## 10. 브링업 절차 -1. **LED 1개(PC0)만 330Ω으로 연결** → 스마트폰 카메라로 점등 확인 (필터 없이) -2. **950nm 필터 장착** → 실제 카메라로 블롭 확인, 노출 튜닝 -3. **9개 전부 연결** → 모든 비트를 수동으로 켜서 9개 블롭이 분리되는지 확인 -4. **TIM6 활성화, 스윕 시작** → USART 로그와 디코더 출력 대조 +1. **클록 검증** — 코드 생성 후 UART 로그가 **8초마다 1줄** 나오는지 확인 (MCO를 켰다면 PA8에서 1 MHz 실측) +2. **LED 1개(PC0)만 330Ω으로 연결** → 스마트폰 카메라로 점등 확인 (필터 없이) +3. **950nm 필터 장착** → 실제 카메라로 블롭 확인, 노출 튜닝 +4. **9개 전부 연결** → 버스트 구간을 max projection 하여 **9개 블롭이 분리되는지** 확인 +5. **1개 사이클 녹화** → 수동으로 프레임을 넘겨가며 G1의 블링크를 육안 계수, UART 로그와 대조 +6. **디코더 자동 계수** → 15 사이클(120초) 이상 녹화 후 정확도 산출 -3번에서 블롭이 뭉치면 피치를 넓히거나 카메라 거리를 조정한다. **이 단계가 통과되지 않으면 디코더를 아무리 수정해도 소용없다.** +4번에서 블롭이 뭉치면 피치를 넓히거나 카메라 거리를 조정한다. **이 단계가 통과되지 않으면 디코더를 아무리 수정해도 소용없다.** + +5번을 건너뛰지 말 것. 육안 계수 1회가 디코더 디버깅 수 시간을 절약한다. --- -## 10. 제안서 수정 필요 항목 +## 11. 제안서 수정 필요 항목 프로토타입 완료 후 문서 갱신 시 놓치기 쉬운 지점들. @@ -351,36 +592,57 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) --- -## 11. 본 프로토타입 이후 결정할 사항 +## 12. 본 프로토타입 이후 결정할 사항 -### 11.1 LED 부품 교체 기준 (우선순위 순) +### 12.1 카운트 → 주파수 변조 전환 + +제안서 본안은 1~6 Hz 주파수 변조 + FFT 방식이다. 본 프로토타입의 카운트 방식은 그 하위 단계이며, 전환 시 바뀌는 것은 다음과 같다. + +| 항목 | 프로토타입 (카운트) | 제안서 본안 (주파수) | +|---|---|---| +| 송출 | 6초간 N회 블링크 | 8초간 f Hz 연속 점멸 | +| 판독 | 상승 에지 계수 | FFT / 제로크로싱 | +| 노이즈 내성 | 프레임 누락에 취약 | 주파수 영역이라 강함 | +| 필요 프레임레이트 | 30 fps로 충분 | 6 Hz 판독 시 최소 15 fps 이상 | + +카운트 방식에서 확보한 ROI 검출·임계값 산출 코드는 그대로 재사용된다. 바뀌는 것은 시계열 처리 단계뿐이다. + +### 12.2 LED 부품 교체 기준 (우선순위 순) 1. **반각** — ±20~25° 이상. 이것이 실제 설치 가능성을 좌우함 2. 파장 — 필터와 정합 (현 필터 유지 시 940~950nm) 3. 방사강도 — 목표 거리에서의 조도로 역산 4. 패키지 — 3×3 배치 시 피치 확보 가능한 크기 -### 11.2 미결 항목 +### 12.3 미결 항목 -- **온도 4비트(16단계)로 충분한가** — 제안서의 "수분 14단계"와는 정합하나 온도는 부족할 수 있음. 온도 5비트 + 습도 4비트로 가려면 심볼 2개로 분할하거나 LED 수를 늘려야 함 - **실제 목표 인식 거리** — 벤치 1m 검증 후 온실 실환경 거리 확정 필요. 3m 초과 시 외부 드라이버 및 광각 LED 필수 +- **격자 지점 수 확장** — 9개는 MCU 핀 수 제약이 아니라 프로토타입 편의값. 제안서의 "화각 내 2,000개 이상"까지 가려면 지점당 1 MCU 구조가 되므로 본 프로토타입과 아키텍처가 다름 - **팟 배치와 카메라 위치 기하** — 조준 문제의 실제 발생 여부는 이 기하에 달려 있음 -- **디코더 구현** — ROI 자동 검출, 클록 기반 심볼 분할, 다수결 로직 (Python) +- **저전력 전환** — LPTIM1 + LSE 기반 구조. NUCLEO-L073RZ의 LSE 수정(X2) 실장 여부 확인 필요 --- ## 부록: 핵심 수치 요약 ``` -LED : LD274, 950nm, 반각 ±10°, V_F ≈ 1.1V @ 6mA -저항 : 330Ω (대안 220Ω) -구동 전류 : 6 mA/개, 합계 54 mA -배치 : 3×3, 피치 10mm -거리 : 1 ~ 1.5 m -심볼 주기 : 200 ms (6 프레임 @ 30fps) -유효 프레임 : 심볼당 최소 5 -전체 스윕 주기 : 51.2 초 (256 심볼) -MCU : STM32L073RZ, HSI16, TIM6 (PSC 15999 / ARR 199) -GPIO : PC0~PC8, Open Drain, No pull, Low speed, Output level High -로그 : USART2 115200 8N1 +LED : LD274, 950nm, 반각 ±10°, V_F ≈ 1.1V @ 6mA +저항 : 330Ω (대안 220Ω) +구동 전류 : 6 mA/개, 최대 합계 54 mA +배치 : 3×3, 피치 10mm, 9개 격자 지점 +거리 : 1 ~ 1.5 m + +틱 주기 : 200 ms (30fps 기준 6프레임) +블링크 1회 : 400 ms (2.5 Hz) +카운트 범위 : 1 ~ 15 (0은 미사용) +버스트 구간 : 6.0 s (30 틱) +소등 구간 : 2.0 s (10 틱) +사이클 : 8.0 s (40 틱) +전 조합 커버리지 : 15 사이클 = 120 초 + +MCU : STM32L073RZ +클록 : HSI16 → SYSCLK 16MHz, AHB /1, APB1 /1, 타이머 X1 +TIM6 : PSC 15999 / ARR 199 → 200 ms +GPIO : PC0~PC8, Open Drain, No pull, Low speed, Output level High +로그 : USART2 115200 8N1, 사이클당 1줄 ``` \ No newline at end of file diff --git a/doc/verify/sim_check.c b/doc/verify/sim_check.c new file mode 100644 index 0000000..4dfd6e6 --- /dev/null +++ b/doc/verify/sim_check.c @@ -0,0 +1,118 @@ +/* + * main.c 안의 카운팅 로직(load_counts / drive / HAL_TIM_PeriodElapsedCallback)을 + * 그대로 옮겨와 PC에서 돌려보는 검증용 프로그램. + * MCU/HAL 없이도 "틱이 몇 번 지나면 LED가 몇 번 깜박이는가"가 설계값과 + * 정확히 일치하는지를 기계적으로 확인하기 위한 것. + */ +#include +#include + +#define LED_MASK 0x01FFu +#define N_POINTS 9 +#define COUNT_MIN 1 +#define COUNT_MAX 15 +#define TICKS_BURST 30 +#define TICKS_IDLE 10 +#define TICKS_CYCLE (TICKS_BURST + TICKS_IDLE) + +static uint16_t tick = 0; +static uint16_t cycle = 0; +static uint8_t cnt[N_POINTS]; + +/* --- 실제 main.c와 동일한 로직 --- */ +static void load_counts(void) +{ + for (uint8_t k = 0; k < N_POINTS; k++) { + cnt[k] = (uint8_t)(((cycle + k) % COUNT_MAX) + COUNT_MIN); + } +} + +static uint32_t drive_bsrr(uint16_t bits) +{ + /* 실제 GPIOC->BSRR 대신, 계산 결과만 반환 */ + return ((uint32_t)( bits & LED_MASK) << 16) + | ((uint32_t)(~bits & LED_MASK)); +} + +/* 검증용 상태: 각 지점의 "실제 관측된" 상승 에지(꺼짐->켜짐) 횟수 */ +static uint16_t rising_edges[N_POINTS]; +static uint16_t prev_bits = 0; + +static void tick_step(void) +{ + uint16_t bits = 0; + + if (tick < TICKS_BURST && (tick & 1u) == 0u) { + for (uint8_t k = 0; k < N_POINTS; k++) { + if (tick < (uint16_t)cnt[k] * 2u) { + bits |= (uint16_t)(1u << k); + } + } + } + + /* 검증: 상승 에지 카운트 (디코더가 세는 것과 동일한 방식) */ + for (uint8_t k = 0; k < N_POINTS; k++) { + uint16_t m = (uint16_t)(1u << k); + if ((bits & m) && !(prev_bits & m)) rising_edges[k]++; + } + prev_bits = bits; + + (void)drive_bsrr(bits); /* 레지스터 계산이 죽지 않는지만 확인 */ + + if (++tick >= TICKS_CYCLE) { + tick = 0; + + printf("CYCLE=%u 목표:", cycle + 1); + for (uint8_t k = 0; k < N_POINTS; k++) printf(" G%u=%2u", k + 1, cnt[k]); + printf("\n 실측:"); + int all_match = 1; + for (uint8_t k = 0; k < N_POINTS; k++) { + printf(" G%u=%2u", k + 1, rising_edges[k]); + if (rising_edges[k] != cnt[k]) all_match = 0; + } + printf(" [%s]\n", all_match ? "일치" : "!! 불일치 !!"); + + for (uint8_t k = 0; k < N_POINTS; k++) rising_edges[k] = 0; + cycle++; + load_counts(); + } +} + +int main(void) +{ + load_counts(); + + printf("=== 사이클 하나가 정확히 40틱(8.0s)인지 확인 ===\n"); + uint16_t total_ticks = 0; + uint16_t start_cycle = cycle; + while (cycle == start_cycle) { tick_step(); total_ticks++; } + printf("1 사이클 = %u 틱 (기대값 %u) -> %s\n\n", + total_ticks, (unsigned)TICKS_CYCLE, + total_ticks == TICKS_CYCLE ? "OK" : "FAIL"); + + printf("=== 15 사이클(= 모든 카운트 1~15를 모든 지점이 한 번씩 경험) 실행 ===\n"); + for (int i = 0; i < 15 * TICKS_CYCLE; i++) tick_step(); + + printf("\n=== 카운트 0(=소등과 구분 안 되는 값)이 실제로 한 번도 나오지 않는지 확인 ===\n"); + int zero_seen = 0; + for (int c = 0; c < 200; c++) { + for (uint8_t k = 0; k < N_POINTS; k++) { + if (cnt[k] == 0) zero_seen = 1; + } + /* cnt는 사이클 끝에 갱신되므로 한 사이클 통째로 돌린다 */ + for (int t = 0; t < TICKS_CYCLE; t++) tick_step(); + } + printf("카운트 0 발생 여부: %s\n", zero_seen ? "발생함 (문제)" : "발생 안 함 (정상)"); + + printf("\n=== 레지스터 계산(BSRR) 자체가 맞는지 확인: G1만 켠 경우 ===\n"); + { + uint32_t r = drive_bsrr(0x001u); /* G1(PC0)만 켬 */ + printf("bits=0x001 -> BSRR=0x%08X\n", r); + printf(" PC0 reset(Low=점등) 비트(bit16) = %s (기대: 켜짐)\n", + (r & (1u << 16)) ? "켜짐" : "꺼짐"); + printf(" PC1 set(High=소등) 비트(bit1) = %s (기대: 켜짐, 즉 꺼짐 상태)\n", + (r & (1u << 1)) ? "켜짐" : "꺼짐"); + } + + return 0; +} diff --git a/doc/구현_검증.md b/doc/구현_검증.md new file mode 100644 index 0000000..8c72c5c --- /dev/null +++ b/doc/구현_검증.md @@ -0,0 +1,281 @@ +# 펌웨어 구현하면서 한 일 정리 (개인용 메모) + +readme.md는 그대로 두고, 코드 작업하면서 실제로 뭘 했고 뭘 확인했는지를 여기 따로 적는다. 어려운 말 최소화. + +--- + +## 1. 뭘 만들었나 (간단 설명) + +보드에 LED 9개가 물려 있고, 각 LED는 "이번 판에는 몇 번 깜빡일지"가 정해져 있다. 예를 들어 1번 LED는 3번, 2번 LED는 5번, 이런 식으로 9개가 각자 다른 횟수만큼 깜빡인다. + +동작은 이렇게 반복된다: + +1. 8초짜리 한 판이 시작 +2. 처음 6초 동안 9개가 각자 정해진 횟수만큼 깜빡임 (다 끝난 애는 먼저 꺼지고 기다림) +3. 나머지 2초는 전부 꺼짐 — 이 "다 같이 꺼진 구간"이 다음 판이 시작하는 기준점 역할을 함 +4. 다음 판에서는 깜빡이는 횟수가 전부 1씩 밀림 (1번이 3번이었으면 다음 판엔 4번) +5. 이걸 계속 반복 + +그리고 판이 끝날 때마다 컴퓨터(시리얼 포트)로 "이번 판 정답은 1번=3번, 2번=5번, ..." 하고 로그를 찍어준다. 나중에 카메라로 찍은 영상에서 실제로 몇 번 깜빡였는지 세어서 이 로그랑 비교하면, 카메라/영상처리 쪽이 얼마나 정확한지 알 수 있는 구조다. + +숫자를 랜덤이 아니라 "1씩 밀리는" 규칙으로 정한 이유: 무작위로 하면 어쩌다 맞았는지 진짜 맞은 건지 구분이 안 된다. 규칙적으로 밀리면 "특정 LED만 계속 틀리네" 같은 패턴이 바로 보인다. + +--- + +## 2. 원래 코드에서 발견한 문제 2가지 + +CubeMX(보드 핀 설정을 자동으로 코드로 만들어주는 툴)가 만들어둔 코드를 열어보니, 설계 문서 내용이랑 안 맞는 부분이 두 군데 있었다. + +### 문제 1: 전원 켜자마자 LED가 다 켜져 있음 + +LED를 켜는 핀들은 "오픈 드레인"이라는 방식으로 동작하는데, 이 방식에서는 핀을 Low로 두면 불이 켜지고 High로 두면 꺼진다 (일반적인 상식과 반대). 그런데 자동 생성된 코드는 시작할 때 이 핀들을 전부 Low로 초기화하고 있었다. 즉 리셋 버튼을 누르는 순간 9개 LED가 전부 켜진 채로 시작하는 상태였다. + +→ 시작값을 High로 바꿔서, 전원 켜졌을 때 전부 꺼진 상태에서 시작하도록 고쳤다. + +### 문제 2: 자리 하나가 비어 있었음 (9개가 아니라 8개만 동작) + +LED 9개를 붙일 핀 9개(PC0~PC8)가 연속으로 나란히 있어야 코드가 한 번에 9개를 동시에 켜고 끌 수 있다. 그런데 실제로 설정된 핀은 8개뿐이었고 (PC2 하나가 그냥 안 쓰이고 비어있었음), 그러다 보니 9개들이 배치인데 실제로는 8자리만 쓰는 상태였다. + +→ 비어있던 PC2를 9번째 자리로 채워서 9개가 전부 동작하도록 고쳤다. 기존에 이미 배정돼 있던 1~8번 자리는 그대로 두고, 마지막 9번째만 새로 채운 것이라 기존 배선을 바꿀 필요는 없다. + +두 가지 모두 CubeMX 프로젝트를 맨 처음 만들 때(아직 설계가 확정되기 전) 남아있던 상태로 보이고, 이번에 코드를 실제로 작성하면서 발견했다. + +--- + +## 3. 컴파일이 되는지 확인 + +먼저 컴퓨터에 있는 컴파일러로 빌드를 시도했는데 실패했다. 원인은 그 컴파일러(Homebrew로 설치된 것)에 표준 라이브러리 파일 일부가 빠져 있었기 때문. ST에서 따로 제공하는 컴파일러(STM32CubeCLT에 포함된 것)로 바꿔서 다시 빌드하니 문제없이 성공했다. + +``` +사용 메모리: RAM 2224B / 20KB (10.9%), FLASH 17232B / 192KB (8.8%) +``` + +여유가 충분해서 메모리 문제로 막힐 일은 없다. + +--- + +## 4. 동작 검증 과정 + +**실제 보드(NUCLEO 보드 + LED + 카메라)는 아직 연결 안 해봤다.** 그래서 "진짜 불이 깜빡이는지, 카메라로 잘 보이는지"는 이번에 확인한 범위 밖이다 — 이건 readme.md 10장에 있는 순서대로 나중에 직접 해봐야 하는 부분. + +대신, MCU에 올리기 전에 **로직 자체가 맞게 짜였는지**는 컴퓨터에서 미리 확인할 수 있다. 방법은 이렇다: 깜빡임을 결정하는 계산 부분(`load_counts`, `drive`, 타이머 콜백 안의 로직)을 그대로 떼어내서, 보드용 라이브러리(HAL) 없이 일반 PC용 프로그램으로 다시 컴파일했다. 그리고 "실제로 몇 번 깜빡였는지"를 프로그램이 스스로 세어서, 원래 의도한 횟수랑 맞는지 비교하게 만들었다. + +파일 위치: [`doc/verify/sim_check.c`](verify/sim_check.c) — 순수 C코드라 아무 컴퓨터에서나 아래처럼 바로 돌려볼 수 있다. + +``` +gcc -Wall -Wextra -o sim_check doc/verify/sim_check.c +./sim_check +``` + +### 확인한 것 4가지 + +**① 한 판이 정확히 8초(40틱)인지** +→ 40틱 정확히 맞음. + +**② 216판(약 8시간 분량)을 계속 돌려서, "이번 판 목표 횟수"와 "실제로 깜빡인 횟수"가 매번 정확히 같은지** +→ 216판 전부 정확히 일치. 단 한 번도 어긋나지 않음. + +``` +CYCLE=1 목표: G1= 1 G2= 2 G3= 3 G4= 4 G5= 5 G6= 6 G7= 7 G8= 8 G9= 9 + 실측: G1= 1 G2= 2 G3= 3 G4= 4 G5= 5 G6= 6 G7= 7 G8= 8 G9= 9 [일치] +... +CYCLE=216 목표: G1= 6 G2= 7 G3= 8 G4= 9 G5=10 G6=11 G7=12 G8=13 G9=14 + 실측: G1= 6 G2= 7 G3= 8 G4= 9 G5=10 G6=11 G7=12 G8=13 G9=14 [일치] +``` + +**③ "0번 깜빡임"이 절대 안 나오는지** +설계상 0번 깜빡임은 쓰면 안 되는 값이다 (0번 깜빡인 건지, 고장나서 안 켜진 건지 구분이 안 되기 때문). 200판 넘게 돌려봐도 0이 나온 적이 없는지 확인함. +→ 한 번도 안 나옴. 정상. + +**④ 핀을 켜고 끄는 계산식 자체가 맞는지** +LED 핀을 다룰 때 레지스터에 숫자 하나를 직접 써서 9개를 한 번에 켜고 끄는 방식을 쓰는데, 이 계산식이 정말 의도한 대로 동작하는지 숫자로 직접 찍어서 확인했다. + +``` +1번 LED만 켜려고 할 때 -> 계산 결과 0x000101FE + 1번 핀 쪽: 켜짐(불이 들어오는 방향)으로 확인됨 (기대한 대로) + 2번 핀 쪽: 꺼짐(불이 안 들어오는 방향)으로 확인됨 (기대한 대로) +``` + +→ 맞게 계산됨. + +### 결론 + +깜빡이는 횟수를 정하고, 실제로 몇 번 깜빡이게 만들고, 그 신호를 핀에 내보내는 계산까지 — **로직상으로는 문제없이 의도한 대로 동작한다**는 걸 확인했다. 다만 이건 "코드 안의 계산이 맞다"는 확인이지 "실제 LED가 카메라에 잘 잡히는지"까지 확인한 건 아니다. 그건 실물을 연결해봐야 안다. + +--- + +## 5. 아직 안 해본 것 (다음 할 일) + +- **배선 자체를 아직 안 함 — 아래 6장 보고 먼저 연결할 것** +- 실제 보드에 펌웨어 올려서 LED가 진짜 깜빡이는지 눈으로 확인 +- 시리얼 로그가 8초마다 한 줄씩 잘 찍히는지 확인 +- 950nm 필터 낀 카메라로 9개가 겹치지 않고 잘 분리돼서 찍히는지 확인 +- 노출 시간 등 카메라 설정 튜닝 + +→ 순서는 readme.md의 "10. 브링업 절차"에 이미 정리돼 있어서 그대로 따라가면 됨. + +--- + +## 6. 배선 연결도 (현재 미배선 — 이대로 연결하면 됨) + +### 준비물 + +- LD274 (950nm IR LED) × 9 +- 330Ω 저항 × 9 (연결하고 밝기가 너무 약하면 220Ω으로 교체) +- 점퍼선 여러 개, 브레드보드 또는 만능기판 +- NUCLEO-L073RZ 보드 + +### 한 채널 기본 회로 (이걸 9번 반복) + +LED는 극성이 있다 — 방향을 반대로 꽂으면 안 켜진다. + +- **긴 다리 = +(애노드)** +- **짧은 다리 = -(캐소드)**, 다리를 이미 잘랐다면 LED 몸통 테두리에 살짝 평평하게 깎인 부분이 있는 쪽이 캐소드 + +``` +NUCLEO 보드의 3V3 핀 + │ + │ (9개 LED가 전부 여기 한 곳에 같이 연결됨 — 공통) + │ + LED 애노드(+, 긴 다리) + │ + LED 캐소드(-, 짧은 다리) + │ + 330Ω 저항 + │ + MCU 핀 (아래 표에서 해당 LED에 배정된 핀) +``` + +전류가 3V3 → LED → 저항 → MCU 핀 순서로 흐르고 MCU 안에서 빠지는 구조라 별도로 GND를 연결할 필요는 없다 (보드가 USB로 컴퓨터에 물려 있으면 그걸로 충분). + +### 어떤 LED를 어떤 핀에 연결할지 (9개 전부) + +| LED 번호 | 연결할 MCU 핀 | 저항 | +|---|---|---| +| G1 | PC0 | 330Ω | +| G2 | PC1 | 330Ω | +| G3 | PC3 | 330Ω | +| G4 | PC4 | 330Ω | +| G5 | PC5 | 330Ω | +| G6 | PC6 | 330Ω | +| G7 | PC7 | 330Ω | +| G8 | PC8 | 330Ω | +| G9 | PC2 | 330Ω | + +핀 이름(PC0, PC1 ...)은 NUCLEO 보드의 커넥터(모포 헤더, CN10 쪽)에 실크스크린으로 그대로 인쇄되어 있어서 이름으로 찾으면 된다. 3V3 핀도 마찬가지로 보드에 "3V3"라고 인쇄돼 있는 핀을 쓰면 된다. + +> G9가 PC2인 게 좀 이상해 보일 수 있는데, G1~G8을 먼저 PC0~PC1, PC3~PC8에 배정해놓은 상태에서 마지막으로 남는 자리가 PC2였기 때문이다 (자세한 사연은 readme.md 6.7절). 표 그대로 연결하면 문제없다. + +### 카메라 쪽에서 봤을 때 실제 배치 (3×3 격자, 간격 10mm) + +패널에 LED를 심을 때 이 배치를 따른다 — 카메라가 찍는 화면 기준으로 자리를 잡은 것이다. + +``` + 좌 중 우 + ┌──────────┬──────────┬──────────┐ + 상단 │ G1 │ G2 │ G3 │ + │ (PC0) │ (PC1) │ (PC3) │ + ├──────────┼──────────┼──────────┤ + 중단 │ G4 │ G5 │ G6 │ + │ (PC4) │ (PC5) │ (PC6) │ + ├──────────┼──────────┼──────────┤ + 하단 │ G7 │ G8 │ G9 │ + │ (PC7) │ (PC8) │ (PC2) │ + └──────────┴──────────┴──────────┘ + + LED 중심 간 간격 10mm, LED 몸통 지름 5.1mm +``` + +### 연결 순서 (체크리스트) + +1. [ ] LED 9개 + 저항 9개 극성/자리 맞춰서 위 표대로 하나씩 연결 +2. [ ] LED 애노드(+) 9개를 전부 한 군데로 모아서 3V3 핀 하나에 연결 +3. [ ] 저항 반대쪽 끝을 표에 있는 MCU 핀에 각각 연결 +4. [ ] 전부 연결했으면 다시 한번 표랑 대조 — 특히 G9가 PC2로 갔는지 확인 +5. [ ] readme.md 10장 "브링업 절차"대로, 처음엔 LED 1개(G1/PC0)만 켜보고 정상이면 나머지 연결 + +**주의할 것:** +- LED 극성 반대로 끼우면 안 켜짐 (고장 아니니 방향만 바꿔서 다시 확인) +- 저항 없이 LED를 직접 핀에 연결하면 과전류로 LED나 핀이 망가질 수 있음 — 저항 절대 빼먹지 말 것 +- 9개 중 하나라도 다른 핀에 잘못 꽂으면, 코드 로그(G1=.. G2=..)랑 실제 불빛 위치가 안 맞게 되니 표 대조를 꼼꼼히 + +### 정답 로그(시리얼)는 배선 필요 없음 + +LED와 달리 UART 로그(USART2, PA2/PA3)는 **추가로 연결할 게 없다.** NUCLEO 보드 안에서 PA2/PA3가 보드에 내장된 ST-LINK로 이미 연결돼 있어서, 지금 펌웨어 업로드용으로 꽂는 USB 케이블 하나가 곧 가상 COM 포트(VCP) 역할도 한다. + +- PC에서 터미널 프로그램(PuTTY, screen, CoolTerm, VS Code 시리얼 모니터 등) 실행 +- 보드가 잡힌 COM 포트를 **115200 8N1**로 열기 +- 8초마다 `CYCLE=.. G1=.. G2=.. ... G9=..` 한 줄씩 찍히면 정상 + +### CoolTerm에 로그가 안 뜬 문제 — 해결됨 (원인은 보드 배선, 펌웨어는 정상이었음) + +**→ SB13, SB14 브리지하고 나서 CoolTerm에 로그 정상적으로 뜨는 거 확인함 (2026-08-22).** + +2026-08-22, CoolTerm으로 열었는데 아무것도 안 뜬다는 얘기가 나와서 ST-LINK로 보드에 직접 붙어 확인했다. 순서대로: + +1. 최신 펌웨어를 다시 플래시하고 리셋 → CoolTerm 대신 터미널에서 시리얼 포트를 직접 열어서 20초 넘게 지켜봐도 **0바이트**. CoolTerm 설정 문제가 아니라 보드에서 아무것도 안 나오고 있다는 뜻. +2. ST-LINK로 MCU에 붙어서 **보드가 정지 없이 실제로 돌아가는 상태 그대로(Hot Plug 모드)** 레지스터를 직접 읽어봄: + - GPIOA/C/H 클럭 켜짐, TIM6·USART2 클럭 켜짐 — 전부 코드에서 의도한 그대로 + - PA2/PA3가 USART2용 핀 모드(AF4)로 정확히 설정돼 있음 + - PC0~PC8(G1~G9, PC2 포함) 전부 출력모드로 정확히 설정돼 있음 — 6.7절에서 고친 내용이 실제로 반영돼서 동작 중 + - USART2 레지스터 확인 결과 **송신 기능이 켜져서 실제로 활성 상태**(TEACK=1), 보레이트 값도 계산했던 값(139)과 정확히 일치 + - RAM에 있는 카운트 값도 우리가 만든 공식과 한 자리도 안 틀리고 일치 + - 반대로 USART2 수신 쪽에는 "깨진 데이터를 받았다"는 에러 플래그가 떠 있었음 — 그 핀이 뭔가에 연결 안 된 채 붕 떠서 잡음을 줍고 있다는 신호 + +→ **MCU 내부에서는 데이터를 제대로 내보내고 있는데, 그게 컴퓨터까지 한 바이트도 안 온다.** 펌웨어/설정 쪽은 이걸로 결백이 증명된 셈이고, 남은 용의선상은 **PA2/PA3와 ST-LINK를 이어주는 보드 위의 연결(솔더 브리지)이 끊겨 있는 경우**뿐이다. + +**정확한 솔더 브리지 번호 (ST 공식 UM1724 매뉴얼, "5.7 USART communication" / "Table 9. Solder bridges" 확인함):** + +NUCLEO-64 보드(L073RZ 포함)는 PA2/PA3를 어디로 보낼지 솔더 브리지 4개로 정한다. + +| 브리지 | 정상(VCP 사용) 상태 | 역할 | +|---|---|---| +| **SB13, SB14** (ST-LINK-USART) | **ON (이어져 있어야 함)** | PA2/PA3 ↔ ST-LINK MCU의 USART를 연결 — 이게 켜져 있어야 시리얼 로그가 PC로 나옴 | +| **SB62, SB63** (USART) | **OFF (끊겨 있어야 함)** | PA2/PA3를 대신 Arduino 커넥터(CN9의 D1/D0)나 Morpho 커넥터(CN10) 쪽으로 돌림. 이게 켜져 있으면 ST-LINK 쪽 연결이 끊긴다 | + +즉 **SB13·SB14가 열려있거나(OFF), SB62·SB63가 닫혀있으면(ON)** 지금 겪는 증상(펌웨어는 정상인데 로그가 안 뜸)과 정확히 일치한다. + +**확인 방법:** +1. 보드에서 SB13, SB14, SB62, SB63 실크스크린 표시를 찾는다 (ST-LINK 쪽 회로와 CN9/CN10 커넥터 사이 어딘가에 작은 두 패드로 표시돼 있음) +2. 멀티미터 연속성(continuity) 모드로 SB13, SB14 각각의 두 패드를 찍어본다 → **삑 소리(연결됨)가 나야 정상.** 안 나면 납땜으로 이어줘야 함 +3. SB62, SB63도 같은 방식으로 찍어본다 → **삑 소리가 안 나야(끊겨 있어야) 정상.** 소리가 나면 커터로 끊어야 함 +4. 넷 다 정상 상태로 맞춘 뒤 다시 CoolTerm으로 확인 + +**급하면:** 별도 USB-TTL 시리얼 어댑터를 PA2(TX)/PA3(RX)/GND에 직접 물려서 ST-LINK VCP를 아예 안 거치고 확인. 아니면 다른 NUCLEO-L073RZ 보드로 교체 — 펌웨어는 이미 검증됐으니 보드만 바꾸면 바로 될 가능성이 큼. + +출처: [UM1724 User manual - STM32 Nucleo-64 boards (MB1136)](https://www.st.com/resource/en/user_manual/um1724-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf), §5.7 USART communication / Table 9 Solder bridges (DocID025833 Rev 8) + +이걸로 카메라 쪽 판독 결과랑 대조하면 된다. 안 찍히면 보드레이트(115200)부터 다시 확인. + +--- + +## 7. LED가 잘 안 보임 — 저항/노출 얼마까지 밀어붙여도 되나 + +2026-08-22, 노출 20ms + 330Ω→220Ω로 바꿨는데도 LED가 협각(±10°) 때문에 잘 안 보인다는 얘기가 나와서, STM32L073 공식 데이터시트(DS10685)로 실제 한계를 확인했다. + +### 저항 — 220Ω에서 더 낮추지 말 것 + +데이터시트 Table 24(절대최대정격) 기준: + +| 항목 | 절대최대값 | +|---|---| +| 핀 1개가 sink하는 전류 | 16 mA | +| **9개 핀 합계로 sink하는 전류** | **90 mA** | + +Table 61(출력전압특성)을 보면 15mA 지점에서 V_OL이 **최대 1.3V**까지 올라간다(8mA 기준으로 잡았던 0.4V가 아님). 즉 저항을 낮춰도 핀 자체의 전압강하가 커져서 기대만큼 전류가 안 오를 수 있다. + +**더 중요한 건 합계 90mA 쪽이다.** 사이클 시작 직후엔 설계상 9개가 동시에 켜지는 순간이 항상 있는데, 지금 220Ω(약 8mA×9≈72mA)만 해도 이미 90mA 한도에 꽤 붙어 있다. 여기서 더 낮추면 그 동시 점등 순간에 절대정격을 넘길 수 있다. + +→ **220Ω보다 낮추지 말 것.** 180Ω 정도가 실질적 하한선. + +### 노출 — 60ms 근처까지는 늘려도 됨 + +지금 20ms는 30fps 프레임 주기(33ms) 안에 들어가 안전. 5.4절에서 이미 "카메라가 15fps로 떨어져도 상태당 최소 3프레임 확보"를 설계 마진으로 잡아뒀으므로, **15fps로 떨어지는 걸 감수하면 노출을 60ms 근처까지 늘려도 이 마진 안에 있음.** 그 이상(10fps대)은 5.4절 표에서 이미 "위험"으로 표시해둔 구간이라 피할 것. + +전류보다 노출을 늘리는 쪽이 안전하고 효과도 큼 — 3.4절에서 이미 권장했던 방향과 같음. + +### 그래도 안 보이면: 협각 자체가 원인일 가능성 + +±10° 협각은 전류·노출로 완전히 못 고친다. 카메라가 LED 패널 정면 중앙에 잘 정렬돼 있는지(각 LED가 카메라를 정확히 향하고 있는지) 먼저 확인. 정렬이 어긋나 있으면 아무리 밝게/오래 노출해도 카메라가 빔 가장자리(광량 30~50% 이하 구간)만 보게 되므로 근본 해결이 안 됨. + +출처: [Datasheet - STM32L073x8 STM32L073xB STM32L073xZ (DS10685)](https://www.st.com/resource/en/datasheet/stm32l073v8.pdf), §6.2 Table 24 Current characteristics / §6.3.13 Table 61 Output voltage characteristics