/******************************************************************************* * @file main.c * @brief MEDiThings VivaMayo - Main Application Entry Point * @author Charles KWON * @date 2025-01-30 * @copyright (c) 2025 Medithings Inc. All rights reserved. * * @version 1.17 * @note 2025-01-30 Refactored into modular structure (Charles KWON) * @note 2025-12-31 Added comprehensive function documentation (Charles KWON) * @note 2025-11-27 Firmware cleanup and refactoring (Charles KWON) ******************************************************************************/ #include #include #include #include "debug_print.h" #include "app_uart.h" /* Nordic SDK */ #include "nordic_common.h" #include "nrf.h" #include "nrf_gpio.h" #include "nrf_sdh.h" #include "nrf_sdh_soc.h" #include "nrf_sdh_ble.h" #include "nrf_pwr_mgmt.h" #include "nrf_delay.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #include "app_timer.h" #include "app_error.h" #include "bsp.h" #include "bsp_btn_ble.h" #include "ble_nus.h" /* Application Modules */ #include "ble/ble_core.h" #include "ble/ble_services.h" #include "ble/ble_data_tx.h" #include "power/power_ctrl.h" #include "peripheral/uart_handler.h" #include "config/device_config.h" #if FEATURE_SECURE_CONNECTION #include "ble/ble_security.h" #include "nrf_ble_lesc.h" #endif /* Hardware and Measurement Modules */ #include "main.h" #include "main_timer.h" #include "battery_saadc.h" #include "measurements.h" #include "full_agc.h" #include "meas_pd_voltage_simple.h" #include "meas_pd_voltage_half.h" /* #include "meas_pd_voltage_full.h" */ /* Moved to unuse/ - FEATURE_PRINTF=0, not used */ #include "meas_pd_voltage_custom.h" #include "meas_pd_imm.h" #include "meas_pd_48.h" #include "power_control.h" #include "cat_interface.h" #include "fstorage.h" #include "drivers/w25q32/w25q32.h" /* Crypto */ #include "nrf_crypto.h" /******************************************************************************* * @section BUILD_CONFIG Build Configuration * @brief Compile-time feature flags and debug settings * @details Controls boot mode selection between minimal (Power+BLE only) and * full initialization (includes EEPROM and all peripherals). * Set DEBUG_MINIMAL_BOOT=1 for rapid debugging without EEPROM dependency. ******************************************************************************/ #define DEBUG_MINIMAL_BOOT 1 /**< 1: Power+BLE only (no EEPROM), 0: Full boot */ /******************************************************************************* * @section CONSTANTS Symbolic Constants * @brief Application-wide constant definitions * @details Magic numbers and compile-time constants used for error handling, * fault detection, and system-level operations. ******************************************************************************/ #define DEAD_BEEF 0xDEADBEEF /******************************************************************************* * @section GLOBALS Global Variables * @brief Shared runtime state and data buffers * @details BLE transmission buffers, command type state, and cross-module * flags. External declarations reference state from other modules. * @warning Global state should be accessed with care in interrupt context. ******************************************************************************/ char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN]; uint16_t ble_bin_buff[BLE_NUS_MAX_DATA_LEN/2]; which_cmd_t cmd_type_t; /* External state flags from other modules */ extern bool ble_got_new_data; extern bool motion_data_once; extern bool adc_enabled; extern bool con_single; extern bool info4; extern uint8_t add_cycle; extern bool motion_raw_data_enabled; /******************************************************************************* * @section FWD_DECL Forward Declarations * @brief Static function prototypes * @details Internal initialization and handler functions declared here for * proper ordering. Visibility limited to this translation unit. ******************************************************************************/ static void timers_init(void); static void log_init(void); static void power_management_init(void); static void buttons_leds_init(bool * p_erase_bonds); #if !DEBUG_MINIMAL_BOOT static void gpio_init(void); #endif static void idle_state_handle(void); /******************************************************************************* * @section ASSERT Assert Handler * @brief System fault callback for SDK assertions * @details Invoked by Nordic SDK on fatal errors. Provides fault location * (file and line) for debugging via app_error_handler. ******************************************************************************/ void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name) { app_error_handler(DEAD_BEEF, line_num, p_file_name); } /******************************************************************************* * @section INIT Initialization Functions * @brief System and peripheral initialization routines * @details Configures logging, power management, timers, and BSP components. * Call order is critical - see main() for proper sequencing. ******************************************************************************/ static void log_init(void) { ret_code_t err_code = NRF_LOG_INIT(NULL); APP_ERROR_CHECK(err_code); NRF_LOG_DEFAULT_BACKENDS_INIT(); } static void power_management_init(void) { ret_code_t err_code; err_code = nrf_pwr_mgmt_init(); APP_ERROR_CHECK(err_code); } static void timers_init(void) { ret_code_t err_code = app_timer_init(); APP_ERROR_CHECK(err_code); /* Power control timers */ power_ctrl_timers_init(); /* Application timers */ main_timer_init(); battery_timer_init(); imm_check_timer_init(); m48_check_timer_init(); full_agc_timer_init(); full_agc_send_timer_init(); mea_send_timer_init(); power_timer_init(); w25q_test_timer_init(); /* full_timer_init() - Moved to unuse/, FEATURE_PRINTF=0 so not used */ } static void buttons_leds_init(bool * p_erase_bonds) { bsp_event_t startup_event; uint32_t err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, power_bsp_event_handler); APP_ERROR_CHECK(err_code); err_code = bsp_btn_ble_init(NULL, &startup_event); APP_ERROR_CHECK(err_code); *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA); } /******************************************************************************* * @section PWR_HOLD Power Hold Initialization * @brief Latches power supply on boot * @details Configures P0.08 (POWER_HOLD) as output HIGH to maintain VCC. * CRITICAL: Must be the first call in main() - any delay risks * power loss if button is released before latch is set. * @note Uses direct register access for minimum latency. ******************************************************************************/ static void power_hold_init(void) { /* P0.08 = POWER_HOLD, configure as output and set HIGH to maintain power */ NRF_P0->DIRSET = (1 << 8); NRF_P0->OUTSET = (1 << 8); } /******************************************************************************* * @section MIN_GPIO Minimal GPIO Initialization (Debug Mode) * @brief Reduced GPIO setup for rapid debugging * @details Initializes only essential pins: POWER_BUTTON input, POWER_HOLD * output, and power control GPIOs. Skips LEDs, PD array, gain * switches, and EEPROM control for faster boot. * @pre DEBUG_MINIMAL_BOOT must be set to 1 ******************************************************************************/ static void minimal_gpio_init(void) { nrf_gpio_cfg_input(POWER_BUTTON, NRF_GPIO_PIN_NOPULL); nrf_gpio_cfg_output(POWER_HOLD); nrf_gpio_pin_set(POWER_HOLD); power_gpio_init(); LOG_PRINTF("[GPIO] Minimal OK\r\n"); } /******************************************************************************* * @section MIN_CFG Default Configuration Loader (Debug Mode) * @brief Loads hardcoded defaults bypassing EEPROM * @details Sets factory defaults for serial number, passkey, PD timing, * and bonding state. Used when EEPROM is unavailable or for * rapid iteration during development. * @pre DEBUG_MINIMAL_BOOT must be set to 1 ******************************************************************************/ static void load_default_config(void) { /* Direct assignment of variables defined in device_config.h */ memset(SERIAL_NO, 0, sizeof(SERIAL_NO)); memcpy(SERIAL_NO, "2025VIVAM00001", 14); memset(m_static_passkey, 0, sizeof(m_static_passkey)); memcpy(m_static_passkey, "123456", 6); m_pd_delay_us = 8000; m_pd_adc_cnt = 8; bond_data_delete = true; LOG_PRINTF("[CFG] Default (S/N=%s)\r\n", SERIAL_NO); } /******************************************************************************* * @section FULL_GPIO Full GPIO Initialization (Production Mode) * @brief Complete peripheral GPIO configuration * @details Initializes all hardware interfaces: LEDs, photodiode array (PD), * trigger switches, AGC gain control, power management, and EEPROM. * All outputs are set to safe default states (LEDs off, PD off). * @pre DEBUG_MINIMAL_BOOT must be set to 0 ******************************************************************************/ #if !DEBUG_MINIMAL_BOOT static void gpio_init(void) { nrf_gpio_cfg_input(POWER_BUTTON, NRF_GPIO_PIN_NOPULL); LED_CONFIG(); LED_ALLOFF(); PD_CONFIG(); PD_ALLOFF(); trig_r_CONFIG(); trig_SW(false); GAIN_SW_CONFIG(); AGC_GAIN_SW(false); power_gpio_init(); eeprom_control(OFF); } #endif /******************************************************************************* * @section IDLE Idle State Handler * @brief Low-power idle loop processing * @details Handles security operations (LESC if enabled), flushes NRF_LOG * buffer, then enters System ON sleep via nrf_pwr_mgmt_run(). * CPU wakes on any enabled interrupt (BLE, timer, GPIO). ******************************************************************************/ static void idle_state_handle(void) { #if FEATURE_SECURE_CONNECTION security_idle_state_handle(); #endif if (NRF_LOG_PROCESS() == false) { nrf_pwr_mgmt_run(); } } /******************************************************************************* * @section MAIN Application Entry Point * @brief System initialization and main loop * @details Boot sequence: Power latch → UART → Logging → GPIO → Timers → * Config → BSP → Power Mgmt → BLE Stack → GAP → GATT → Services → * Advertising → Connection Params → Power Button Handler. * Main loop runs idle_state_handle() for low-power operation. * @note Initialization order is critical for proper hardware bringup. ******************************************************************************/ /* LED slow blink macro (approximately 0.5 second period) */ #define LED_BLINK() do { \ NRF_P0->OUTSET = (1 << 12); \ for (volatile uint32_t _d = 0; _d < 1000000; _d++); \ NRF_P0->OUTCLR = (1 << 12); \ for (volatile uint32_t _d = 0; _d < 1000000; _d++); \ } while(0) #define LED_PAUSE() do { \ for (volatile uint32_t _d = 0; _d < 2000000; _d++); \ } while(0) int main(void) { #if FEATURE_SECURE_CONNECTION bool erase_bonds_local = false; #endif /* Configure LED output (P0.12) */ NRF_P0->DIRSET = (1 << 12); power_hold_init(); if (power_off_duble_prohibit) return 0; cnt_s = 0; uart_handler_init(); nrf_delay_ms(100); /* Wait for UART peripheral stabilization */ log_init(); g_log_enable = true; /* Enable boot logging (set to false to disable boot logs) */ LOG_PRINTF("\r\n\r\n"); LOG_PRINTF("========================================\r\n"); LOG_PRINTF(" VivaMayo UART OK\r\n"); LOG_PRINTF("========================================\r\n"); #if DEBUG_MINIMAL_BOOT LOG_PRINTF("[1] GPIO (minimal)\r\n"); minimal_gpio_init(); #else LOG_PRINTF("[1] GPIO (full)\r\n"); gpio_init(); #endif info4 = false; LOG_PRINTF("[2] Timers\r\n"); timers_init(); LOG_PRINTF("[4] Buttons/LEDs\r\n"); #if FEATURE_SECURE_CONNECTION buttons_leds_init(&erase_bonds_local); erase_bonds = erase_bonds_local; #else bool dummy_erase; buttons_leds_init(&dummy_erase); #endif LOG_PRINTF("[5] PWR\r\n"); nrf_delay_ms(10); power_management_init(); LOG_PRINTF("[6] BLE\r\n"); nrf_delay_ms(10); ble_stack_init(); /* Flash Storage - must be after BLE stack (SoftDevice required) */ /* and before GAP params (to use loaded config for device name) */ LOG_PRINTF("[6.5] FDS\r\n"); nrf_delay_ms(10); fs_storage_init(); config_load(); LOG_PRINTF("[6.6] Config\r\n"); load_device_configuration(); /* W25Q32 Flash - NOT initialized during boot * Initialize via BLE command after advertising starts * (부팅 시 SPIM + BSP 이벤트 충돌로 SYSTEM OFF 발생 방지) */ LOG_PRINTF("[7] GAP\r\n"); nrf_delay_ms(10); gap_params_init(); gatt_init(); services_init(); LOG_PRINTF("[8] ADV\r\n"); nrf_delay_ms(10); advertising_init(); conn_params_init(); power_ctrl_timers_start(); /* advertising_start() is invoked from power_ctrl.c after 5ms delay */ nrf_delay_ms(20); LOG_PRINTF("=== READY ===\r\n"); /*---------------------------------------------------------- * Main Loop: Idle State Handler *----------------------------------------------------------*/ for (;;) { idle_state_handle(); } }