From 2861cb981597dad1608b8c695887282e758977ea Mon Sep 17 00:00:00 2001 From: jhchun Date: Thu, 16 Apr 2026 12:01:51 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 주석 영문으로 변경 - Allman 스타일로 통일 --- .../ble_app_bladder_patch/command/cmd_table.c | 7 +- .../command/handlers/cmd_device.c | 32 +- .../command/handlers/cmd_info.c | 6 +- .../command/handlers/cmd_piezo.c | 100 +- .../command/handlers/cmd_sensor.c | 19 +- .../command/handlers/cmd_sensor.h | 5 +- .../ble_app_bladder_patch/command/parser.c | 92 +- .../ble_app_bladder_patch/hal/fds/fstorage.c | 293 +++--- .../ble_app_bladder_patch/hal/fds/fstorage.h | 104 +-- .../hal/i2c/i2c_manager.c | 149 ++- .../hal/i2c/i2c_manager.h | 49 +- .../ble_app_bladder_patch/main.c | 860 +++++++++--------- .../ble_app_bladder_patch/main.h | 150 +-- .../measurement/adc121s051/dr_adc121s051.c | 441 ++++++--- .../measurement/adc121s051/dr_adc121s051.h | 11 +- .../measurement/battery/battery_saadc.c | 339 +++---- .../measurement/battery/battery_saadc.h | 41 +- .../measurement/imu/app_raw/app_raw.c | 269 +++--- .../measurement/imu/app_raw/app_raw.h | 74 +- .../measurement/imu/app_raw/app_raw_main.c | 162 ++-- .../measurement/imu/app_raw/app_raw_main.h | 17 +- .../measurement/imu/system_interface.c | 124 +-- .../measurement/imu/system_interface.h | 41 +- .../measurement/piezo/dr_piezo.c | 502 +++++----- .../measurement/piezo/dr_piezo.h | 265 ++---- .../measurement/temperature/tmp235_q1.c | 250 ++--- .../measurement/temperature/tmp235_q1.h | 31 +- .../system/debug_print.h | 30 +- .../system/led/led_control.c | 92 +- .../system/led/led_control.h | 42 +- .../ble_app_bladder_patch/system/main_timer.c | 186 ++-- .../ble_app_bladder_patch/system/main_timer.h | 24 +- .../system/power/power_control.c | 114 +-- .../system/power/power_control.h | 32 +- .../system/security/ble_quick_security.c | 10 +- 35 files changed, 2406 insertions(+), 2557 deletions(-) diff --git a/project/ble_peripheral/ble_app_bladder_patch/command/cmd_table.c b/project/ble_peripheral/ble_app_bladder_patch/command/cmd_table.c index 46d377b..bdeccdb 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/command/cmd_table.c +++ b/project/ble_peripheral/ble_app_bladder_patch/command/cmd_table.c @@ -1,10 +1,9 @@ /*============================================================================== * cmd_table.c - Command table definition * - * This file is the only linkage point between parser.c and the cmd_*.c - * handler modules. It pulls in every cmd_*.h to gather handler prototypes, - * builds the CmdEntry array, and injects the table into the parser via - * dr_parser_init() inside cmd_table_init(). + * This file is the only linkage point between parser.c and the cmd_*.c handler modules. + * It pulls in every cmd_*.h to gather handler prototypes, + * builds the CmdEntry array, and injects the table into the parser via dr_parser_init() inside cmd_table_init(). * * Adding a new command: * 1) Implement the handler in the appropriate cmd_*.c diff --git a/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_device.c b/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_device.c index f50e502..7405ab0 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_device.c +++ b/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_device.c @@ -19,9 +19,8 @@ * Request: [TAG 4B "msq?"] [val 2B BE] [CRC 2B] * Response: [TAG 4B "rsq:"] [val 2B BE] [CRC 2B] * - * Sends BLE response first; the actual power-off is performed from the main - * loop. Powering off immediately would prevent the response from reaching - * the host, so a flag + timer pattern is used instead. + * Sends BLE response first; the actual power-off is performed from the main loop. + * Powering off immediately would prevent the response from reaching the host, so a flag + timer pattern is used instead. *============================================================================*/ int Cmd_msq(const ParsedCmd *cmd) { @@ -44,8 +43,7 @@ int Cmd_msq(const ParsedCmd *cmd) * Request: [TAG 4B "mss?"] [val 2B BE] [CRC 2B] * Response: [TAG 4B "rss:"] [val 2B BE] [CRC 2B] * - * Resets without erasing bond information. Reset status code is persisted - * to FDS so the boot path can identify the cause. + * Resets without erasing bond information. Reset status code is persisted to FDS so the boot path can identify the cause. *============================================================================*/ int Cmd_mss(const ParsedCmd *cmd) { @@ -118,9 +116,18 @@ int Cmd_cmd(const ParsedCmd *cmd) return 1; } - if (!dr_get_u16(cmd, 0, &v1)) v1 = 0; - if (!dr_get_u16(cmd, 1, &v2)) v2 = 0; - if (!dr_get_u16(cmd, 2, &v3)) v3 = 0; + if (!dr_get_u16(cmd, 0, &v1)) + { + v1 = 0; + } + if (!dr_get_u16(cmd, 1, &v2)) + { + v2 = 0; + } + if (!dr_get_u16(cmd, 2, &v3)) + { + v3 = 0; + } pin_number = NRF_GPIO_PIN_MAP(v1, v2); nrf_gpio_cfg_output(pin_number); @@ -142,15 +149,15 @@ int Cmd_cmd(const ParsedCmd *cmd) * mls? -> rls: Set LED state (app -> device) * * Request: [TAG 4B "mls?"] [state 2B BE] [CRC 2B] - * state: led_state_t enum value - * 0=OFF, 4=DETACH_WARNING, 5=ALIGN_SEARCHING, 6=ALIGN_COMPLETE + * state: led_state_t enum value: 0=OFF, 4=DETACH_WARNING, 5=ALIGN_SEARCHING, 6=ALIGN_COMPLETE * Response: [TAG 4B "rls:"] [state 2B] [CRC 2B] * Error: rls: + 0xFFFF (insufficient data) * rls: + 0xFFFE (state out of range) *============================================================================*/ int Cmd_mls(const ParsedCmd *cmd) { - if (cmd->data_len < 2) { + if (cmd->data_len < 2) + { dr_ble_return_1("rls:", 0xFFFF); return 1; } @@ -158,7 +165,8 @@ int Cmd_mls(const ParsedCmd *cmd) uint16_t state; dr_get_u16(cmd, 0, &state); - if (state > LED_STATE_ERROR) { + if (state > LED_STATE_ERROR) + { dr_ble_return_1("rls:", 0xFFFE); return 1; } diff --git a/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_info.c b/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_info.c index d4d671f..2039635 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_info.c +++ b/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_info.c @@ -57,7 +57,8 @@ int Cmd_mwh(const ParsedCmd *cmd) { char buf[13]; - if (cmd->data_len < 12) { + if (cmd->data_len < 12) + { dr_ble_return_1("rwh:", 0xFFFF); return 1; } @@ -83,7 +84,8 @@ int Cmd_mws(const ParsedCmd *cmd) { char buf[13]; - if (cmd->data_len < 12) { + if (cmd->data_len < 12) + { dr_ble_return_1("rws:", 0xFFFF); return 1; } diff --git a/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_piezo.c b/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_piezo.c index 5aba363..b0bb065 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_piezo.c +++ b/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_piezo.c @@ -22,15 +22,14 @@ * * Request: [TAG 4B "mpa?"] [CRC 2B] * Response: [TAG 4B "rpa:"] [1 2B] [CRC 2B] - * - * Must be called before mec? / maa?. *============================================================================*/ int Cmd_mpa(const ParsedCmd *cmd) { (void)cmd; dr_piezo_power_on(); - if (g_plat.tx_bin) { + if (g_plat.tx_bin) + { single_format_data(ble_bin_buffer, "rpa:", 1); dr_binary_tx_safe(ble_bin_buffer, 3); } @@ -50,7 +49,8 @@ int Cmd_mpb(const ParsedCmd *cmd) (void)cmd; dr_piezo_power_off(); - if (g_plat.tx_bin) { + if (g_plat.tx_bin) + { single_format_data(ble_bin_buffer, "rpb:", 1); dr_binary_tx_safe(ble_bin_buffer, 3); } @@ -79,22 +79,36 @@ int Cmd_mpc(const ParsedCmd *cmd) (void)dr_get_u16(cmd, 1, &freq_option); (void)dr_get_u16(cmd, 2, &piezo_ch); - if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; + if (piezo_ch >= MAA_NUM_CHANNELS) + { + piezo_ch = 0; + } - if (cycles < 3 || cycles > 7) { + if (cycles < 3 || cycles > 7) + { dr_ble_return_1("rpc:", 2); return 1; } dr_piezo_select_channel((uint8_t)piezo_ch); - switch (freq_option) { - case 0: dr_piezo_burst_sw_18mhz((uint8_t)cycles); break; - case 2: dr_piezo_burst_sw_20mhz((uint8_t)cycles); break; - case 3: dr_piezo_burst_sw_17mhz((uint8_t)cycles); break; - case 4: dr_piezo_burst_sw_22mhz((uint8_t)cycles); break; - case 1: - default: dr_piezo_burst_sw((uint8_t)cycles); break; + switch (freq_option) + { + case 0: + dr_piezo_burst_sw_18mhz((uint8_t)cycles); + break; + case 2: + dr_piezo_burst_sw_20mhz((uint8_t)cycles); + break; + case 3: + dr_piezo_burst_sw_17mhz((uint8_t)cycles); + break; + case 4: + dr_piezo_burst_sw_22mhz((uint8_t)cycles); + break; + default: + dr_piezo_burst_sw((uint8_t)cycles); + break; } dr_ble_return_1("rpc:", (uint8_t)cycles); @@ -121,7 +135,8 @@ int Cmd_mec(const ParsedCmd *cmd) uint16_t averaging = 1; uint16_t piezo_ch = 0; - if (!dr_piezo_is_power_on()) { + if (!dr_piezo_is_power_on()) + { dr_piezo_power_on(); } @@ -132,15 +147,25 @@ int Cmd_mec(const ParsedCmd *cmd) (void)dr_get_u16(cmd, 4, &averaging); (void)dr_get_u16(cmd, 5, &piezo_ch); - if (averaging == 0) averaging = 1; - if (averaging > 1000) averaging = 1000; - if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; + if (averaging == 0) + { + averaging = 1; + } + if (averaging > 1000) + { + averaging = 1000; + } + if (piezo_ch >= MAA_NUM_CHANNELS) + { + piezo_ch = 0; + } dr_adc_err_t err = dr_adc_burst_capture_transmit( (uint8_t)freq_option, delay_us, num_samples, (uint8_t)cycles, (uint16_t)averaging, (uint8_t)piezo_ch, ble_bin_buffer, 0); - if (err != DR_ADC_OK) { + if (err != DR_ADC_OK) + { dr_ble_return_2("rer:", 0xEE00 | (uint16_t)err, num_samples); } @@ -165,12 +190,14 @@ int Cmd_maa(const ParsedCmd *cmd) dr_adc_err_t err; (void)cmd; - if (maa_async_is_busy()) { + if (maa_async_is_busy()) + { dr_ble_return_1("raa:", 0xFFFE); return 1; } - if (!dr_piezo_is_power_on()) { + if (!dr_piezo_is_power_on()) + { dr_piezo_power_on(); } @@ -183,8 +210,12 @@ int Cmd_maa(const ParsedCmd *cmd) ble_bin_buffer ); - if (err != DR_ADC_OK) { - if (g_plat.log) g_plat.log("[Cmd_maa] start failed err=%d\r\n", err); + if (err != DR_ADC_OK) + { + if (g_plat.log) + { + g_plat.log("[Cmd_maa] start failed err=%d\r\n", err); + } single_format_data(ble_bin_buffer, "raa:", (uint16_t)(0xFF00 | err)); dr_binary_tx_safe(ble_bin_buffer, 3); dr_piezo_power_off(); @@ -214,7 +245,8 @@ int Cmd_mbb(const ParsedCmd *cmd) all_sensors(); - if (maa_async_is_busy()) { + if (maa_async_is_busy()) + { dr_ble_return_1("raa:", 0xFFFE); return 1; } @@ -230,8 +262,12 @@ int Cmd_mbb(const ParsedCmd *cmd) ble_bin_buffer ); - if (err != DR_ADC_OK) { - if (g_plat.log) g_plat.log("[Cmd_mbb] start failed err=%d\r\n", err); + if (err != DR_ADC_OK) + { + if (g_plat.log) + { + g_plat.log("[Cmd_mbb] start failed err=%d\r\n", err); + } single_format_data(ble_bin_buffer, "raa:", (uint16_t)(0xFF00 | err)); dr_binary_tx_safe(ble_bin_buffer, 3); dr_piezo_power_off(); @@ -245,8 +281,7 @@ int Cmd_mbb(const ParsedCmd *cmd) * mcf? -> rcf: Read piezo parameters from FDS * * Request: [TAG 4B "mcf?"] [CRC 2B] - * Response: [TAG 4B "rcf:"] [freq 2B] [cycles 2B] [avg 2B] - * [delay_us 2B] [num_samples 2B] [CRC 2B] + * Response: [TAG 4B "rcf:"] [freq 2B] [cycles 2B] [avg 2B] [delay_us 2B] [num_samples 2B] [CRC 2B] *============================================================================*/ int Cmd_mcf(const ParsedCmd *cmd) { @@ -266,15 +301,18 @@ int Cmd_mcf(const ParsedCmd *cmd) /*============================================================================== * mcs? -> rcs: Write piezo parameters to FDS * - * Request: [TAG 4B "mcs?"] [freq 2B] [cycles 2B] [avg 2B] - * [delay_us 2B] [num_samples 2B] [CRC 2B] + * Request: [TAG 4B "mcs?"] [freq 2B] [cycles 2B] [avg 2B] [delay_us 2B] [num_samples 2B] [CRC 2B] * Response: [TAG 4B "rcs:"] [stored 5 values] [CRC 2B] * Error: rcs: + 0xFFFF (insufficient data) *============================================================================*/ int Cmd_mcs(const ParsedCmd *cmd) { - if (cmd->data_len < 10) { - if (g_plat.log) g_plat.log("[Cmd_mcs] missing params (data_len=%u)\r\n", cmd->data_len); + if (cmd->data_len < 10) + { + if (g_plat.log) + { + g_plat.log("[Cmd_mcs] missing params (data_len=%u)\r\n", cmd->data_len); + } dr_ble_return_1("rcs:", 0xFFFF); return 1; } diff --git a/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_sensor.c b/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_sensor.c index 1a4136a..5c94dac 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_sensor.c +++ b/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_sensor.c @@ -69,9 +69,12 @@ int Cmd_msi(const ParsedCmd *cmd) motion_raw_data_enabled = true; ble_got_new_data = false; - if (cmd->data_len > 0 && (char)cmd->data[0] == 'c') { + if (cmd->data_len > 0 && (char)cmd->data[0] == 'c') + { motion_data_once = false; - } else { + } + else + { motion_data_once = true; } @@ -99,7 +102,8 @@ void all_sensors(void) /* 1. Battery voltage -> info_batt */ battery_saadc_done = false; battery_level_meas(); - for (timeout_cnt = 0; !battery_saadc_done && timeout_cnt < 100; timeout_cnt++) { + for (timeout_cnt = 0; !battery_saadc_done && timeout_cnt < 100; timeout_cnt++) + { dr_sd_delay_ms(1); } @@ -108,13 +112,15 @@ void all_sensors(void) imu_read_direct(); /* 3. Temperature -> info_temp (TMP235 needs Piezo TX/RX power) */ - if (!dr_piezo_is_power_on()) { + if (!dr_piezo_is_power_on()) + { dr_piezo_power_on(); } tmp235_saadc_done = false; tmp235_voltage_level_meas(); - for (timeout_cnt = 0; !tmp235_saadc_done && timeout_cnt < 100; timeout_cnt++) { + for (timeout_cnt = 0; !tmp235_saadc_done && timeout_cnt < 100; timeout_cnt++) + { dr_sd_delay_ms(1); } @@ -126,7 +132,8 @@ void all_sensors(void) buf[4] = (uint8_t)(info_batt >> 8); buf[5] = (uint8_t)(info_batt & 0xFF); - for (int i = 0; i < 6; i++) { + for (int i = 0; i < 6; i++) + { buf[6 + i * 2] = (uint8_t)(info_imu[i] >> 8); buf[6 + i * 2 + 1] = (uint8_t)(info_imu[i] & 0xFF); } diff --git a/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_sensor.h b/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_sensor.h index 5c9c0d3..20d2c89 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_sensor.h +++ b/project/ble_peripheral/ble_app_bladder_patch/command/handlers/cmd_sensor.h @@ -11,9 +11,8 @@ int Cmd_mso(const ParsedCmd *cmd); /* mso? -> rso: TMP235 temperature reading int Cmd_msp(const ParsedCmd *cmd); /* msp? -> rsp: IMU 6-axis single read */ int Cmd_msi(const ParsedCmd *cmd); /* msi? -> rsi: IMU streaming start */ -/* Helper for the mbb? handler: sequentially measures battery / IMU / - * temperature, then emits a single rbb: response. Called from - * Cmd_mbb() in cmd_piezo.c. */ +/* Helper for the mbb? handler: sequentially measures battery / IMU / temperature, then emits a single rbb: response. + * Called from Cmd_mbb() in cmd_piezo.c. */ void all_sensors(void); #endif /* CMD_SENSOR_H */ diff --git a/project/ble_peripheral/ble_app_bladder_patch/command/parser.c b/project/ble_peripheral/ble_app_bladder_patch/command/parser.c index b050d8a..7a03e80 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/command/parser.c +++ b/project/ble_peripheral/ble_app_bladder_patch/command/parser.c @@ -74,11 +74,11 @@ static bool dr_tag_eq(const char *tag, const char *key4) bool dr_get_u16(const ParsedCmd *cmd, uint8_t word_index, uint16_t *out) { uint8_t pos = (uint8_t)(word_index * 2); - if (cmd->data_len < (uint8_t)(pos + 2)) { + if (cmd->data_len < (uint8_t)(pos + 2)) + { return false; } - *out = (uint16_t)((uint16_t)cmd->data[pos] << 8) - | (uint16_t)cmd->data[pos + 1]; + *out = (uint16_t)((uint16_t)cmd->data[pos] << 8) | (uint16_t)cmd->data[pos + 1]; return true; } @@ -87,17 +87,20 @@ void dr_get_ascii(const ParsedCmd *cmd, uint8_t offset, char *out, uint8_t max_l uint8_t i; uint8_t remain; - if (offset >= cmd->data_len) { + if (offset >= cmd->data_len) + { out[0] = '\0'; return; } remain = (uint8_t)(cmd->data_len - offset); - if (remain > max_len) { + if (remain > max_len) + { remain = max_len; } - for (i = 0; i < remain; i++) { + for (i = 0; i < remain; i++) + { out[i] = (char)cmd->data[offset + i]; } out[remain] = '\0'; @@ -113,7 +116,8 @@ uint16_t dr_crc16_compute(const uint8_t *p_data, uint32_t size, const uint16_t * uint32_t i; uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc; - for (i = 0; i < size; i++) { + for (i = 0; i < size; i++) + { crc = (uint8_t)(crc >> 8) | (crc << 8); crc ^= p_data[i]; crc ^= (uint8_t)(crc & 0xFF) >> 4; @@ -134,7 +138,8 @@ static bool dr_crc16_check_packet(const uint8_t *packet, uint32_t packet_len) uint16_t expected_crc; uint32_t data_len; - if (packet_len < 2) { + if (packet_len < 2) + { return false; } @@ -154,7 +159,8 @@ static bool dr_crc16_check_packet(const uint8_t *packet, uint32_t packet_len) *============================================================================*/ static void dr_send_error(const char *err_tag, const char *cmd_tag) { - if (g_plat.tx_bin) { + if (g_plat.tx_bin) + { uint8_t err_buf[8]; memcpy(&err_buf[0], err_tag, 4); memcpy(&err_buf[4], cmd_tag, 4); @@ -173,9 +179,11 @@ static bool dr_parse_cmd(const uint8_t *buffer, uint8_t length, ParsedCmd *out) uint8_t data_len; /* Less than 4 bytes -> TAG cannot be identified */ - if (length < 4) { + if (length < 4) + { dr_send_error("rxs:", "????"); - if (g_plat.log && g_log_enable) { + if (g_plat.log && g_log_enable) + { g_plat.log("[parser] too short (%u bytes) -> rxs:\r\n", length); } return false; @@ -185,18 +193,23 @@ static bool dr_parse_cmd(const uint8_t *buffer, uint8_t length, ParsedCmd *out) dr_copy_tag(buffer, out->tag); /* CRC verification */ - if (g_plat.crc_check) { - if (length < 7) { + if (g_plat.crc_check) + { + if (length < 7) + { dr_send_error("rxs:", out->tag); - if (g_plat.log && g_log_enable) { + if (g_plat.log && g_log_enable) + { g_plat.log("[parser] CRC enabled but too short (%u) -> rxs:\r\n", length); } return false; } - if (!dr_crc16_check_packet(buffer, length)) { + if (!dr_crc16_check_packet(buffer, length)) + { dr_send_error("rxc:", out->tag); - if (g_plat.log && g_log_enable) { + if (g_plat.log && g_log_enable) + { g_plat.log("[parser] CRC mismatch '%s' -> rxc:\r\n", out->tag); } return false; @@ -204,15 +217,18 @@ static bool dr_parse_cmd(const uint8_t *buffer, uint8_t length, ParsedCmd *out) data_len = (uint8_t)(length - 4 - 2); /* strip TAG and CRC */ } - else { + else + { data_len = (uint8_t)(length - 4); } - if (data_len > DR_MAX_DATA) { + if (data_len > DR_MAX_DATA) + { data_len = DR_MAX_DATA; } - if (data_len > 0) { + if (data_len > 0) + { memcpy(out->data, buffer + 4, data_len); } out->data_len = data_len; @@ -232,33 +248,43 @@ static int dr_cmd_dispatch(const ParsedCmd *cmd) uint16_t i; char tag_lower[5]; - if (m_cmd_table == NULL) { + if (m_cmd_table == NULL) + { dr_send_error("rxn:", cmd->tag); - if (g_plat.log) g_plat.log("[parser] table not initialized -> rxn:\n"); + if (g_plat.log) + { + g_plat.log("[parser] table not initialized -> rxn:\n"); + } return 0; } /* Case-insensitive matching */ - for (i = 0; i < 4 && cmd->tag[i]; i++) { - tag_lower[i] = (cmd->tag[i] >= 'A' && cmd->tag[i] <= 'Z') - ? (cmd->tag[i] + 32) : cmd->tag[i]; + for (i = 0; i < 4 && cmd->tag[i]; i++) + { + tag_lower[i] = (cmd->tag[i] >= 'A' && cmd->tag[i] <= 'Z') ? (cmd->tag[i] + 32) : cmd->tag[i]; } tag_lower[i] = '\0'; - for (i = 0; i < m_cmd_count; i++) { - if (dr_tag_eq(tag_lower, m_cmd_table[i].tag)) { + for (i = 0; i < m_cmd_count; i++) + { + if (dr_tag_eq(tag_lower, m_cmd_table[i].tag)) + { - if (!m_cmd_table[i].enabled) { + if (!m_cmd_table[i].enabled) + { dr_send_error("rxd:", cmd->tag); - if (g_plat.log && g_log_enable) { + if (g_plat.log && g_log_enable) + { g_plat.log("Command '%s' disabled -> rxd:\n", cmd->tag); } return 0; } - if (m_cmd_table[i].handler == NULL) { + if (m_cmd_table[i].handler == NULL) + { dr_send_error("rxn:", cmd->tag); - if (g_plat.log) { + if (g_plat.log) + { g_plat.log("[parser] NULL handler for '%s' -> rxn:\n", cmd->tag); } return 0; @@ -270,7 +296,8 @@ static int dr_cmd_dispatch(const ParsedCmd *cmd) /* TAG not found in table */ dr_send_error("rxx:", cmd->tag); - if (g_plat.log && g_log_enable) { + if (g_plat.log && g_log_enable) + { g_plat.log("Unknown TAG '%s' -> rxx:\n", cmd->tag); } return 0; @@ -299,7 +326,8 @@ int dr_cmd_parser(const uint8_t *buf, uint8_t len) { ParsedCmd cmd; - if (!dr_parse_cmd(buf, len, &cmd)) { + if (!dr_parse_cmd(buf, len, &cmd)) + { if (g_plat.log) g_plat.log("[PARSER] PARSE FAIL\r\n"); return -1; } diff --git a/project/ble_peripheral/ble_app_bladder_patch/hal/fds/fstorage.c b/project/ble_peripheral/ble_app_bladder_patch/hal/fds/fstorage.c index 2923269..0ff98af 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/hal/fds/fstorage.c +++ b/project/ble_peripheral/ble_app_bladder_patch/hal/fds/fstorage.c @@ -1,39 +1,21 @@ -/******************************************************************************* - TEST medi50 Dec 23 - ******************************************************************************/ - -/** - * @file fstorage.c - * @brief FDS(Flash Data Storage) 기반 설정 저장 모듈 +/*============================================================================== + * fstorage.c - FDS (Flash Data Storage) configuration module * - * 외부 EEPROM을 대체하여 nRF52840 내장 플래시에 장치 설정을 저장/로드한다. + * Stores and loads device configuration to/from nRF52840 internal flash, + * replacing external EEPROM. Coexists safely with the SoftDevice. * - * [레코드 관리] - * - CONFIG_FILE = 0x8010, CONFIG_REC_KEY = 0x7010 으로 단일 레코드를 관리한다. + * Record management: + * CONFIG_FILE = 0x8010, CONFIG_REC_KEY = 0x7010 (single record) * - * [config_data_t 구조체 필드] - * - magic(4B) : 포맷 확인용 매직 넘버 (0x20231226 -> 0x20260318(기본값 재생성을 위해 매직넘버 변경)) - * - hw_no(12B) : 하드웨어 버전 (기본값: "") - * - serial_no(12B) : 시리얼 번호 (기본값: "VB026030000") - * - passkey(6B) : BLE 페어링용 정적 패스키(기본값: "123456") - * - bond_data_delete(1B) : 본딩 데이터 삭제 플래그(기본값: 1) - * - reset_status(1B) : 리셋 상태 값(기본값: 99) - * - life_cycle(4B) : 장치 사용 횟수(기본값: 0) - * - piezo_freq_option : Piezo 송신 펄스 주파수(기본값: 1=2.1MHz) - * - piezo_cycles : Piezo 송신 펄스 사이클 수 (기본값: 7) - * - piezo_averaging : Piezo 채널당 반복 측정 횟수 (기본값: 5) - * - piezo_delay_us : Piezo 송신 펄스 출력 후 ADC 시작 시까지 대기시간(us) (기본값: 10) - * - piezo_num_samples : Piezo 측정 ADC 샘플 개수 (기본값: 100) + * Magic number validation: + * Data loaded from flash is checked against CONFIG_MAGIC_NUMBER_VALUE. + * Mismatch -> reinitialise with factory defaults. * - * [매직 넘버 검증] - * - 플래시에서 로드한 데이터의 magic 값이 0x20231226과 일치하는지 확인하여 - * 유효한 설정인지 판별한다. 불일치 시 기본값으로 초기화한다. - * - * [FDS 이벤트 후처리] - * - FDS 쓰기/업데이트 완료 이벤트 수신 후, 대기 중인 후처리를 수행한다: - * 전원 OFF (go_device_power_off), 슬립 진입 (go_sleep_mode_enter), - * 시스템 리셋 (go_NVIC_SystemReset) - */ + * FDS event post-processing: + * After a write/update completes, pending actions are executed: + * power-off (go_device_power_off), sleep (go_sleep_mode_enter), + * system reset (go_NVIC_SystemReset). + *============================================================================*/ #include "sdk_config.h" @@ -60,29 +42,28 @@ #include "debug_print.h" -/* FDS 레코드 식별자: 파일 ID와 레코드 키로 단일 설정 레코드를 관리 */ - +/* FDS record identifiers */ #define CONFIG_FILE (0x8010) #define CONFIG_REC_KEY (0x7010) -/* 매직 넘버: 플래시에 저장된 데이터가 유효한 설정인지 판별하는 데 사용 */ +/* Magic number used to validate stored data */ #define CONFIG_MAGIC_NUMBER_VALUE (0x20260319) -/* 전역 설정 데이터 구조체 인스턴스 */ +/* Global configuration instance */ config_data_t m_config; -/* FDS 쓰기 완료 후 수행할 후처리 플래그 (main.c에서 선언) */ -extern bool go_device_power_off; /* 전원 OFF 요청 */ -extern bool go_sleep_mode_enter; /* 슬립 모드 진입 요청 */ -extern bool go_NVIC_SystemReset; /* 시스템 리셋 요청 */ +/* Post-processing flags (declared in main.c) */ +extern bool go_device_power_off; +extern bool go_sleep_mode_enter; +extern bool go_NVIC_SystemReset; -/* FDS 초기화 완료 여부 플래그 (fds_evt_handler에서 true로 설정) */ +/* FDS initialisation complete flag (set in fds_evt_handler) */ static bool volatile m_fds_initialized; -/* FDS 쓰기 진행 중 플래그: true이면 쓰기 완료 대기 중 */ +/* FDS write-in-progress flag */ bool fds_flag_write = false; -/* FDS에 기록할 레코드 템플릿 (m_config 데이터를 가리킴) */ +/* FDS record template pointing to m_config */ static fds_record_t const m_dummy_record = { .file_id = CONFIG_FILE, @@ -93,60 +74,58 @@ static fds_record_t const m_dummy_record = }; -/* 기본 설정값 상수 */ -int8_t reset_status_dflt = 99; /* 리셋 상태 기본값 */ -uint8_t static_passkey_dflt[6] = DEFAULT_PASSKEY; /* BLE 패스키 기본값 */ +/* Default values */ +int8_t reset_status_dflt = 99; +uint8_t static_passkey_dflt[6] = DEFAULT_PASSKEY; -/** - * @brief 기본 설정값 초기화 +/*============================================================================== + * fds_default_value_set - Initialise m_config with factory defaults * - * m_config 구조체의 각 필드를 공장 초기값으로 설정한다. - * 플래시에 유효한 설정이 없거나 매직 넘버가 불일치할 때 호출된다.VB0HW0000 - */ + * Called when flash contains no valid configuration or the magic number + * does not match. + *============================================================================*/ void fds_default_value_set(void) { - /* HW Number - default from HARDWARE_VERSION */ - memset(m_config.hw_no, 0, 12); - memcpy(m_config.hw_no, HARDWARE_VERSION, strlen(HARDWARE_VERSION)); + /* HW number */ + memset(m_config.hw_no, 0, 12); + memcpy(m_config.hw_no, HARDWARE_VERSION, strlen(HARDWARE_VERSION)); - /* Serial Number - default from SERIAL_NUMBER */ - memset(m_config.serial_no, 0, 12); - memcpy(m_config.serial_no, SERIAL_NUMBER, strlen(SERIAL_NUMBER)); + /* Serial number */ + memset(m_config.serial_no, 0, 12); + memcpy(m_config.serial_no, SERIAL_NUMBER, strlen(SERIAL_NUMBER)); - /* Static Passkey */ - memcpy(m_config.static_passkey, static_passkey_dflt, 6); + /* Static passkey */ + memcpy(m_config.static_passkey, static_passkey_dflt, 6); - /* Bond data delete — 기본값: 삭제 요청 없음 (공장 초기화 경로에서만 1로 세팅) */ - m_config.bond_data_delete = 0; + /* Bond delete — default: no pending delete */ + m_config.bond_data_delete = 0; - /* Reset status */ - m_config.reset_status = reset_status_dflt; + /* Reset status */ + m_config.reset_status = reset_status_dflt; - /* Device usage count */ - m_config.life_cycle = 0; + /* Device usage count */ + m_config.life_cycle = 0; - /* 피에조 측정 파라미터 기본값 */ - m_config.piezo_freq_option = 1; /* 2.1MHz */ - m_config.piezo_delay_us = 10; /* 버스트 후 10us */ - m_config.piezo_num_samples = 100; /* 100샘플 */ - m_config.piezo_cycles = 7; /* 7사이클 */ - m_config.piezo_averaging = 3; /* 3회 평균화 */ + /* Piezo measurement parameter defaults */ + m_config.piezo_freq_option = 1; /* 2.1 MHz */ + m_config.piezo_delay_us = 10; /* 10 us after burst */ + m_config.piezo_num_samples = 100; /* 100 samples */ + m_config.piezo_cycles = 7; /* 7 cycles */ + m_config.piezo_averaging = 3; /* 3x averaging */ } -/* 마지막 FDS 이벤트 ID 저장 (디버깅용) */ +/* Last FDS event ID (for debugging) */ static volatile uint8_t fds_last_evt = 0xFF; -/** - * @brief FDS 이벤트 콜백 핸들러 +/*============================================================================== + * fds_evt_handler - FDS event callback * - * FDS 내부에서 비동기 작업이 완료될 때 호출된다. - * - FDS_EVT_INIT : FDS 초기화 완료 → m_fds_initialized 플래그 설정 - * - FDS_EVT_WRITE : 새 레코드 쓰기 완료 → fds_flag_write 해제 - * - FDS_EVT_UPDATE : 레코드 업데이트 완료 → fds_flag_write 해제 후 - * 대기 중인 전원 OFF / 슬립 진입 / 시스템 리셋 수행 - * - FDS_EVT_DEL_RECORD / FDS_EVT_DEL_FILE / FDS_EVT_GC : 현재 미사용 - */ + * FDS_EVT_INIT : initialisation complete -> set m_fds_initialized + * FDS_EVT_WRITE : new record written -> clear fds_flag_write + * FDS_EVT_UPDATE : record updated -> clear flag, then execute any + * pending power-off / sleep / system reset + *============================================================================*/ static void fds_evt_handler( fds_evt_t const *p_evt ) { fds_last_evt = p_evt->id; @@ -166,48 +145,46 @@ static void fds_evt_handler( fds_evt_t const *p_evt ) } break; - case FDS_EVT_UPDATE: - { - fds_flag_write = false; + case FDS_EVT_UPDATE: + { + fds_flag_write = false; - if(go_device_power_off == true) { - /* After flash writing completed, System Power Off */ + if(go_device_power_off == true) + { device_power_off(); } - if(go_sleep_mode_enter == true) { - /* After flash writing completed, System go to Sleep Mode */ + if(go_sleep_mode_enter == true) + { sleep_mode_enter(); } - if(go_NVIC_SystemReset == true) { - /* After flash writing completed, System Reset */ + if(go_NVIC_SystemReset == true) + { DBG_PRINTF("Off FDS_EVENT\r\n"); NVIC_SystemReset(); } - } - break; + } + break; - case FDS_EVT_DEL_RECORD: - break; + case FDS_EVT_DEL_RECORD: + break; - case FDS_EVT_DEL_FILE: - break; + case FDS_EVT_DEL_FILE: + break; - case FDS_EVT_GC: - break; + case FDS_EVT_GC: + break; - default: + default: break; } } -/** - * @brief FDS 초기화 완료 대기 +/*============================================================================== + * wait_for_fds_ready - Block until FDS initialisation completes * - * m_fds_initialized 플래그가 true가 될 때까지 대기한다. - * 최대 3초(3000ms) 타임아웃이 설정되어 있으며, - * 타임아웃 시 에러 로그를 출력하고 반환한다. - */ + * Times out after 3 seconds with an error log. + *============================================================================*/ static void wait_for_fds_ready( void ) { uint32_t timeout = 0; @@ -216,7 +193,7 @@ static void wait_for_fds_ready( void ) nrf_pwr_mgmt_run(); nrf_delay_ms(1); timeout++; - if (timeout > 3000) /* 3 second timeout */ + if (timeout > 3000) { DBG_PRINTF("[FDS] TIMEOUT!\r\n"); break; @@ -225,27 +202,25 @@ static void wait_for_fds_ready( void ) } -/** - * @brief FDS에서 설정 로드 +/*============================================================================== + * config_load - Load configuration from FDS * - * 플래시에서 CONFIG_FILE/CONFIG_REC_KEY 레코드를 검색하여 m_config에 로드한다. - * - * 동작 흐름: - * 1. fds_record_find()로 레코드 검색 (실패 시 최대 10회 재시도, 100ms 간격) - * 2. 레코드 발견 시: - * - fds_record_open()으로 열기 (CRC 에러 시 삭제 후 기본값으로 재생성) - * - 데이터를 m_config로 복사 - * - 매직 넘버 불일치 시 기존 레코드 삭제 → 기본값 설정 → 재기록 - * 3. 레코드 미발견 시: - * - 기본값으로 새 레코드 생성 후 다시 로드 - */ + * Flow: + * 1. Search for CONFIG_FILE / CONFIG_REC_KEY (retry up to 10x, 100 ms apart) + * 2. If found: + * - Open the record (on CRC error: delete and regenerate defaults) + * - Copy into m_config + * - Validate magic number; on mismatch: delete -> defaults -> rewrite + * 3. If not found: + * - Write factory defaults as a new record, then reload + *============================================================================*/ void config_load( void ) { ret_code_t rc; fds_record_desc_t desc = { 0 }; fds_find_token_t tok = { 0 }; uint8_t cfg_retry = 0; - uint32_t fds_wait_cnt = 0; // FDS write 대기 카운터 + uint32_t fds_wait_cnt = 0; cfg_load_start: memset((char *)&desc, 0, sizeof(desc)); @@ -264,10 +239,8 @@ void config_load( void ) if( rc == NRF_SUCCESS ) { - /* A config file is in flash. Let's update it. */ fds_flash_record_t config = { 0 }; - /* Open the record and read its contents. */ rc = fds_record_open(&desc, &config); if (rc != NRF_SUCCESS) { @@ -279,26 +252,22 @@ void config_load( void ) goto cfg_load_write_new; } - /* Copy the configuration from flash into m_config. */ memcpy(&m_config, config.p_data, sizeof(config_data_t)); - /* Close the record when done reading. */ rc = fds_record_close(&desc); APP_ERROR_CHECK(rc); DBG_PRINTF("[FDS] magic=0x%08X (expect 0x%08X)\r\n", m_config.magic_number, CONFIG_MAGIC_NUMBER_VALUE); if( m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE ) - { // first init + { DBG_PRINTF("[FDS] FORMAT! overwriting with defaults\r\n"); rc = fds_record_delete(&desc); APP_ERROR_CHECK(rc); m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE; - // default.... - fds_default_value_set(); + fds_default_value_set(); - /* Write the updated record to flash. */ rc = fds_record_update(&desc, &m_dummy_record); if( (rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH) ) { @@ -317,36 +286,34 @@ void config_load( void ) { cfg_load_write_new: DBG_PRINTF("[FDS] New - writing defaults\r\n"); - /* System config not found (or corrupt); write a new one. */ m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE; - // default.... - fds_default_value_set(); + fds_default_value_set(); fds_flag_write = true; rc = fds_record_write(&desc, &m_dummy_record); - + if (rc != NRF_SUCCESS) { DBG_PRINTF("[FDS] Write ERR=%u\r\n", rc); fds_flag_write = false; } - fds_wait_cnt = 0; // + fds_wait_cnt = 0; - while(fds_flag_write && fds_wait_cnt < 3000) // FDS write 최대 3초 타임아웃 + while(fds_flag_write && fds_wait_cnt < 3000) /* 3 second timeout */ { nrf_pwr_mgmt_run(); nrf_delay_ms(1); fds_wait_cnt++; } - if(fds_flag_write) // FDS write 타임아웃 시 플래그 강제 해제 + if(fds_flag_write) { DBG_PRINTF("[FDS] write TIMEOUT! forcing flag clear\r\n"); fds_flag_write = false; } - + if( (rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH) ) { rc = fds_gc(); @@ -363,21 +330,19 @@ void config_load( void ) } -/** - * @brief 현재 설정을 FDS에 저장 +/*============================================================================== + * config_save - Persist current configuration to FDS * - * m_config의 내용을 플래시에 기록한다. + * Flow: + * 1. If a previous FDS write is in progress, wait up to 3 seconds + * 2. Fix magic number if needed + * 3. If existing record found: fds_record_update() + * - On no-space: GC then retry + * 4. If not found: fds_record_write() (new record) * - * 동작 흐름: - * 1. 이전 FDS 쓰기 작업이 진행 중이면 최대 3초 대기 - * 2. 매직 넘버가 올바르지 않으면 보정 - * 3. 기존 레코드가 있으면 fds_record_update()로 갱신 - * - 플래시 공간 부족 시 GC(가비지 컬렉션) 수행 후 재시도 - * 4. 기존 레코드가 없으면 fds_record_write()로 새로 생성 - * - * 참고: 쓰기 완료는 fds_evt_handler()에서 비동기로 처리되며, - * 완료 후 전원 OFF/슬립/리셋 등의 후처리가 수행될 수 있다. - */ + * Write completion is asynchronous (fds_evt_handler). Post-processing + * (power-off / sleep / reset) may follow. + *============================================================================*/ void config_save( void ) { ret_code_t rc; @@ -443,7 +408,7 @@ void config_save( void ) fds_flag_write = true; rc = fds_record_write(&desc, &m_dummy_record); DBG_PRINTF("[CFG_SAVE] write rc=%u\r\n", rc); - + if( rc != NRF_SUCCESS ) { DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc); @@ -455,37 +420,33 @@ void config_save( void ) } -/** - * @brief config_load()의 래퍼 함수 - * - * 외부 모듈에서 설정 로드를 요청할 때 사용한다. - */ +/*============================================================================== + * fs_set_value - Wrapper for config_load() + *============================================================================*/ void fs_set_value(void) { - config_load(); + config_load(); } -/** - * @brief FDS 초기화 +/*============================================================================== + * fs_storage_init - Initialise FDS * - * 부팅 시 호출되어 FDS 모듈을 초기화한다. - * 1. fds_register()로 이벤트 핸들러 등록 - * 2. fds_init()로 FDS 초기화 시작 - * 3. wait_for_fds_ready()로 초기화 완료 대기 (최대 3초) - * 4. fds_stat()로 플래시 상태 확인 - */ + * Called once at boot: + * 1. Register event handler + * 2. Start FDS initialisation + * 3. Wait for completion (up to 3 seconds) + * 4. Verify flash stats + *============================================================================*/ void fs_storage_init(void) { ret_code_t rc; - /* Register first to receive an event when initialization is complete. */ rc = fds_register(fds_evt_handler); APP_ERROR_CHECK(rc); rc = fds_init(); APP_ERROR_CHECK(rc); - /* Wait for fds to initialize. */ wait_for_fds_ready(); fds_stat_t stat = { 0 }; diff --git a/project/ble_peripheral/ble_app_bladder_patch/hal/fds/fstorage.h b/project/ble_peripheral/ble_app_bladder_patch/hal/fds/fstorage.h index fa44ac3..ac321d2 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/hal/fds/fstorage.h +++ b/project/ble_peripheral/ble_app_bladder_patch/hal/fds/fstorage.h @@ -1,32 +1,25 @@ -/******************************************************************************* - * @file fstorage.h - * @author CandyPops Co. - * @version V1.0.0 - * @date 2022-09-05 - * @brief FDS(Flash Data Storage) 기반 설정 저장 모듈 인터페이스 - ******************************************************************************* +/*============================================================================== + * fstorage.h - FDS (Flash Data Storage) configuration module interface * - * [헤더 개요] - * nRF52840 내장 플래시에 디바이스 설정을 저장/로드하는 FDS 모듈의 공용 API. - * 외부 EEPROM을 대체하며, SoftDevice와 공존하여 플래시를 안전하게 관리한다. + * Stores and loads device configuration to/from the nRF52840 internal flash + * via the Nordic FDS library. Replaces external EEPROM and coexists safely + * with the SoftDevice. * - * [config_data_t 구조체] (45바이트, 패킹됨) - * magic_number(4B): 포맷 확인용 (0x20231226) - * hw_no(12B): 하드웨어 번호 - * serial_no(12B): 시리얼 번호 (BLE 디바이스 이름으로도 사용) - * static_passkey(6B): BLE 페어링 패스키 (숫자 6자리) - * bond_data_delete(1B): 본딩 삭제 플래그 - * reset_status(1B): 리셋 상태 코드 - * pd_adc_cnt(1B): ADC 샘플링 횟수 - * pd_delay_us(2B): PD 안정화 딜레이 (마이크로초) - * life_cycle(4B): 디바이스 사용 횟수 + * config_data_t (48 bytes, packed): + * magic_number (4B) : format validation (0x20231226) + * hw_no (12B): hardware version string + * serial_no (12B): serial number (also used as BLE device name) + * static_passkey(6B) : BLE pairing passkey (6-digit numeric) + * bond_data_delete(1B): bond-delete flag + * reset_status (1B) : reset cause code + * life_cycle (4B) : device usage count + * piezo_* (8B) : piezo measurement parameters * - * [주요 API] - * fs_storage_init(): FDS 초기화 (부트 시 1회) - * config_load(): FDS에서 설정 로드 (없으면 기본값 생성) - * config_save(): 현재 설정을 FDS에 저장 - * - ******************************************************************************/ + * API: + * fs_storage_init() : initialise FDS (once at boot) + * config_load() : load config from FDS (creates defaults if absent) + * config_save() : persist current config to FDS + *============================================================================*/ #ifndef IHP_FSTORAGE_H_ #define IHP_FSTORAGE_H_ @@ -36,21 +29,21 @@ #include "nordic_common.h" #include -/* ------------------------------------------------------------------------- -* 기본 버전 정보 (FDS 기본값 및 빈 필드 복구 시 사용) -* -* 하드웨어 식별 코드 -* - VBTHW0100 = 개발(시험)용 Ver 1.00 -* - VB0HW0100 = 양산용 Ver 1.00 -* -* Firmware 식별 코드 -* - VBTFW0100 = 개발(시험)용 Ver 1.00 -* - VB0FW0100 = 양산용 Ver 1.00 -* -* 시리얼 넘버 식별 코드 -* - VBT26030001 = 개발(시험)용 26년 3월 생산 1번 -* - VB026030001 = 양산용 26년 3월 생산 1번 -------------------------------------------------------------------------- */ +/*------------------------------------------------------------------------------ + * Default version identifiers (used for FDS defaults / empty field recovery) + * + * Hardware ID: + * VBTHW0100 = development / test Ver 1.00 + * VB0HW0100 = production Ver 1.00 + * + * Firmware ID: + * VBTFW0100 = development / test Ver 1.00 + * VB0FW0100 = production Ver 1.00 + * + * Serial number: + * VBT26030001 = dev/test, manufactured Mar 2026, unit #1 + * VB026030001 = production, Mar 2026, unit #1 + *----------------------------------------------------------------------------*/ #define HARDWARE_VERSION "VBTHW0100" #define SERIAL_NUMBER "VBT26030001" #define DEFAULT_PASSKEY "123456" @@ -58,21 +51,21 @@ #pragma pack(1) typedef struct { - uint32_t magic_number; /* 4B - 포맷 확인용 매직 넘버 */ - char hw_no[12]; /* 12B - HW Version */ - char serial_no[12]; /* 12B - Serial Number */ - uint8_t static_passkey[6]; /* 6B - BLE Passkey */ - uint8_t bond_data_delete; /* 1B - Bond delete flag */ - int8_t reset_status; /* 1B - Reset status */ - uint32_t life_cycle; /* 4B - Device usage count */ + uint32_t magic_number; /* 4B - format validation magic */ + char hw_no[12]; /* 12B - HW version */ + char serial_no[12]; /* 12B - serial number */ + uint8_t static_passkey[6]; /* 6B - BLE passkey */ + uint8_t bond_data_delete; /* 1B - bond delete flag */ + int8_t reset_status; /* 1B - reset status */ + uint32_t life_cycle; /* 4B - device usage count */ - /* Piezo 측정 파라미터 - 8B */ - uint8_t piezo_freq_option; /* 1B - Frequency : 송신 펄스 주파수 (0=1.8M, 1=2.1M, 2=2.0M, 3=1.7M) */ - uint8_t piezo_cycles; /* 1B - Burst Cycle : 송신 펄스 사이클 수 (3~7) */ - uint16_t piezo_averaging; /* 2B - 평균화 수 : 채널당 반복 측정 횟수 (1~10) */ - uint16_t piezo_delay_us; /* 2B - 대기 시간(Delay) : 송신 펄스 출력 후 ADC 시작 시까지 대기시간 (us) (0~30) */ - uint16_t piezo_num_samples; /* 2B - 측정 ADC 샘플 개수 (80~140) */ -} config_data_t; /* Total: 48 bytes - FDS에 저장하는 디바이스 설정 */ + /* Piezo measurement parameters - 8B */ + uint8_t piezo_freq_option; /* 1B - TX pulse frequency (0=1.8M, 1=2.1M, 2=2.0M, 3=1.7M) */ + uint8_t piezo_cycles; /* 1B - burst pulse cycle count (3..7) */ + uint16_t piezo_averaging; /* 2B - averages per channel (1..10) */ + uint16_t piezo_delay_us; /* 2B - delay from TX pulse to ADC start (us) (0..30) */ + uint16_t piezo_num_samples; /* 2B - ADC sample count (80..140) */ +} config_data_t; /* Total: 48 bytes */ extern config_data_t m_config; @@ -84,4 +77,3 @@ void fs_set_value(void); void fs_storage_init(void); #endif /* IHP_FSTORAGE_H_ */ - diff --git a/project/ble_peripheral/ble_app_bladder_patch/hal/i2c/i2c_manager.c b/project/ble_peripheral/ble_app_bladder_patch/hal/i2c/i2c_manager.c index a1aa349..868399a 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/hal/i2c/i2c_manager.c +++ b/project/ble_peripheral/ble_app_bladder_patch/hal/i2c/i2c_manager.c @@ -1,29 +1,18 @@ -/******************************************************************************* - * @file i2c_manager.c - * @brief Reliable HW↔SW I2C Switching Logic (with Mode Set Logging) - ******************************************************************************* +/*============================================================================== + * i2c_manager.c - HW / SW I2C mutex switching logic * - * [모듈 개요] - * I2C 버스의 HW(하드웨어 TWI) / SW(소프트웨어 비트뱅) 모드 전환을 관리하는 모듈. + * Manages mutually-exclusive HW (TWI peripheral) and SW (bit-bang) I2C modes. * - * - HW I2C: nRF52840 내장 TWI 하드웨어 주변장치를 사용 (ICM42670P IMU 센서 통신용, 400kHz) - * - SW I2C: GPIO 비트뱅 방식의 소프트웨어 I2C (현재 사용하지 않는 레거시 코드) + * HW I2C : nRF52840 TWI hardware, 400 kHz Fast Mode (ICM42670P IMU) + * SW I2C : GPIO bit-bang (legacy, currently unused) * - * [핀 설정] + * Pins: * SCL = P1.14 (ICM42670_I2C_SCL_PIN) * SDA = P1.15 (ICM42670_I2C_SDA_PIN) * - * [주요 함수] - * hw_i2c_init_once() : SW→HW 전환 또는 HW 초기화 (이미 HW 모드면 중복 초기화 방지) - * sw_i2c_init_once() : HW→SW 전환 (TWI 해제 후 SW 모드 진입, 레거시) - * i2c_reset_state() : 모든 I2C 모드 플래그를 초기화 (HW/SW 모두 false) - * - * [동작 원리] - * HW_I2C_FRQ, SW_I2C_FRQ 두 개의 bool 플래그로 현재 모드를 추적하며, - * 한 번에 하나의 모드만 활성화되도록 상호 배제(mutex) 방식으로 관리한다. - * 모드 전환 시 기존 모드의 리소스를 먼저 해제한 후 새 모드를 초기화한다. - * - ******************************************************************************/ + * The two bool flags HW_I2C_FRQ and SW_I2C_FRQ track the current mode. + * Switching releases the old mode's resources before initialising the new one. + *============================================================================*/ #include "i2c_manager.h" #include "debug_print.h" @@ -34,137 +23,101 @@ #include "boards.h" #include "system_interface.h" -/* 현재 I2C 모드 상태 플래그 (true = 해당 모드 활성화) */ -bool HW_I2C_FRQ = true; /* HW TWI 모드 활성 여부 (기본값: true, 초기 상태는 HW) */ -bool SW_I2C_FRQ = false; /* SW 비트뱅 모드 활성 여부 */ +/* Current I2C mode flags */ +bool HW_I2C_FRQ = true; +bool SW_I2C_FRQ = false; -/* TWI 인스턴스 번호 (nRF52840은 TWI0, TWI1 두 개 지원) */ +/* TWI instance (nRF52840 supports TWI0 and TWI1) */ #define TWI_INSTANCE 0 - -/* TWI (I2C) 하드웨어 인스턴스 : IMU 드라이버에서 사용 - jhChun 26.03.16 */ const nrfx_twi_t m_twi = NRFX_TWI_INSTANCE(TWI_INSTANCE); -/* TWI (I2C) 해제 - jhChun 26.03.16 */ -/* TWI 하드웨어를 비활성화하고 초기화 해제하여 GPIO 핀을 반환한다 */ -static void twi_uninitialize(void){ - nrfx_twi_disable(&m_twi); /* TWI 주변장치 비활성화 */ - nrfx_twi_uninit(&m_twi); /* TWI 초기화 해제 (핀 리소스 반환) */ +/* Disable and uninitialise the TWI peripheral, releasing GPIO pins. */ +static void twi_uninitialize(void) +{ + nrfx_twi_disable(&m_twi); + nrfx_twi_uninit(&m_twi); } -/* TWI (I2C) 하드웨어 초기화 (SCL/SDA핀, 400kHz) - jhChun 26.03.16 */ -/* SCL, SDA 핀을 설정하고 400kHz Fast Mode로 TWI를 초기화 및 활성화한다 */ +/* Initialise the TWI peripheral (SCL/SDA pins, 400 kHz, blocking mode). */ static void twi_initialize(void){ ret_code_t err_code; - /* TWI 설정 구조체: 핀 번호, 클럭 속도, 인터럽트 우선순위 지정 */ const nrfx_twi_config_t twi_config = { - .scl = ICM42670_I2C_SCL_PIN, /* SCL 핀 (P1.14) */ - .sda = ICM42670_I2C_SDA_PIN, /* SDA 핀 (P1.15) */ - .frequency = NRF_TWI_FREQ_400K, /* 400kHz Fast Mode */ - .interrupt_priority = APP_IRQ_PRIORITY_HIGH, /* 높은 인터럽트 우선순위 */ + .scl = ICM42670_I2C_SCL_PIN, + .sda = ICM42670_I2C_SDA_PIN, + .frequency = NRF_TWI_FREQ_400K, + .interrupt_priority = APP_IRQ_PRIORITY_HIGH, }; - /* TWI 초기화 (이벤트 핸들러 NULL = 블로킹 모드) */ err_code = nrfx_twi_init(&m_twi, &twi_config, NULL, NULL); APP_ERROR_CHECK(err_code); - nrfx_twi_enable(&m_twi); /* TWI 주변장치 활성화 → I2C 통신 가능 */ + nrfx_twi_enable(&m_twi); } -/* -------------------------------------------------------------------------- */ -/* HW (TWI) 초기화 */ -/* -------------------------------------------------------------------------- */ -/* - * HW I2C 모드로 전환하거나 초기화하는 함수. - * - SW 모드가 활성화되어 있으면 SW 플래그를 해제하고 HW로 전환 - * - 이미 HW 모드이면 중복 초기화를 방지하여 바로 리턴 - * - 최초 HW 초기화 시 twi_initialize()를 호출하여 TWI 하드웨어 설정 - */ +/*============================================================================== + * hw_i2c_init_once - Switch to or initialise HW TWI mode + * + * If SW mode is active, its flag is cleared first. + * If HW mode is already active, returns immediately (no re-init). + *============================================================================*/ void hw_i2c_init_once(void) { - // SW 모드일 경우 강제 해제 후 HW 전환 if (SW_I2C_FRQ) { - //DBG_PRINTF("[I2C]SW→HW\r\n"); - - // SW 리소스 해제 (필요 시 추가) SW_I2C_FRQ = false; - nrf_delay_ms(2); /* 모드 전환 안정화 대기 (2ms) */ + nrf_delay_ms(2); /* mode-switch settling */ } - /* 이미 HW 모드가 활성화되어 있으면 중복 초기화 방지를 위해 즉시 리턴 */ - // 이미 HW면 스킵 if (HW_I2C_FRQ) { - // DBG_PRINTF("[I2C] HW I2C set\r\n"); return; } - /* HW TWI 하드웨어 초기화 수행 (SCL/SDA 핀 설정, 400kHz, 활성화) */ - // 실제 HW 초기화 twi_initialize(); - nrf_delay_ms(2); /* 초기화 후 안정화 대기 (2ms) */ + nrf_delay_ms(2); - /* 모드 플래그 갱신: HW 활성, SW 비활성 */ HW_I2C_FRQ = true; SW_I2C_FRQ = false; - - // DBG_PRINTF("[I2C] HW I2C Mode set!\r\n"); } -/* -------------------------------------------------------------------------- */ -/* SW (Port Bang-Bang) 초기화 */ -/* -------------------------------------------------------------------------- */ -/* - * SW I2C(비트뱅) 모드로 전환하는 함수. (현재 레거시, 사용하지 않음) - * - HW 모드가 활성화되어 있으면 TWI를 해제하고 SW로 전환 - * - 이미 SW 모드이면 중복 초기화를 방지하여 바로 리턴 - * - power_control.c의 power_loop()에서 Step 0에서 호출됨 - */ +/*============================================================================== + * sw_i2c_init_once - Switch to SW bit-bang mode (legacy, unused) + * + * If HW mode is active, TWI is released first. + * If SW mode is already active, returns immediately. + *============================================================================*/ void sw_i2c_init_once(void) { - // HW 모드일 경우 강제 해제 후 SW 전환 if (HW_I2C_FRQ) { - //DBG_PRINTF("[I2C]HW→SW\r\n"); - - nrfx_twi_disable(&m_twi); /* TWI 비활성화 */ - nrfx_twi_uninit(&m_twi); /* TWI 초기화 해제 */ - nrf_delay_ms(2); /* 모드 전환 안정화 대기 (2ms) */ - + nrfx_twi_disable(&m_twi); + nrfx_twi_uninit(&m_twi); + nrf_delay_ms(2); HW_I2C_FRQ = false; } - // 이미 SW 모드면 재실행 금지 if (SW_I2C_FRQ) { - // DBG_PRINTF("[I2C] SWI2C already initialized\r\n"); return; } - /* TWI 라인 완전 해제 후 SW 비트뱅 모드 진입 */ - // 실제 SW 초기화 - twi_uninitialize(); // TWI 라인 해제 - nrf_delay_ms(1); /* 해제 후 안정화 대기 (1ms) */ + twi_uninitialize(); + nrf_delay_ms(1); - /* 모드 플래그 갱신: SW 활성, HW 비활성 */ SW_I2C_FRQ = true; HW_I2C_FRQ = false; - - // DBG_PRINTF("[I2C] SW I2C Mode set!\r\n"); } -/* -------------------------------------------------------------------------- */ -/* 전체 리셋 */ -/* -------------------------------------------------------------------------- */ -/* - * 모든 I2C 모드 플래그를 초기화하는 함수. - * HW/SW 모두 비활성 상태로 만들어, 다음 init 호출 시 강제로 재초기화되도록 한다. - * 주로 시스템 리셋이나 에러 복구 시 사용. - */ +/*============================================================================== + * i2c_reset_state - Clear all mode flags + * + * Forces re-initialisation on the next init call. Used for system reset + * or error recovery. + *============================================================================*/ void i2c_reset_state(void) { - HW_I2C_FRQ = false; /* HW 모드 플래그 초기화 */ - SW_I2C_FRQ = false; /* SW 모드 플래그 초기화 */ + HW_I2C_FRQ = false; + SW_I2C_FRQ = false; DBG_PRINTF("Flags reset\r\n"); } diff --git a/project/ble_peripheral/ble_app_bladder_patch/hal/i2c/i2c_manager.h b/project/ble_peripheral/ble_app_bladder_patch/hal/i2c/i2c_manager.h index b08af04..1d87004 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/hal/i2c/i2c_manager.h +++ b/project/ble_peripheral/ble_app_bladder_patch/hal/i2c/i2c_manager.h @@ -1,50 +1,31 @@ -/******************************************************************************* - * @file i2c_manager.h - * @brief Common header for HW/SW I2C mutex control - ******************************************************************************* +/*============================================================================== + * i2c_manager.h - HW / SW I2C mutex control * - * [헤더 개요] - * I2C 버스 HW/SW 모드 전환 관리자의 공용 인터페이스 헤더. + * Manages the mutually-exclusive HW TWI and SW bit-bang I2C modes. + * Only one mode may be active at a time. * - * - HW_I2C_FRQ: HW TWI 모드 활성 여부 (true = HW I2C 사용 중) - * - SW_I2C_FRQ: SW 비트뱅 모드 활성 여부 (true = SW I2C 사용 중) - * - * 두 플래그는 상호 배제적으로 동작하며, 동시에 true가 되지 않도록 관리된다. - * - ******************************************************************************/ + * Flags: + * HW_I2C_FRQ : true when HW TWI mode is active + * SW_I2C_FRQ : true when SW bit-bang mode is active + *============================================================================*/ #ifndef __I2C_MANAGER_H__ #define __I2C_MANAGER_H__ #include #include "app_error.h" -/* I2C 모드 상태 플래그 (외부 참조용) */ -extern bool HW_I2C_FRQ; /* HW TWI 모드 활성 여부 */ -extern bool SW_I2C_FRQ; /* SW 비트뱅 모드 활성 여부 */ +extern bool HW_I2C_FRQ; +extern bool SW_I2C_FRQ; -/** - * @brief HW I2C(TWI) 모드 초기화 (중복 초기화 방지) - * - * SW 모드가 활성화되어 있으면 해제 후 HW로 전환한다. - * 이미 HW 모드이면 아무 동작 없이 리턴한다. - * ICM42670P IMU 센서 통신 전에 호출하여 HW I2C를 준비한다. - */ +/* Initialise HW I2C (TWI) mode. If SW mode is active it is released first. + * No-op if HW mode is already active. Call before ICM42670P IMU access. */ void hw_i2c_init_once(void); -/** - * @brief SW I2C(비트뱅) 모드 초기화 (레거시, 현재 미사용) - * - * HW 모드가 활성화되어 있으면 TWI를 해제한 후 SW로 전환한다. - * 이미 SW 모드이면 아무 동작 없이 리턴한다. - */ +/* Initialise SW I2C (bit-bang) mode (legacy, currently unused). + * If HW mode is active it is released first. */ void sw_i2c_init_once(void); -/** - * @brief I2C 모드 플래그 전체 초기화 - * - * HW_I2C_FRQ, SW_I2C_FRQ를 모두 false로 리셋한다. - * 다음 init 호출 시 강제로 재초기화가 수행된다. - */ +/* Reset both mode flags to false, forcing re-initialisation on next call. */ void i2c_reset_state(void); #endif diff --git a/project/ble_peripheral/ble_app_bladder_patch/main.c b/project/ble_peripheral/ble_app_bladder_patch/main.c index a38ea54..afe1659 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/main.c +++ b/project/ble_peripheral/ble_app_bladder_patch/main.c @@ -5,68 +5,68 @@ * @date 2025-12-09 * @author Charles KWON * - * [시스템 아키텍처 개요] - * VesiScan BASIC은 nRF52840(ARM Cortex-M4F, 64MHz) 기반 BLE 방광 모니터링 패치 디바이스이다. - * SoftDevice S140(BLE 5.0)을 사용하며, 스마트폰 앱과 BLE NUS(Nordic UART Service) 프로토콜로 바이너리 통신한다. + * [System Architecture Overview] + * VesiScan BASIC is an nRF52840 (ARM Cortex-M4F, 64MHz) based BLE bladder monitoring patch device. + * Uses SoftDevice S140 (BLE 5.0) and communicates with the smartphone app via BLE NUS (Nordic UART Service) binary protocol. * - * [하드웨어 구성] + * [Hardware Configuration] * - MCU: nRF52840 (SoftDevice S140 v7.x) - * - IMU: ICM42670P 6축 가속도/자이로 (I2C, 0x68) - * - 온도: TMP235-Q1 아날로그 센서 (SAADC AIN3) - * - 배터리: 분압 회로 (SAADC AIN2, 1/3 프리스케일) - * - 전원: POWER_HOLD(P0.8) 자가유지 회로, POWER_BUTTON(P1.8) 입력 + * - IMU: ICM42670P 6-axis accel/gyro (I2C, 0x68) + * - Temperature: TMP235-Q1 analog sensor (SAADC AIN3) + * - Battery: voltage divider (SAADC AIN2, 1/3 prescale) + * - Power: POWER_HOLD(P0.8) self-latch circuit, POWER_BUTTON(P1.8) input * - * [소프트웨어 부트 시퀀스 — main() 함수] - * Phase 1: 하드웨어 기본 (전원유지, 로그, GPIO, 타이머, 설정, 버튼/LED) - * Phase 2: BLE 스택 (SoftDevice, 전원관리, DC-DC) - * Phase 3: FDS + 설정 로드 - * Phase 4: BLE 프로토콜 (GAP, GATT, NUS, Advertising, 보안) - * Phase 5: 애플리케이션 (파서, 피에조) - * Boot 완료 → 전원 버튼 상태머신(main_s) → idle 루프 + * [Software Boot Sequence - main()] + * Phase 1: Basic hardware (power latch, log, GPIO, timers, config, buttons/LEDs) + * Phase 2: BLE stack (SoftDevice, power mgmt, DC-DC) + * Phase 3: FDS + config load + * Phase 4: BLE protocol (GAP, GATT, NUS, Advertising, security) + * Phase 5: Application (parser, piezo) + * Boot complete -> power button state machine (main_s) -> idle loop * - * [전원 버튼 상태머신 — main_s() 콜백, 5ms 간격] - * - 짧은 눌림 (<1.5초, cnt_s<150): 전원 OFF - * - 중간 눌림 (1.5초~10초, cnt_s≥150): 부팅 시퀀스 시작 (센서+BLE 광고) - * - 긴 눌림 (>10초, cnt_s>1000): 공장 초기화 (패스키 리셋 + 전원 OFF) + * [Power Button State Machine - main_s() callback, 5ms interval] + * - Short press (<1.5s, cnt_s<150): power OFF + * - Medium press (1.5s~10s, cnt_s>=150): start boot sequence (sensor+BLE advertising) + * - Long press (>10s, cnt_s>1000): factory reset (passkey reset + power OFF) * - * [이벤트 흐름] - * 1. 앱에서 BLE NUS로 명령 수신 → nus_data_handler → dr_cmd_parser - * 2. UART에서 명령 수신 → uart_event_handle → dr_cmd_parser - * 3. 명령 파서(cmd_parse.c)가 태그 기반 디스패치 (sta?, ssn?, ssp? 등) - * 4. 센서 데이터를 format_data() 계열 함수로 바이너리 패킷 생성 - * 5. dr_binary_tx_safe()로 CRC16 추가 후 BLE 전송 (최대 100회 재시도) + * [Event Flow] + * 1. Receive command from app via BLE NUS -> nus_data_handler -> dr_cmd_parser + * 2. Receive command from UART -> uart_event_handle -> dr_cmd_parser + * 3. Command parser (cmd_parse.c) dispatches by tag (sta?, ssn?, ssp?, etc.) + * 4. Build binary packets from sensor data via format_data() family + * 5. Transmit over BLE with CRC16 via dr_binary_tx_safe() (max 100 retries) * - * [빌드 모드] - * BLE_DEV_MODE = 1 : 보안 없음 (빠른 페어링, 개발용) - * BLE_DEV_MODE = 0 : 정적 패스키 + MITM 보호 (양산용) + * [Build Modes] + * BLE_DEV_MODE = 1 : No security (fast pairing, development) + * BLE_DEV_MODE = 0 : Static passkey + MITM protection (production) ******************************************************************************/ /*============================================================================== - * 인클루드 (INCLUDES) + * INCLUDES *============================================================================*/ #include #include #include "nordic_common.h" #include "nrf.h" -#include "ble_hci.h" /* BLE HCI 에러 코드 정의 */ -#include "ble_advdata.h" /* BLE 광고 데이터 구조체 */ -#include "ble_advertising.h" /* BLE 광고 모듈 */ -#include "ble_srv_common.h" /* BLE 서비스 공통 유틸리티 */ -#include "ble_conn_params.h" /* BLE 연결 파라미터 협상 */ -#include "nrf_sdh.h" /* SoftDevice 핸들러 (BLE 스택 관리) */ -#include "nrf_sdh_soc.h" /* SoftDevice SoC 이벤트 핸들러 */ -#include "nrf_sdh_ble.h" /* SoftDevice BLE 이벤트 핸들러 */ -#include "nrf_ble_gatt.h" /* BLE GATT 모듈 (MTU 협상) */ -#include "nrf_ble_qwr.h" /* BLE Queued Write 모듈 (긴 특성값 쓰기) */ -#include "app_timer.h" /* 앱 타이머 (RTC1 기반, 소프트 타이머) */ -#include "ble_nus.h" /* Nordic UART Service (BLE 양방향 데이터 전송) */ -#include "app_uart.h" /* 물리 UART 드라이버 (디버그/공장 테스트용) */ -#include "app_util_platform.h" /* 인터럽트 우선순위 매크로 */ -#include "bsp_btn_ble.h" /* BSP 버튼-BLE 연동 모듈 */ -#include "nrf_pwr_mgmt.h" /* 전원 관리 (idle 시 WFE/슬립) */ -#include "nrf_delay.h" /* 블로킹 딜레이 (us/ms) */ +#include "ble_hci.h" /* BLE HCI error code definitions */ +#include "ble_advdata.h" /* BLE advertising data structures */ +#include "ble_advertising.h" /* BLE advertising module */ +#include "ble_srv_common.h" /* BLE service common utilities */ +#include "ble_conn_params.h" /* BLE connection parameter negotiation */ +#include "nrf_sdh.h" /* SoftDevice handler (BLE stack management) */ +#include "nrf_sdh_soc.h" /* SoftDevice SoC event handler */ +#include "nrf_sdh_ble.h" /* SoftDevice BLE event handler */ +#include "nrf_ble_gatt.h" /* BLE GATT module (MTU negotiation) */ +#include "nrf_ble_qwr.h" /* BLE Queued Write module (long characteristic writes) */ +#include "app_timer.h" /* App timer (RTC1-based soft timer) */ +#include "ble_nus.h" /* Nordic UART Service (BLE bidirectional data) */ +#include "app_uart.h" /* Physical UART driver (debug/factory test) */ +#include "app_util_platform.h" /* Interrupt priority macros */ +#include "bsp_btn_ble.h" /* BSP button-BLE integration module */ +#include "nrf_pwr_mgmt.h" /* Power management (WFE/sleep when idle) */ +#include "nrf_delay.h" /* Blocking delay (us/ms) */ #include "math.h" -#include "crc16.h" /* CRC16 계산 (BLE 패킷 무결성 검증) */ +#include "crc16.h" /* CRC16 computation (BLE packet integrity) */ #if defined (UART_PRESENT) #include "nrf_uart.h" @@ -89,193 +89,193 @@ #include "nrf_svci_async_handler.h" #endif -/* ── 애플리케이션 모듈 헤더 ── */ -#include "system_interface.h" /* 시스템 인터페이스 (IMU 센서 I2C 통신) */ -#include "main.h" /* 메인 헤더 (전역 구조체, 상수, 외부 함수 선언) */ -#include "app_raw_main.h" /* 센서 원시 데이터 처리 모듈 */ -#include "main_timer.h" /* 메인 이벤트 루프 타이머 (10ms 주기) */ -#include "power_control.h" /* 전원 시퀀스 제어 (ON/OFF/슬립) */ -#include "tmp235_q1.h" /* TMP235-Q1 온도 센서 드라이버 */ -#include "fds.h" /* Flash Data Storage (비휘발성 설정 저장) */ -#include "battery_saadc.h" /* 배터리 전압 측정 (SAADC) */ +/* -- Application module headers -- */ +#include "system_interface.h" /* System interface (IMU sensor I2C communication) */ +#include "main.h" /* Main header (global structs, constants, extern declarations) */ +#include "app_raw_main.h" /* Sensor raw data processing module */ +#include "main_timer.h" /* Main event loop timer (10ms period) */ +#include "power_control.h" /* Power sequence control (ON/OFF/sleep) */ +#include "tmp235_q1.h" /* TMP235-Q1 temperature sensor driver */ +#include "fds.h" /* Flash Data Storage (non-volatile config) */ +#include "battery_saadc.h" /* Battery voltage measurement (SAADC) */ -/* ── BLE 보안 관련 헤더 (FEATURE_SECURE_CONNECTION 활성 시) ── */ +/* -- BLE security headers (when FEATURE_SECURE_CONNECTION enabled) -- */ #if FEATURE_SECURE_CONNECTION -#include "peer_manager.h" /* Peer Manager (본딩/페어링 관리) */ -#include "peer_manager_handler.h" /* Peer Manager 기본 이벤트 핸들러 */ -#include "nrf_ble_lesc.h" /* BLE LESC(LE Secure Connections) 지원 */ -#include "ble_quick_security.h" /* 간편 보안 설정 래퍼 */ -#include "i2c_manager.h" /* I2C 버스 관리자 (센서 통신) */ +#include "peer_manager.h" /* Peer Manager (bonding/pairing management) */ +#include "peer_manager_handler.h" /* Peer Manager default event handler */ +#include "nrf_ble_lesc.h" /* BLE LESC (LE Secure Connections) support */ +#include "ble_quick_security.h" /* Quick security setup wrapper */ +#include "i2c_manager.h" /* I2C bus manager (sensor communication) */ #endif -/* ── 암호화/명령 파서/디버그 ── */ -#include "nrf_crypto.h" /* nRF 암호화 라이브러리 (AES 등) */ -#include "parser.h" /* 새 바이너리 명령 파서 (dr_cmd_parser) */ -#include "cmd_table.h" /* 명령 테이블 등록 (cmd_table_init) */ -#include "debug_print.h" /* 디버그 출력 매크로 (DBG_PRINTF) */ -#include "fstorage.h" /* Flash Storage 래퍼 (FDS 초기화/저장/로드) */ -#include "dr_piezo.h" /* 피에조 초음파 드라이버 */ -#include "led_control.h" /* LED 직접 제어 드라이버 (BSP 미사용) */ +/* -- Crypto / command parser / debug -- */ +#include "nrf_crypto.h" /* nRF crypto library (AES, etc.) */ +#include "parser.h" /* Binary command parser (dr_cmd_parser) */ +#include "cmd_table.h" /* Command table registration (cmd_table_init) */ +#include "debug_print.h" /* Debug output macro (DBG_PRINTF) */ +#include "fstorage.h" /* Flash Storage wrapper (FDS init/save/load) */ +#include "dr_piezo.h" /* Piezo ultrasound driver */ +#include "led_control.h" /* LED direct control driver (not using BSP) */ /*============================================================================== - * 빌드 설정 + * Build Configuration *============================================================================*/ -#define BLE_DEV_MODE 0 /* 1: 개발 모드 (보안 없음), 0: 양산 모드 (패스키 필수) */ +#define BLE_DEV_MODE 0 /* 1: Dev mode (no security), 0: Production mode (passkey required) */ /*============================================================================== - * 하드웨어 핀 정의 + * Hardware Pin Definitions *============================================================================*/ -#define POWER_HOLD NRF_GPIO_PIN_MAP(0,8) /* 전원 자가유지 핀 (HIGH=전원 유지) */ -#define POWER_BUTTON NRF_GPIO_PIN_MAP(1,8) /* 전원 버튼 입력 핀 (LOW=눌림) */ +#define POWER_HOLD NRF_GPIO_PIN_MAP(0,8) /* Power self-latch pin (HIGH=power maintained) */ +#define POWER_BUTTON NRF_GPIO_PIN_MAP(1,8) /* Power button input pin (LOW=pressed) */ /*============================================================================== - * BLE 설정 + * BLE Configuration *============================================================================*/ -#define APP_BLE_CONN_CFG_TAG 1 /* SoftDevice BLE 설정 태그 */ -#define NUS_SERVICE_UUID_TYPE BLE_UUID_TYPE_VENDOR_BEGIN /* NUS UUID 타입 (벤더 특정) */ -#define APP_BLE_OBSERVER_PRIO 3 /* BLE 이벤트 옵저버 우선순위 */ -#define APP_ADV_INTERVAL 64 /* 광고 간격: 64 x 0.625ms = 40ms */ +#define APP_BLE_CONN_CFG_TAG 1 /* SoftDevice BLE config tag */ +#define NUS_SERVICE_UUID_TYPE BLE_UUID_TYPE_VENDOR_BEGIN /* NUS UUID type (vendor-specific) */ +#define APP_BLE_OBSERVER_PRIO 3 /* BLE event observer priority */ +#define APP_ADV_INTERVAL 64 /* Advertising interval: 64 x 0.625ms = 40ms */ -#define APP_ADV_DURATION_NORMAL 60000 /* 일반 disconnect 시 광고 지속시간: 60000 x 10ms = 10분 */ -#define APP_ADV_DURATION_UNLIMITED 0 /* 의도치 않은 disconnect 시: 무한 광고 */ -#define APP_ADV_DURATION APP_ADV_DURATION_NORMAL /* 기본 광고 지속시간 (disconnect reason에 따라 런타임 변경) */ +#define APP_ADV_DURATION_NORMAL 60000 /* Normal disconnect adv duration: 60000 x 10ms = 10min */ +#define APP_ADV_DURATION_UNLIMITED 0 /* Unintended disconnect: unlimited advertising */ +#define APP_ADV_DURATION APP_ADV_DURATION_NORMAL /* Default adv duration (changed at runtime by disconnect reason) */ -#define MIN_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* 최소 연결 간격: 30ms */ -#define MAX_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* 최대 연결 간격: 30ms */ -#define SLAVE_LATENCY 0 /* 슬레이브 지연: 0 (매 연결 이벤트마다 응답) */ -#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /* 연결 감독 타임아웃: 4초 */ -#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /* 첫 파라미터 갱신 요청까지 5초 대기 */ -#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /* 이후 갱신 요청 간격: 30초 */ -#define MAX_CONN_PARAMS_UPDATE_COUNT 3 /* 최대 파라미터 갱신 시도 횟수 */ +#define MIN_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* Min connection interval: 30ms */ +#define MAX_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* Max connection interval: 30ms */ +#define SLAVE_LATENCY 0 /* Slave latency: 0 (respond every connection event) */ +#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /* Connection supervision timeout: 4s */ +#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /* Wait 5s before first param update request */ +#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /* Subsequent param update interval: 30s */ +#define MAX_CONN_PARAMS_UPDATE_COUNT 3 /* Max param update attempts */ -/* ── BLE 보안 파라미터 (FEATURE_SECURE_CONNECTION 활성 시) ── - * 본딩(BOND=1): 페어링 정보를 Flash에 저장하여 재연결 시 재사용 - * MITM 보호(MITM=1): 중간자 공격 방지 (패스키 인증 필수) - * LESC: 정적 패스키 모드에서는 비활성(0), 동적 패스키 모드에서는 활성(1) - * IO 능력: DISPLAY_ONLY → 디바이스가 패스키를 표시, 사용자가 앱에 입력 +/* -- BLE security parameters (when FEATURE_SECURE_CONNECTION enabled) -- + * Bonding (BOND=1): store pairing info in flash for reconnection + * MITM protection (MITM=1): prevent man-in-the-middle attacks (passkey auth required) + * LESC: disabled(0) in static passkey mode, enabled(1) in dynamic passkey mode + * IO capabilities: DISPLAY_ONLY -> device displays passkey, user enters in app */ #if FEATURE_SECURE_CONNECTION -#define LESC_DEBUG_MODE 0 /* LESC 디버그 모드 비활성 */ -#define SEC_PARAM_BOND 1 /* 본딩 활성화 (페어링 정보 저장) */ -#define SEC_PARAM_MITM 1 /* MITM 보호 활성화 */ +#define LESC_DEBUG_MODE 0 /* LESC debug mode disabled */ +#define SEC_PARAM_BOND 1 /* Bonding enabled (store pairing info) */ +#define SEC_PARAM_MITM 1 /* MITM protection enabled */ #if FEATURE_STATIC_PASSKEY -#define SEC_PARAM_LESC 0 /* 정적 패스키 모드: LESC 비활성 */ +#define SEC_PARAM_LESC 0 /* Static passkey mode: LESC disabled */ #else -#define SEC_PARAM_LESC 1 /* 동적 패스키 모드: LESC 활성 */ +#define SEC_PARAM_LESC 1 /* Dynamic passkey mode: LESC enabled */ #endif -#define SEC_PARAM_KEYPRESS 0 /* 키 입력 알림 비활성 */ -#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_DISPLAY_ONLY /* 디스플레이만 가능 */ -#define SEC_PARAM_OOB 0 /* OOB(Out-of-Band) 데이터 없음 */ -#define SEC_PARAM_MIN_KEY_SIZE 7 /* 최소 암호화 키 크기 (바이트) */ -#define SEC_PARAM_MAX_KEY_SIZE 16 /* 최대 암호화 키 크기 (바이트) */ -#define PASSKEY_TXT_LENGTH 8 /* 패스키 텍스트 버퍼 길이 */ +#define SEC_PARAM_KEYPRESS 0 /* Keypress notification disabled */ +#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_DISPLAY_ONLY /* Display only */ +#define SEC_PARAM_OOB 0 /* No OOB (Out-of-Band) data */ +#define SEC_PARAM_MIN_KEY_SIZE 7 /* Min encryption key size (bytes) */ +#define SEC_PARAM_MAX_KEY_SIZE 16 /* Max encryption key size (bytes) */ +#define PASSKEY_TXT_LENGTH 8 /* Passkey text buffer length */ #endif /*============================================================================== - * 시스템 상수 + * System Constants *============================================================================*/ -#define DEAD_BEEF 0xDEADBEEF /* SDK 에러 핸들러 매직 넘버 */ -#define UART_TX_BUF_SIZE 16384 /* UART 송신 FIFO 버퍼 크기 (16KB) */ -#define UART_RX_BUF_SIZE 512 /* UART 수신 FIFO 버퍼 크기 */ -#define POWER_ON_DELAY 5 /* 전원 버튼 폴링 간격 (5ms) */ -#define POWER_OFF_DELAY 3000 /* 전원 OFF 지연: LED 표시 후 3초 대기 */ -#define POWER_RESET_DELAY 2000 /* 리셋 지연: 2초 */ -#define LED_NUM 24 /* LED 핀 번호 */ -#define AES_KEY_SIZE 16 /* AES 암호화 키 크기 (128비트) */ -#define AES_BLOCK_SIZE 16 /* AES 블록 크기 (128비트) */ +#define DEAD_BEEF 0xDEADBEEF /* SDK error handler magic number */ +#define UART_TX_BUF_SIZE 16384 /* UART TX FIFO buffer size (16KB) */ +#define UART_RX_BUF_SIZE 512 /* UART RX FIFO buffer size */ +#define POWER_ON_DELAY 5 /* Power button polling interval (5ms) */ +#define POWER_OFF_DELAY 3000 /* Power-off delay: 3s after LED indication */ +#define POWER_RESET_DELAY 2000 /* Reset delay: 2s */ +#define LED_NUM 24 /* LED pin number */ +#define AES_KEY_SIZE 16 /* AES encryption key size (128-bit) */ +#define AES_BLOCK_SIZE 16 /* AES block size (128-bit) */ /*============================================================================== - * BLE 인스턴스 (SoftDevice 매크로로 정적 할당) + * BLE Instances (statically allocated via SoftDevice macros) *============================================================================*/ -BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT); /* Nordic UART Service 인스턴스 */ -NRF_BLE_GATT_DEF(m_gatt); /* GATT 모듈 인스턴스 (MTU 협상) */ -NRF_BLE_QWR_DEF(m_qwr); /* Queued Write 인스턴스 */ -BLE_ADVERTISING_DEF(m_advertising); /* 광고 모듈 인스턴스 */ +BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT); /* Nordic UART Service instance */ +NRF_BLE_GATT_DEF(m_gatt); /* GATT module instance (MTU negotiation) */ +NRF_BLE_QWR_DEF(m_qwr); /* Queued Write instance */ +BLE_ADVERTISING_DEF(m_advertising); /* Advertising module instance */ /*============================================================================== - * 타이머 인스턴스 + * Timer Instances *============================================================================*/ -APP_TIMER_DEF(m_power_on_delay_timer_id); /* 전원 버튼 폴링 타이머 (5ms 싱글샷, main_s 콜백) */ -APP_TIMER_DEF(m_power_off_delay_timer_id); /* 전원 OFF 지연 타이머 (3초 후 물리적 전원 차단) */ -APP_TIMER_DEF(m_PM_timer_id); /* Peer Manager 타이머 (연결 강제 해제용) */ +APP_TIMER_DEF(m_power_on_delay_timer_id); /* Power button polling timer (5ms single-shot, main_s callback) */ +APP_TIMER_DEF(m_power_off_delay_timer_id); /* Power-off delay timer (physical power cut after 3s) */ +APP_TIMER_DEF(m_PM_timer_id); /* Peer Manager timer (forced disconnect) */ /*============================================================================== - * 정적 변수 + * Static Variables *============================================================================*/ #if FEATURE_SECURE_CONNECTION static pm_peer_id_t m_peer_to_be_deleted = PM_PEER_ID_INVALID; #endif -static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /* 현재 BLE 연결 핸들 */ -static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3; /* NUS 최대 데이터 길이 (MTU - 오버헤드) */ -static ble_uuid_t m_adv_uuids[] = {{BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}}; /* 광고에 포함될 UUID */ +static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /* Current BLE connection handle */ +static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3; /* NUS max data length (MTU - overhead) */ +static ble_uuid_t m_adv_uuids[] = {{BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}}; /* UUIDs included in advertising */ -static uint8_t m_tx_buffer[BLE_NUS_MAX_DATA_LEN]; /* ASCII 텍스트 전송용 버퍼 */ -static uint16_t m_tx_len = 0; /* 전송할 데이터 길이 */ -static volatile bool m_tx_in_progress = false; /* TX 전송 진행 중 플래그 */ -static volatile bool m_tx_complete_pending = false; /* TX 완료 대기 플래그 */ -static uint8_t c_addr[6]; /* 연결된 피어의 BLE 주소 */ +static uint8_t m_tx_buffer[BLE_NUS_MAX_DATA_LEN]; /* ASCII text transmission buffer */ +static uint16_t m_tx_len = 0; /* Data length to transmit */ +static volatile bool m_tx_in_progress = false; /* TX in progress flag */ +static volatile bool m_tx_complete_pending = false; /* TX completion pending flag */ +static uint8_t c_addr[6]; /* Connected peer BLE address */ static char * roles_str[] = {"INVALID_ROLE", "CENTRAL", "PERIPHERAL"}; /*============================================================================== - * 전역 변수 + * Global Variables *============================================================================*/ -uint8_t m_encrypted_text[AES_BLOCK_SIZE]; /* AES 암호화 결과 버퍼 */ -uint8_t m_encrypted_text2[AES_BLOCK_SIZE]; /* AES 암호화 결과 버퍼 2 */ -uint8_t m_decrypted_text[AES_BLOCK_SIZE]; /* AES 복호화 결과 버퍼 */ +uint8_t m_encrypted_text[AES_BLOCK_SIZE]; /* AES encryption result buffer */ +uint8_t m_encrypted_text2[AES_BLOCK_SIZE]; /* AES encryption result buffer 2 */ +uint8_t m_decrypted_text[AES_BLOCK_SIZE]; /* AES decryption result buffer */ -volatile uint8_t Sj_type; /* 명령 타입 식별자 */ -volatile bool processing; /* 센서 데이터 처리 중 플래그 (중복 명령 방지) */ -bool power_off_duble_prohibit = false; /* 전원 OFF 중복 방지 플래그 */ -volatile bool power_state = false; /* 전원 상태 추적 */ +volatile uint8_t Sj_type; /* Command type identifier */ +volatile bool processing; /* Sensor data processing flag (prevents duplicate commands) */ +bool power_off_duble_prohibit = false; /* Power-off double prevention flag */ +volatile bool power_state = false; /* Power state tracking */ -/* ── 외부 모듈 플래그 (main_timer/power_control에서 정의) ── */ -extern bool go_device_power_off; /* 전원 OFF 요청 플래그 */ -extern bool go_sleep_mode_enter; /* 슬립 모드 진입 요청 플래그 */ -extern bool go_NVIC_SystemReset; /* 시스템 리셋 요청 플래그 */ -extern bool ble_got_new_data; /* BLE 새 데이터 수신 플래그 */ -extern bool motion_data_once; /* 모션 데이터 1회 전송 플래그 */ -extern bool con_single; /* 단일 연결 모드 플래그 */ -extern bool info4; /* 디바이스 정보 전송 완료 플래그 */ -extern uint8_t add_cycle; /* 추가 측정 사이클 카운터 */ -extern bool motion_raw_data_enabled; /* 모션 원시 데이터 스트리밍 활성화 */ +/* -- External module flags (defined in main_timer/power_control) -- */ +extern bool go_device_power_off; /* Power-off request flag */ +extern bool go_sleep_mode_enter; /* Sleep mode entry request flag */ +extern bool go_NVIC_SystemReset; /* System reset request flag */ +extern bool ble_got_new_data; /* BLE new data received flag */ +extern bool motion_data_once; /* Motion data one-shot transmit flag */ +extern bool con_single; /* Single connection mode flag */ +extern bool info4; /* Device info transmission complete flag */ +extern uint8_t add_cycle; /* Additional measurement cycle counter */ +extern bool motion_raw_data_enabled; /* Motion raw data streaming enabled */ -uint16_t cnt_s; /* 전원 버튼 폴링 카운터 (5ms 단위, 150=0.75초) */ -char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN]; /* BLE 전송 텍스트 버퍼 */ -uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN]; /* BLE 바이너리 응답 버퍼 (2026-03-17: cmd_parse.c에서 이동) */ -uint16_t ble_bin_buff[BLE_NUS_MAX_DATA_LEN/2]; /* BLE 바이너리 전송 버퍼 (워드 단위) */ -which_cmd_t cmd_type_t; /* 현재 명령 소스 (CMD_BLE 또는 CMD_UART) */ +uint16_t cnt_s; /* Power button polling counter (5ms units, 150=0.75s) */ +char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN]; /* BLE text transmission buffer */ +uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN]; /* BLE binary response buffer (2026-03-17: moved from cmd_parse.c) */ +uint16_t ble_bin_buff[BLE_NUS_MAX_DATA_LEN/2]; /* BLE binary transmission buffer (word units) */ +which_cmd_t cmd_type_t; /* Current command source (CMD_BLE or CMD_UART) */ -bool device_status = false; /* 디바이스 활성 상태 (true=동작 중) */ -bool device_reset = true; /* 부팅 중 플래그 (true=아직 미부팅) */ +bool device_status = false; /* Device active state (true=running) */ +bool device_reset = true; /* Booting flag (true=not yet booted) */ #if FEATURE_SECURE_CONNECTION -bool erase_bonds; /* 본딩 삭제 플래그 (부팅 시 버튼 상태에 따라 설정) */ +bool erase_bonds; /* Bond erase flag (set by button state at boot) */ #endif -volatile bool ble_connection_st; /* BLE 연결 상태 (1=연결됨, 0=미연결) */ -volatile bool data_tx_in_progress = false; /* 바이너리 TX 진행 중 플래그 */ +volatile bool ble_connection_st; /* BLE connection state (1=connected, 0=disconnected) */ +volatile bool data_tx_in_progress = false; /* Binary TX in progress flag */ -/* ── BLE TX 비동기 재시도 상태 ── */ -static volatile bool s_tx_pending = false; /* TX 재시도 대기 중 */ -static uint8_t s_tx_pending_buf[BLE_NUS_MAX_DATA_LEN]; /* 대기 중인 패킷 (CRC 포함) */ -static uint16_t s_tx_pending_len = 0; /* 대기 중인 패킷 길이 */ +/* -- BLE TX async retry state -- */ +static volatile bool s_tx_pending = false; /* TX retry pending */ +static uint8_t s_tx_pending_buf[BLE_NUS_MAX_DATA_LEN]; /* Pending packet (with CRC) */ +static uint16_t s_tx_pending_len = 0; /* Pending packet length */ -char m_static_passkey[PASSKEY_LENGTH] = DEFAULT_PASSKEY; /* 정적 패스키 (6자리, FDS에서 로드) */ -char SERIAL_NO[SERIAL_NO_LENGTH]; /* 시리얼 번호 (BLE 디바이스 이름으로 사용) */ -char HW_NO[HW_NO_LENGTH]; /* 하드웨어 번호 (FDS 저장/읽기) */ -bool bond_data_delete; /* 본딩 데이터 삭제 요청 플래그 */ -uint32_t m_life_cycle; /* 디바이스 수명 사이클 카운터 */ -uint8_t resetCount = 0; /* 통신 타임아웃 카운터 (리셋 감지용) */ -bool info4; /* 센서 측정 정보(배터리/IMU/온도) 포함 측정 플래그 */ -uint8_t m_reset_status; /* 리셋 상태 코드 (1=정상, 2=SW리셋, 5=보안리셋, 10=본딩완료) */ +char m_static_passkey[PASSKEY_LENGTH] = DEFAULT_PASSKEY; /* Static passkey (6 digits, loaded from FDS) */ +char SERIAL_NO[SERIAL_NO_LENGTH]; /* Serial number (used as BLE device name) */ +char HW_NO[HW_NO_LENGTH]; /* Hardware number (FDS stored/read) */ +bool bond_data_delete; /* Bond data delete request flag */ +uint32_t m_life_cycle; /* Device life cycle counter */ +uint8_t resetCount = 0; /* Communication timeout counter (reset detection) */ +bool info4; /* Measurement with sensor info (battery/IMU/temp) flag */ +uint8_t m_reset_status; /* Reset status code (1=normal, 2=SW reset, 5=security reset, 10=bond complete) */ /*============================================================================== - * 미사용 변수 경고 억제 + * Suppress Unused Variable Warnings *============================================================================*/ #if FEATURE_SECURE_CONNECTION static void __attribute__((unused)) suppress_unused_warnings(void) @@ -287,50 +287,50 @@ static void __attribute__((unused)) suppress_unused_warnings(void) #endif /*============================================================================== - * 전원 관리 + * Power Management *============================================================================*/ /** - * @brief 전원 자가유지 핀 초기화 + * @brief Initialize power self-latch pin * - * P0.8(POWER_HOLD)을 출력으로 설정하고 HIGH로 구동하여 외부 전원 래치 회로를 유지 - * P0.8(POWER_HOLD) 핀이 LOW가 되면 물리적으로 전원이 차단 - * main() 진입 직후 가장 먼저 호출해야 함(Phase 0) + * Configure P0.8 (POWER_HOLD) as output and drive HIGH to maintain the external power latch. + * When P0.8 goes LOW, physical power is cut off. + * Must be called first upon main() entry (Phase 0). */ static void power_hold_init(void) { - NRF_P0->DIRSET = (1 << 8); /* P0.8을 출력으로 설정 */ - NRF_P0->OUTSET = (1 << 8); /* P0.8을 HIGH로 → 전원 유지 */ + NRF_P0->DIRSET = (1 << 8); /* Set P0.8 as output */ + NRF_P0->OUTSET = (1 << 8); /* Set P0.8 HIGH -> maintain power */ } /** - * @brief 물리적 전원 ON/OFF 제어 + * @brief Physical power ON/OFF control * - * POWER_HOLD 핀을 직접 제어하여 디바이스 전원을 물리적으로 ON/OFF - * OFF 시 전원 래치가 풀리면서 전체 시스템이 꺼짐(복귀 불가) + * Directly controls the POWER_HOLD pin for physical device power ON/OFF. + * When OFF, the power latch releases and the entire system shuts down (non-recoverable). */ static void power_control_handler(on_off_cont_t device_power_st) { if (device_power_st == OFF) { - nrf_gpio_pin_clear(POWER_HOLD); /* P0.8 LOW → 전원 래치 해제 → 전원 차단 */ + nrf_gpio_pin_clear(POWER_HOLD); /* P0.8 LOW -> release power latch -> power cut */ DBG_PRINTF("[PWR] OFF\r\n"); } else if (device_power_st == ON) { - nrf_gpio_pin_set(POWER_HOLD); /* P0.8 HIGH → 전원 유지 */ + nrf_gpio_pin_set(POWER_HOLD); /* P0.8 HIGH -> maintain power */ DBG_PRINTF("[PWR] ON\r\n"); } } /*============================================================================== - * GPIO 초기화 + * GPIO Initialization *============================================================================*/ /** - * @brief GPIO 초기화 + * @brief GPIO initialization * - * 전원 버튼 입력과 전원 유지 핀만 설정 + * Configure power button input and power hold pin only. */ static void gpio_init(void) { @@ -340,16 +340,16 @@ static void gpio_init(void) } /*============================================================================== - * 설정 로드 - * FDS(Flash Data Storage)에서 설정을 읽기 전에 기본값을 먼저 로드 - * FDS 초기화 후 load_flash_config()에서 플래시 저장값으로 덮어씀 + * Configuration Loading + * Load defaults before reading from FDS (Flash Data Storage). + * After FDS init, load_flash_config() overwrites with flash-stored values. *============================================================================*/ /** - * @brief 기본 설정값 로드 + * @brief Load default configuration values * - * FDS에서 설정을 읽기 전에 안전한 기본값을 전역 변수에 설정 - * 시리얼 번호, 패스키, 리셋 상태 등을 기본값으로 초기화 + * Set safe defaults in global variables before reading from FDS. + * Initializes serial number, passkey, reset status, etc. to defaults. */ static void load_default_config(void) { @@ -366,11 +366,11 @@ static void load_default_config(void) } /** - * @brief 내장 Flash(FDS)에서 설정값을 전역 변수로 로드 + * @brief Load configuration from internal flash (FDS) into global variables * - * 반드시 ble_stack_init() → fs_storage_init() → config_load() 이후에 호출해야 한다. - * config_load()가 FDS에서 읽어온 m_config 구조체를 런타임 전역 변수로 복사한다. - * 빈 필드(0x00 또는 0xFF)는 기본값으로 채우고 m_need_save_defaults 플래그를 설정한다. + * Must be called after ble_stack_init() -> fs_storage_init() -> config_load(). + * Copies the m_config struct read from FDS into runtime global variables. + * Empty fields (0x00 or 0xFF) are filled with defaults and m_need_save_defaults is set. */ static bool m_need_save_defaults = false; @@ -378,7 +378,7 @@ static void load_flash_config(void) { m_need_save_defaults = false; - /* 하드웨어 번호 — 비어있으면 기본값 채움 */ + /* Hardware number -- fill with default if empty */ if (m_config.hw_no[0] == 0 || m_config.hw_no[0] == (char)0xFF) { memset(m_config.hw_no, 0, HW_NO_LENGTH); @@ -387,7 +387,7 @@ static void load_flash_config(void) m_need_save_defaults = true; } - /* 시리얼 번호 — 비어있으면 기본값 채움 */ + /* Serial number -- fill with default if empty */ if (m_config.serial_no[0] == 0 || m_config.serial_no[0] == (char)0xFF) { memset(m_config.serial_no, 0, SERIAL_NO_LENGTH); @@ -396,18 +396,18 @@ static void load_flash_config(void) m_need_save_defaults = true; } - /* 시리얼 번호 → BLE 디바이스 이름으로 복사 */ + /* Copy serial number -> BLE device name */ memset(SERIAL_NO, 0, SERIAL_NO_LENGTH); memcpy(SERIAL_NO, m_config.serial_no, SERIAL_NO_LENGTH); - /* 패스키 복사 */ + /* Copy passkey */ memset(m_static_passkey, 0, PASSKEY_LENGTH); memcpy(m_static_passkey, m_config.static_passkey, PASSKEY_LENGTH); - /* 본딩 데이터 삭제 플래그 */ + /* Bond data delete flag */ bond_data_delete = m_config.bond_data_delete; - /* 리셋 상태 */ + /* Reset status */ m_reset_status = m_config.reset_status; DBG_PRINTF("[CFG] HW=%.12s S/N=%s passkey=%.6s bond=%d rst=%d\r\n", @@ -420,18 +420,18 @@ static void load_flash_config(void) } /*============================================================================== - * 타이머 콜백 함수 + * Timer Callback Functions *============================================================================*/ -static void main_s(void * p_context); /* 전원 버튼 상태머신 (전방 선언) */ -static void t_power_off_timeout_handler(void * p_context); /* 전원 OFF 타임아웃 */ -static void PM_s(void * p_context); /* Peer Manager 타이머 */ +static void main_s(void * p_context); /* Power button state machine (forward declaration) */ +static void t_power_off_timeout_handler(void * p_context); /* Power-off timeout */ +static void PM_s(void * p_context); /* Peer Manager timer */ /** - * @brief 전원 OFF 타임아웃 콜백 + * @brief Power-off timeout callback * - * POWER_OFF_DELAY(3초) 경과 후 호출됨 - * LED를 끄고 POWER_HOLD 핀을 LOW로 설정하여 물리적 전원을 차단 - * 이 함수 이후 디바이스는 완전히 꺼짐(복귀 불가) + * Called after POWER_OFF_DELAY (3s) elapses. + * Turns off LED and sets POWER_HOLD pin LOW for physical power cut. + * After this function, the device is completely off (non-recoverable). */ static void t_power_off_timeout_handler(void * p_context) { @@ -439,14 +439,14 @@ static void t_power_off_timeout_handler(void * p_context) APP_ERROR_CHECK(app_timer_stop(m_power_off_delay_timer_id)); DBG_PRINTF("[PWR] Off timeout\r\n"); led_set_state(LED_STATE_OFF); /* LED OFF */ - power_control_handler(OFF); /* P0.8 LOW → 전원 차단 */ + power_control_handler(OFF); /* P0.8 LOW -> power cut */ } /** - * @brief Peer Manager 타이머 콜백 + * @brief Peer Manager timer callback * - * 보안 관련 상태(m_reset_status==5)에서 BLE 연결을 강제 해제 - * 본딩 정보 삭제 후 재연결을 위해 기존 연결을 끊을 때 사용 + * Force BLE disconnect when in security-related state (m_reset_status==5). + * Used to drop the existing connection for reconnection after bond deletion. */ static void PM_s(void * p_context) { @@ -461,19 +461,19 @@ static void PM_s(void * p_context) } /*============================================================================== - * 타이머 초기화/시작 함수 + * Timer Initialization / Start Functions *============================================================================*/ /** - * @brief 모든 앱 타이머 초기화 (부트 Phase 3) + * @brief Initialize all app timers (boot Phase 3) * - * app_timer 모듈 초기화 후 각 타이머 생성: - * - m_power_on_delay_timer: 전원 버튼 폴링 (5ms 싱글샷, main_s 콜백) - * - m_power_off_delay_timer: 전원 OFF 지연 (3초 싱글샷) - * - m_PM_timer: Peer Manager 연결 해제 (싱글샷) - * - main_timer: 메인 이벤트 루프 (10ms 싱글샷, main_loop 콜백) - * - battery_timer: 배터리 모니터링 (5초 반복) - * - power_timer: 전원 시퀀스 (20ms 싱글샷, power_loop 콜백) + * Initialize app_timer module then create each timer: + * - m_power_on_delay_timer: power button polling (5ms single-shot, main_s callback) + * - m_power_off_delay_timer: power-off delay (3s single-shot) + * - m_PM_timer: Peer Manager disconnect (single-shot) + * - main_timer: main event loop (10ms single-shot, main_loop callback) + * - battery_timer: battery monitoring (5s repeating) + * - power_timer: power sequence (20ms single-shot, power_loop callback) */ static void timers_init(void) { @@ -494,10 +494,10 @@ static void timers_init(void) } /** - * @brief 전원 버튼 폴링 타이머 시작 + * @brief Start the power button polling timer * - * 5ms(POWER_ON_DELAY) 후 main_s() 콜백 - * main_s()에서 버튼 상태를 확인하고, 아직 눌려있으면 다시 호출하여 상태머신 구동 + * Fires main_s() callback after 5ms (POWER_ON_DELAY). + * main_s() checks button state; if still pressed, re-arms the timer to drive the state machine. */ static void timers_start(void) { @@ -507,9 +507,9 @@ static void timers_start(void) } /*============================================================================== - * BLE DFU 핸들러 - * DFU(Device Firmware Update) 이벤트를 처리하여 BLE를 통한 펌웨어 업데이트를 지원한다. - * 부트로더 진입 준비, 진입 성공/실패 등의 이벤트를 로그로 출력한다. + * BLE DFU Handler + * Handles DFU (Device Firmware Update) events for firmware update over BLE. + * Logs bootloader entry preparation, success/failure events. *============================================================================*/ #if BLE_DFU_ENABLED static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event) @@ -536,15 +536,15 @@ static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event) #endif /*============================================================================== - * BLE GAP 초기화 + * BLE GAP Initialization *============================================================================*/ /** - * @brief GAP(Generic Access Profile) 파라미터 초기화 + * @brief Initialize GAP (Generic Access Profile) parameters * - * 1) 디바이스 이름을 SERIAL_NO(시리얼 번호)로 설정 → BLE 스캔 시 표시됨 - * 2) 연결 파라미터 설정 (20~75ms 간격, 슬레이브 지연 0, 감독 타임아웃 4초) - * 3) 정적 패스키 설정 (FEATURE_STATIC_PASSKEY 활성 시) + * 1) Set device name to SERIAL_NO -> shown during BLE scan + * 2) Configure connection parameters (20~75ms interval, slave latency 0, supervision timeout 4s) + * 3) Set static passkey (when FEATURE_STATIC_PASSKEY enabled) */ static void gap_params_init(void) { @@ -579,28 +579,28 @@ static void gap_params_init(void) } /*============================================================================== - * BLE 서비스 초기화 + * BLE Service Initialization *============================================================================*/ static void nrf_qwr_error_handler(uint32_t nrf_error) { APP_ERROR_HANDLER(nrf_error); } -/* 비동기 MAA TX 완료 핸들러 (전방 선언) */ +/* Async MAA TX complete handler (forward declarations) */ extern bool maa_async_on_tx_ready(void); extern bool maa_async_is_busy(void); extern void maa_async_abort(void); -/* 2026-03-17: BLE 콜백에서 블로킹 방지 — 명령을 버퍼에 저장 후 메인 루프에서 처리 */ +/* 2026-03-17: Prevent blocking in BLE callback -- buffer command for main loop processing */ static volatile uint8_t pending_cmd_buf[BLE_NUS_MAX_DATA_LEN]; static volatile uint8_t pending_cmd_len = 0; /** - * @brief NUS(Nordic UART Service) 데이터 수신 핸들러 + * @brief NUS (Nordic UART Service) data receive handler * - * BLE를 통해 스마트폰 앱에서 데이터를 수신했을 때 호출됨 - * - RX_DATA 이벤트: 수신 데이터를 버퍼에 복사 (메인 루프에서 dr_cmd_parser 호출) - * - TX_RDY 이벤트: BLE TX 버퍼에 공간이 생겼을 때 비동기 MAA 전송 계속 + * Called when data is received from the smartphone app over BLE. + * - RX_DATA event: copy received data to buffer (dr_cmd_parser called in main loop) + * - TX_RDY event: BLE TX buffer has space, continue async MAA transmission */ static void nus_data_handler(ble_nus_evt_t * p_evt) { @@ -609,7 +609,7 @@ static void nus_data_handler(ble_nus_evt_t * p_evt) cmd_type_t = CMD_BLE; ble_got_new_data = true; - /* Central이 연결 간격을 늘렸을 수 있으므로 빠른 간격 재요청 (30초에 1회) */ + /* Central may have increased connection interval; re-request fast interval (once per 30s) */ { static uint32_t last_update_tick = 0; uint32_t now_tick = app_timer_cnt_get(); @@ -626,7 +626,7 @@ static void nus_data_handler(ble_nus_evt_t * p_evt) } } - /* 콜백에서는 복사만 하고, 메인 루프에서 처리 */ + /* Only copy in callback; process in main loop */ if (p_evt->params.rx_data.length <= BLE_NUS_MAX_DATA_LEN) { memcpy((void *)pending_cmd_buf, p_evt->params.rx_data.p_data, p_evt->params.rx_data.length); @@ -635,12 +635,12 @@ static void nus_data_handler(ble_nus_evt_t * p_evt) } else if (p_evt->type == BLE_NUS_EVT_TX_RDY) { - /* BLE TX 버퍼에 공간이 생김 - 비동기 MAA 전송 계속 */ + /* BLE TX buffer has space - continue async MAA transmission */ if (maa_async_is_busy()) { maa_async_on_tx_ready(); } - /* pending 패킷 재시도 (dr_binary_tx_safe 비동기) */ + /* Retry pending packet (dr_binary_tx_safe async) */ else if (s_tx_pending) { uint16_t send_len = s_tx_pending_len; @@ -649,16 +649,16 @@ static void nus_data_handler(ble_nus_evt_t * p_evt) { s_tx_pending = false; } - /* NRF_ERROR_RESOURCES → 다음 TX_RDY에서 다시 시도 */ + /* NRF_ERROR_RESOURCES -> retry on next TX_RDY */ } } } /** - * @brief BLE 서비스 초기화 + * @brief Initialize BLE services * - * QWR(Queued Write) + NUS(Nordic UART Service) + DFU(선택) 서비스 초기화 - * NUS의 data_handler로 nus_data_handler를 등록하여 BLE 수신 데이터 처리 + * Initialize QWR (Queued Write) + NUS (Nordic UART Service) + DFU (optional). + * Register nus_data_handler as the NUS data_handler for BLE receive processing. */ static void services_init(void) { @@ -684,9 +684,9 @@ static void services_init(void) } /*============================================================================== - * BLE 연결 파라미터 협상 + * BLE Connection Parameter Negotiation *============================================================================*/ -/** @brief 연결 파라미터 협상 실패 시 연결 해제 */ +/** @brief Disconnect on connection parameter negotiation failure */ static void on_conn_params_evt(ble_conn_params_evt_t * p_evt) { if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED) @@ -703,10 +703,10 @@ static void conn_params_error_handler(uint32_t nrf_error) } /** - * @brief BLE 연결 파라미터 모듈 초기화 + * @brief Initialize BLE connection parameter module * - * 연결 후 5초 대기 → 파라미터 갱신 요청 → 30초 간격으로 최대 3회 재시도 - * 실패 시 on_conn_params_evt에서 연결 해제 처리 + * Wait 5s after connection -> request param update -> retry every 30s up to 3 times. + * On failure, on_conn_params_evt disconnects. */ static void conn_params_init(void) { @@ -728,17 +728,17 @@ static void conn_params_init(void) } /*============================================================================== - * BLE 스택 초기화 + * BLE Stack Initialization *============================================================================*/ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context); /** - * @brief SoftDevice BLE 스택 초기화 + * @brief Initialize SoftDevice BLE stack * - * 1) SoftDevice 활성화 요청 - * 2) BLE 기본 설정 (RAM 시작 주소 자동 계산) - * 3) BLE 활성화 - * 4) BLE 이벤트 옵저버 등록 (ble_evt_handler) + * 1) Request SoftDevice enable + * 2) Set BLE default config (auto-compute RAM start address) + * 3) Enable BLE + * 4) Register BLE event observer (ble_evt_handler) */ static void ble_stack_init(void) { @@ -759,10 +759,10 @@ static void ble_stack_init(void) /*============================================================================== * BLE GATT (Generic Attribute Profile) - * MTU 협상을 통해 한 번에 전송 가능한 최대 데이터 크기를 결정한다. + * Determines max data size per transfer via MTU negotiation. *============================================================================*/ -/** @brief GATT MTU 갱신 이벤트 핸들러 — NUS 최대 데이터 길이를 업데이트 */ +/** @brief GATT MTU update event handler -- updates NUS max data length */ void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt) { if ((m_conn_handle == p_evt->conn_handle) && (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED)) @@ -771,7 +771,7 @@ void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt) } } -/** @brief GATT 모듈 초기화 — MTU를 최대 크기로 설정 */ +/** @brief Initialize GATT module -- set MTU to max size */ static void gatt_init(void) { ret_code_t err_code; @@ -784,14 +784,14 @@ static void gatt_init(void) } /*============================================================================== - * BLE 광고 (Advertising) + * BLE Advertising *============================================================================*/ /** - * @brief BLE 광고 이벤트 핸들러 + * @brief BLE advertising event handler * - * - FAST: 광고 시작됨 (LED 표시) - * - IDLE: 광고 타임아웃 → 슬립 모드 진입 플래그 설정 + * - FAST: advertising started (LED indication) + * - IDLE: advertising timeout -> set sleep mode entry flag */ static void on_adv_evt(ble_adv_evt_t ble_adv_evt) { @@ -812,11 +812,11 @@ static void on_adv_evt(ble_adv_evt_t ble_adv_evt) } /** - * @brief BLE 광고 초기화 + * @brief Initialize BLE advertising * - * 광고 데이터 설정: 디바이스 이름(전체), NUS UUID - * 광고 모드: Fast (40ms 간격), 기본 지속시간 10분 - * disconnect reason에 따라 런타임에 무한 광고로 전환될 수 있음 + * Advertising data: full device name, NUS UUID. + * Advertising mode: Fast (40ms interval), default duration 10 min. + * May switch to unlimited advertising at runtime based on disconnect reason. */ static void advertising_init(void) { @@ -844,7 +844,7 @@ static void advertising_init(void) } #if FEATURE_SECURE_CONNECTION -/** @brief 모든 본딩(페어링) 정보를 Flash에서 삭제 */ +/** @brief Delete all bonding (pairing) info from flash */ static void delete_bonds(void) { ret_code_t err_code; @@ -854,11 +854,11 @@ static void delete_bonds(void) } /** - * @brief BLE 광고 시작 (보안 모드) + * @brief Start BLE advertising (secure mode) * - * erase_bonds_flag가 true이면 먼저 모든 본딩 정보를 삭제한 후 - * PM_EVT_PEERS_DELETE_SUCCEEDED 이벤트에서 광고를 시작한다. - * false이면 즉시 Fast 모드로 광고를 시작한다. + * If erase_bonds_flag is true, delete all bond info first, then + * start advertising from PM_EVT_PEERS_DELETE_SUCCEEDED event. + * If false, start Fast mode advertising immediately. */ static void advertising_start(bool erase_bonds_flag) { @@ -867,12 +867,12 @@ static void advertising_start(bool erase_bonds_flag) DBG_PRINTF("[BOND] Delete requested (count_before=%lu)\r\n", (unsigned long)pm_peer_count()); - /* 본딩 삭제 요청 플래그를 플래시에서도 클리어 → 다음 부팅 시 재삭제 방지 */ + /* Clear bond delete request flag in flash -> prevent re-deletion on next boot */ bond_data_delete = false; m_config.bond_data_delete = 0; config_save(); - delete_bonds(); /* 삭제 완료 후 pm_evt_handler에서 광고 시작 */ + delete_bonds(); /* After deletion, advertising starts from pm_evt_handler */ } else { @@ -881,7 +881,7 @@ static void advertising_start(bool erase_bonds_flag) } } #else -/** @brief BLE 광고 시작 (비보안 모드, 즉시 Fast 광고) */ +/** @brief Start BLE advertising (non-secure mode, immediate Fast advertising) */ static void advertising_start(void) { uint32_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST); @@ -890,19 +890,19 @@ static void advertising_start(void) #endif /*============================================================================== - * BLE 보안 (Peer Manager) - * LESC/정적 패스키 기반 MITM 보호, 본딩 관리 + * BLE Security (Peer Manager) + * LESC/static passkey MITM protection, bonding management *============================================================================*/ #if FEATURE_SECURE_CONNECTION /** - * @brief Peer Manager 이벤트 핸들러 + * @brief Peer Manager event handler * - * BLE 보안 관련 이벤트를 처리한다: - * - CONN_SEC_SUCCEEDED: 보안 연결 성공 → MITM 보호 확인 후 연결 활성화 - * - CONN_SEC_FAILED: 보안 실패 → PIN/키 누락 시 재시도 - * - PEERS_DELETE_SUCCEEDED: 본딩 삭제 완료 → 광고 재시작 - * - CONN_SEC_CONFIG_REQ: 재페어링 허용 설정 - * - PEER_DATA_UPDATE_SUCCEEDED: 피어 데이터 갱신 → 주소 저장, 리셋 상태 업데이트 + * Handles BLE security events: + * - CONN_SEC_SUCCEEDED: security established -> verify MITM then activate connection + * - CONN_SEC_FAILED: security failed -> retry on PIN/key missing + * - PEERS_DELETE_SUCCEEDED: bond deletion complete -> restart advertising + * - CONN_SEC_CONFIG_REQ: allow re-pairing config + * - PEER_DATA_UPDATE_SUCCEEDED: peer data updated -> save address, update reset status */ static void pm_evt_handler(pm_evt_t const * p_evt) { @@ -910,28 +910,28 @@ static void pm_evt_handler(pm_evt_t const * p_evt) uint32_t return_code; ret_code_t err_code; - /* SDK 기본 핸들러 + 보안 처리 (ble_quick_security 내부에서 SDK 핸들러 호출) */ + /* SDK default handler + security processing (SDK handler called inside ble_quick_security) */ ble_security_quick_pm_handler(p_evt); switch (p_evt->evt_id) { - /* 보안 연결 성공 */ + /* Security connection succeeded */ case PM_EVT_CONN_SEC_SUCCEEDED: { pm_conn_sec_status_t conn_sec_status; err_code = pm_conn_sec_status_get(p_evt->conn_handle, &conn_sec_status); APP_ERROR_CHECK(err_code); - /* MITM 보호 확인 (개발 모드에서는 무조건 통과) */ + /* Verify MITM protection (always pass in dev mode) */ if (conn_sec_status.mitm_protected || BLE_DEV_MODE) { DBG_PRINTF("[PM] Secured\r\n"); - ble_connection_st = 1; /* 연결 상태 활성화 */ - battery_timer_start(); /* 배터리 모니터링 시작 */ + ble_connection_st = 1; /* Activate connection state */ + battery_timer_start(); /* Start battery monitoring */ } else { - /* MITM 보호 안 됨 → 피어 삭제 후 연결 해제 */ + /* MITM not protected -> delete peer and disconnect */ DBG_PRINTF("[PM] Sec FAIL\r\n"); err_code = pm_peer_id_get(m_conn_handle, &m_peer_to_be_deleted); APP_ERROR_CHECK(err_code); @@ -941,12 +941,12 @@ static void pm_evt_handler(pm_evt_t const * p_evt) } break; - /* 보안 연결 실패 (retry/disconnect는 ble_quick_security에서 처리) */ + /* Security connection failed (retry/disconnect handled by ble_quick_security) */ case PM_EVT_CONN_SEC_FAILED: DBG_PRINTF("[PM] Sec failed\r\n"); break; - /* 모든 본딩 삭제 완료 → 활성 연결이 없을 때만 광고 재시작 */ + /* All bonds deleted -> restart advertising only if no active connection */ case PM_EVT_PEERS_DELETE_SUCCEEDED: DBG_PRINTF("[BOND] Deleted all (count_after=%lu)\r\n", (unsigned long)pm_peer_count()); @@ -955,9 +955,9 @@ static void pm_evt_handler(pm_evt_t const * p_evt) } break; - /* PM_EVT_CONN_SEC_CONFIG_REQ → ble_quick_security에서 처리 */ + /* PM_EVT_CONN_SEC_CONFIG_REQ -> handled by ble_quick_security */ - /* 피어 데이터(본딩 정보) 갱신 성공 → 피어 BLE 주소 저장 */ + /* Peer data (bonding info) update succeeded -> save peer BLE address */ case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED: { return_code = pm_peer_data_bonding_load(p_evt->peer_id, &peer_bonding_data); @@ -965,7 +965,7 @@ static void pm_evt_handler(pm_evt_t const * p_evt) { memcpy(c_addr, peer_bonding_data.peer_ble_id.id_addr_info.addr, sizeof(c_addr)); - /* 새 본딩 생성 시 피어 ID와 주소를 함께 로그로 남김 */ + /* Log peer ID and address when new bond is created */ if (p_evt->params.peer_data_update_succeeded.data_id == PM_PEER_DATA_ID_BONDING) { DBG_PRINTF("[BOND] Created peer_id=%u addr=%02X:%02X:%02X:%02X:%02X:%02X (count=%lu)\r\n", @@ -974,7 +974,7 @@ static void pm_evt_handler(pm_evt_t const * p_evt) (unsigned long)pm_peer_count()); } } - m_reset_status = 10; /* 본딩 완료 상태 */ + m_reset_status = 10; /* Bond complete status */ } break; @@ -984,10 +984,10 @@ static void pm_evt_handler(pm_evt_t const * p_evt) } /** - * @brief Peer Manager 초기화 + * @brief Initialize Peer Manager * - * BLE 보안 모듈을 초기화하고 이벤트 핸들러를 등록한다. - * BLE_DEV_MODE에 따라 보안 수준이 결정된다 (0=패스키 필수, 1=보안 없음). + * Initialize BLE security module and register event handler. + * Security level determined by BLE_DEV_MODE (0=passkey required, 1=no security). */ static void peer_manager_init(void) { @@ -1003,22 +1003,22 @@ static void peer_manager_init(void) #endif /*============================================================================== - * BLE 이벤트 핸들러 - * SoftDevice에서 발생하는 모든 BLE 이벤트를 처리한다. + * BLE Event Handler + * Handles all BLE events from the SoftDevice. *============================================================================*/ /** - * @brief BLE 이벤트 핸들러 (SoftDevice 옵저버) + * @brief BLE event handler (SoftDevice observer) * - * 주요 이벤트 처리: - * - DISCONNECTED: 연결 해제 → 디바이스 슬립, 상태 초기화 - * - CONNECTED: 연결 성공 → QWR 핸들 할당, TX 파워 +8dBm 설정 - * - PHY_UPDATE_REQUEST: PHY 업데이트 자동 수락 - * - TIMEOUT: 연결/GATT 타임아웃 → 강제 연결 해제 - * - SEC_PARAMS_REQUEST: 보안 파라미터 요청 (보안 미사용 시 거부) - * - PASSKEY_DISPLAY: 패스키 표시 (디버그 로그) - * - AUTH_KEY_REQUEST: 정적 패스키 응답 - * - HVN_TX_COMPLETE: TX 완료 → 전송 플래그 해제 + * Key events handled: + * - DISCONNECTED: connection lost -> device sleep, state reset + * - CONNECTED: connection established -> assign QWR handle, set TX power +8dBm + * - PHY_UPDATE_REQUEST: auto-accept PHY update + * - TIMEOUT: connection/GATT timeout -> force disconnect + * - SEC_PARAMS_REQUEST: security parameter request (reject if security unused) + * - PASSKEY_DISPLAY: display passkey (debug log) + * - AUTH_KEY_REQUEST: respond with static passkey + * - HVN_TX_COMPLETE: TX complete -> clear transmission flag */ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) { @@ -1035,15 +1035,15 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) switch (p_ble_evt->header.evt_id) { - /* BLE 연결이 끊어지는 경우 */ + /* BLE connection lost */ case BLE_GAP_EVT_DISCONNECTED: { uint8_t disc_reason = p_ble_evt->evt.gap_evt.params.disconnected.reason; - /* 의도치 않은 disconnect 판별: + /* Determine unintended disconnect: * 0x08 supervision timeout, 0x22 LMP response timeout, * 0x3D MIC failure, 0x3E connection failed to be established - * → 무한 광고 유지 + 슬립 진입 차단 + * -> maintain unlimited advertising + block sleep entry */ bool unintended_disc = (disc_reason == BLE_HCI_CONNECTION_TIMEOUT) || (disc_reason == BLE_HCI_STATUS_CODE_LMP_RESPONSE_TIMEOUT) || @@ -1057,10 +1057,10 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) m_conn_handle = BLE_CONN_HANDLE_INVALID; m_tx_in_progress = false; - maa_async_abort(); // 비동기 측정 상태에 의한 먹통 현상 방지 - s_tx_pending = false; // pending TX 클리어 + maa_async_abort(); // Prevent hang caused by async measurement state + s_tx_pending = false; // Clear pending TX - /* 광고 지속시간을 disconnect 원인에 따라 런타임 조정 */ + /* Adjust advertising duration at runtime based on disconnect reason */ (void)sd_ble_gap_adv_stop(m_advertising.adv_handle); m_advertising.adv_modes_config.ble_adv_fast_timeout = unintended_disc ? APP_ADV_DURATION_UNLIMITED : APP_ADV_DURATION_NORMAL; @@ -1071,7 +1071,7 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) APP_ERROR_CHECK(adv_err); } - /* 의도치 않은 disconnect: 슬립 진입 차단하여 지속 광고 보장 */ + /* Unintended disconnect: block sleep entry to ensure continuous advertising */ if (!unintended_disc && device_status == true) { if (device_sleep_mode() == 0) @@ -1096,7 +1096,7 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_conn_handle, 4); - // 2M PHY 요청 (지원 안 되면 자동으로 1M 유지) + // Request 2M PHY (falls back to 1M automatically if unsupported) { ble_gap_phys_t const phys = { .rx_phys = BLE_GAP_PHY_2MBPS, @@ -1106,7 +1106,7 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) APP_ERROR_CHECK(err_code); } - led_set_state(LED_STATE_OFF); /* 연결 완료 → LED OFF */ + led_set_state(LED_STATE_OFF); /* Connection complete -> LED OFF */ break; @@ -1183,7 +1183,7 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) case BLE_GATTS_EVT_HVN_TX_COMPLETE: m_tx_in_progress = false; - m_tx_complete_pending = false; /* TX 완료를 대기 중인 함수에 알림 */ + m_tx_complete_pending = false; /* Notify waiting functions of TX completion */ break; default: @@ -1192,16 +1192,16 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) } /*============================================================================== - * BSP(Board Support Package) 이벤트 핸들러 + * BSP (Board Support Package) Event Handler *============================================================================*/ /** - * @brief BSP 이벤트 핸들러 (버튼/LED) + * @brief BSP event handler (buttons/LEDs) * - * - SLEEP: 슬립 모드 진입 - * - DISCONNECT: BLE 연결 강제 해제 - * - WHITELIST_OFF: 화이트리스트 없이 광고 재시작 - * - POWER_CONTROL: 전원 OFF 시퀀스 시작 + * - SLEEP: enter sleep mode + * - DISCONNECT: force BLE disconnect + * - WHITELIST_OFF: restart advertising without whitelist + * - POWER_CONTROL: start power-off sequence */ void bsp_event_handler(bsp_event_t event) { @@ -1249,10 +1249,10 @@ void bsp_event_handler(bsp_event_t event) } /*============================================================================== - * 유틸리티 함수 + * Utility Functions *============================================================================*/ -/** @brief BSP 버튼 초기화 + LED 직접 초기화 (부트 Phase 5) */ +/** @brief BSP button init + LED direct init (boot Phase 5) */ static void buttons_leds_init(bool * p_erase_bonds) { bsp_event_t startup_event; @@ -1265,10 +1265,10 @@ static void buttons_leds_init(bool * p_erase_bonds) err_code = bsp_btn_ble_init(NULL, &startup_event); APP_ERROR_CHECK(err_code); - /* VesiScan은 전원 버튼을 눌러서 부팅하는 구조이므로, 부팅 순간에는 항상 - * 전원 버튼이 눌린 상태로 읽힌다. Nordic BSP는 이 상태를 - * BSP_EVENT_CLEAR_BONDING_DATA로 해석해 본딩을 삭제하려 하므로, - * startup event 기반 본딩 삭제 트리거를 무효화한다. */ + /* VesiScan boots by pressing the power button, so the button is always + * read as pressed at boot time. Nordic BSP interprets this as + * BSP_EVENT_CLEAR_BONDING_DATA and tries to delete bonds, + * so we invalidate the startup event-based bond erase trigger. */ (void)startup_event; *p_erase_bonds = false; } @@ -1281,11 +1281,11 @@ static void log_init(void) } /** - * @brief UART 수신 이벤트 핸들러 + * @brief UART receive event handler * - * 물리 UART(1Mbps)로 수신된 데이터를 한 바이트씩 버퍼에 축적하다가 - * '\n' 또는 '\r'를 수신하면 received_command_process()로 전달한다. - * 디버그 및 공장 테스트용. + * Accumulates bytes received from physical UART (1Mbps) into a buffer. + * On receiving '\n' or '\r', passes data to received_command_process(). + * Used for debug and factory testing. */ void uart_event_handle(app_uart_evt_t * p_event) { @@ -1324,7 +1324,7 @@ void uart_event_handle(app_uart_evt_t * p_event) } } -/** @brief UART 초기화 (1Mbps, 흐름제어 없음, 패리티 없음) — 현재 미사용 (전력 절감) */ +/** @brief UART init (1Mbps, no flow control, no parity) -- currently unused (power saving) */ #if 0 static void uart_init(void) { @@ -1362,11 +1362,11 @@ static void power_management_init(void) } /** - * @brief 유휴 상태 처리 + * @brief Idle state processing * - * BLE 보안(LESC) 요청을 처리하고, 로그를 플러시한 뒤 - * nrf_pwr_mgmt_run()으로 CPU를 저전력 상태로 전환한다. - * BLE 이벤트 발생 시 자동으로 깨어난다. + * Process BLE security (LESC) requests, flush logs, then + * put the CPU into low-power state via nrf_pwr_mgmt_run(). + * Automatically wakes on BLE events. */ static void idle_state_handle(void) { @@ -1388,10 +1388,10 @@ void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name) } /** - * @brief 슬립 모드 진입 + * @brief Enter sleep mode * - * LED를 켜서 사용자에게 알린 후, 3초(POWER_OFF_DELAY) 대기 타이머를 시작한다. - * 타이머 만료 시 t_power_off_timeout_handler()에서 물리적 전원을 차단한다. + * Indicate to user via LED, then start 3s (POWER_OFF_DELAY) wait timer. + * On timer expiry, t_power_off_timeout_handler() cuts physical power. */ void sleep_mode_enter(void) { @@ -1401,10 +1401,10 @@ void sleep_mode_enter(void) } /** - * @brief 디바이스 전원 OFF + * @brief Device power off * - * LED를 켜서 사용자에게 알린 후, 3초 대기 타이머를 시작한다. - * sleep_mode_enter()와 동일한 메커니즘으로 물리적 전원을 차단한다. + * Indicate to user via LED, then start 3s wait timer. + * Cuts physical power using the same mechanism as sleep_mode_enter(). */ void device_power_off(void) { @@ -1413,16 +1413,16 @@ void device_power_off(void) } /*============================================================================== - * 데이터 전송 함수 - * BLE NUS를 통해 센서 데이터/응답을 스마트폰 앱으로 전송한다. - * 패킷 구조: [데이터][CRC16 2바이트] + * Data Transmission Functions + * Transmit sensor data/responses to the smartphone app via BLE NUS. + * Packet structure: [data][CRC16 2 bytes] *============================================================================*/ /** - * @brief ASCII 텍스트 BLE 전송 + * @brief ASCII text BLE transmission * - * 문자열에서 '\r'까지의 내용을 복사하고 CRC16을 추가하여 BLE로 전송한다. - * 이미 전송 중(m_tx_in_progress)이면 즉시 리턴한다. + * Copy string content up to '\r', append CRC16, and send via BLE. + * Returns immediately if a transmission is already in progress (m_tx_in_progress). */ void data_tx_handler(char const *p_data_to_send) { @@ -1454,7 +1454,7 @@ void data_tx_handler(char const *p_data_to_send) m_conn_handle = BLE_CONN_HANDLE_INVALID; m_tx_in_progress = false; } else { - DBG_PRINTF("[BLE TX] Err:0x%X\r\n", err_code); // APP_ERROR_CHECK 대신 로그 - jhChun 26.03.16 + DBG_PRINTF("[BLE TX] Err:0x%X\r\n", err_code); // Log instead of APP_ERROR_CHECK - jhChun 26.03.16 m_tx_in_progress = false; //APP_ERROR_CHECK(err_code); } @@ -1462,10 +1462,10 @@ void data_tx_handler(char const *p_data_to_send) } /** - * @brief 단일 uint16_t 값을 바이너리 패킷으로 포맷 + * @brief Format single uint16_t value into binary packet * - * 패킷 구조: [태그 4바이트][값 2바이트] = 총 6바이트 (3 워드) - * 태그는 ASCII 4문자 (예: "rta:", "rsn:"), 값은 빅엔디안으로 저장 + * Packet structure: [tag 4 bytes][value 2 bytes] = 6 bytes total (3 words). + * Tag is 4 ASCII chars (e.g. "rta:", "rsn:"), value stored big-endian. */ void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value) { @@ -1481,10 +1481,10 @@ void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value) } /** - * @brief uint16_t 배열을 바이너리 패킷으로 포맷 + * @brief Format uint16_t array into binary packet * - * 패킷 구조: [태그 4바이트][데이터0 2바이트][데이터1 2바이트]... - * 각 데이터는 빅엔디안으로 저장, length는 바이트 수가 아닌 배열 원소 수 + * Packet structure: [tag 4 bytes][data0 2 bytes][data1 2 bytes]... + * Each value stored big-endian; length is array element count, not byte count. */ void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length) { @@ -1503,9 +1503,9 @@ void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, s } /** - * @brief uint8_t 바이트 배열을 바이너리 패킷으로 포맷 + * @brief Format uint8_t byte array into binary packet * - * 패킷 구조: [태그 4바이트][바이트0][바이트1]... + * Packet structure: [tag 4 bytes][byte0][byte1]... */ void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length) { @@ -1523,10 +1523,10 @@ void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_arra } /** - * @brief ASCII 문자열을 바이너리 패킷으로 포맷 + * @brief Format ASCII string into binary packet * - * 패킷 구조: [태그 4바이트][문자0][문자1]... - * 시리얼 번호, 패스키, 펌웨어 버전 등 문자열 전송에 사용 + * Packet structure: [tag 4 bytes][char0][char1]... + * Used for transmitting serial number, passkey, firmware version, etc. */ void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length) { @@ -1544,25 +1544,25 @@ void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, } /** - * @brief BLE 바이너리 안전 전송 (dr_binary_tx_safe용 선행 설명) + * @brief BLE binary safe transmission (preamble for dr_binary_tx_safe) * - * NRF_ERROR_RESOURCES(TX 큐 가득 참) 발생 시 5ms 간격으로 재시도하고, - * 연결 오류 시 graceful하게 리턴한다. + * On NRF_ERROR_RESOURCES (TX queue full), retries at 5ms intervals. + * Returns gracefully on connection errors. * - * @param ble_bin_buff 전송할 데이터 버퍼 - * @param length uint16_t 워드 수 (실제 바이트 = length * 2) + * @param ble_bin_buff Data buffer to transmit + * @param length uint16_t word count (actual bytes = length * 2) */ /** - * @brief BLE 작업용 안전한 딜레이 + * @brief Safe delay for BLE operations * - * 인터럽트 안전한 nrf_delay_ms()를 사용한다. - * BLE 스택은 인터럽트로 동작하므로 딜레이 중에도 TX 완료 이벤트가 처리된다. + * Uses interrupt-safe nrf_delay_ms(). + * BLE stack runs on interrupts, so TX complete events are processed during delay. * - * 주의: __WFE()나 sd_app_evt_wait()를 여기서 사용하면 안 됨! - * SoftDevice 전원 관리와 충돌한다. + * Note: Do NOT use __WFE() or sd_app_evt_wait() here! + * It conflicts with SoftDevice power management. * - * @param ms 딜레이 시간 (밀리초) + * @param ms Delay time in milliseconds */ void dr_sd_delay_ms(uint32_t ms) { @@ -1596,7 +1596,7 @@ void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length) uint32_t err_code; static uint8_t tx_buffer[BLE_NUS_MAX_DATA_LEN] = {0}; uint16_t retry_count = 0; - const uint16_t MAX_RETRIES = 20; /* 최대 재시도: 2ms × 20 = 40ms (기존 5ms × 100 = 500ms) */ + const uint16_t MAX_RETRIES = 20; /* Max retries: 2ms x 20 = 40ms (was 5ms x 100 = 500ms) */ if (ble_connection_st == 0) return; @@ -1621,7 +1621,7 @@ void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length) } else if (err_code == NRF_ERROR_RESOURCES) { - nrf_delay_ms(2); /* 기존 5ms → 2ms */ + nrf_delay_ms(2); /* Was 5ms -> 2ms */ retry_count++; } else if (err_code == NRF_ERROR_INVALID_STATE || err_code == NRF_ERROR_NOT_FOUND) @@ -1644,14 +1644,14 @@ void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length) } /*============================================================================== - * 전원 버튼 상태머신 (main_s) + * Power Button State Machine (main_s) * - * 5ms 간격의 싱글샷 타이머(m_power_on_delay_timer)에 의해 반복 호출된다. - * 버튼 릴리즈 시점의 cnt_s 값에 따라 동작이 결정된다: - * - cnt_s < 150 (< 0.75초): 짧은 눌림 → 전원 OFF - * - cnt_s ≥ 150 (~0.75초): 정상 부팅 시퀀스 시작 - * - cnt_s > 1000 (~5초): 공장 초기화 (패스키 리셋 + 전원 OFF) - * - m_reset_status == 2: 소프트웨어 리셋 후 재부팅 + * Called repeatedly by a 5ms single-shot timer (m_power_on_delay_timer). + * Action determined by cnt_s value at button release: + * - cnt_s < 150 (< 0.75s): short press -> power OFF + * - cnt_s >= 150 (~0.75s): normal boot sequence start + * - cnt_s > 1000 (~5s): factory reset (passkey reset + power OFF) + * - m_reset_status == 2: reboot after software reset *============================================================================*/ static void main_s(void * p_context) { @@ -1692,7 +1692,7 @@ static void main_s(void * p_context) battery_timer_start(); #if FEATURE_SECURE_CONNECTION - /* DEV 모드: ble_security_quick_init()에서 이미 bond 삭제됨 → 중복 방지 */ + /* DEV mode: bonds already deleted in ble_security_quick_init() -> prevent duplicate */ advertising_start(!ble_security_is_dev_mode() && (erase_bonds || bond_data_delete)); #else advertising_start(); @@ -1717,25 +1717,25 @@ static void main_s(void * p_context) } /*============================================================================== - * 메인 함수 + * Main Function * - * 시스템 초기화를 단계별(Phase 0~9)로 수행한 뒤 무한 루프에 진입한다. - * 무한 루프에서는 idle_state_handle()로 CPU를 저전력 상태로 유지하며, - * BLE/타이머 인터럽트에 의해 깨어나 이벤트를 처리한다. + * Performs system initialization in phases (Phase 0~9), then enters infinite loop. + * In the loop, idle_state_handle() keeps the CPU in low-power state, + * waking on BLE/timer interrupts to process events. *============================================================================*/ int main(void) { bool erase_bonds_local = false; /*────────────────────────────────────────────────────────────── - * Phase 1: 하드웨어 기본 초기화 (BLE 무관) + * Phase 1: Basic hardware init (BLE-independent) * - * - 전원 자가유지 래치 (P0.8 HIGH) - * - RTT 로그 출력 - * - GPIO (전원 버튼 입력) - * - 앱 타이머 (전원 폴링, 배터리, 메인 루프) - * - 기본 설정값 (시리얼 번호, 패스키) - * - 버튼/LED (BSP) + * - Power self-latch (P0.8 HIGH) + * - RTT log output + * - GPIO (power button input) + * - App timers (power polling, battery, main loop) + * - Default config (serial number, passkey) + * - Buttons/LEDs (BSP) *────────────────────────────────────────────────────────────*/ power_hold_init(); @@ -1760,11 +1760,11 @@ int main(void) DBG_PRINTF(" gpio/timer/config/btn OK\r\n"); /*────────────────────────────────────────────────────────────── - * Phase 2: BLE 스택 초기화 + * Phase 2: BLE stack init * - * - 전원 관리 모듈 (idle 시 WFE/슬립) - * - SoftDevice S140 (BLE 5.0 프로토콜 스택) - * - DC-DC 컨버터 활성화 (SoftDevice 이후 필수) + * - Power management module (WFE/sleep when idle) + * - SoftDevice S140 (BLE 5.0 protocol stack) + * - DC-DC converter enable (required after SoftDevice) *────────────────────────────────────────────────────────────*/ DBG_PRINTF("[2] BLE Stack\r\n"); power_management_init(); @@ -1773,11 +1773,11 @@ int main(void) DBG_PRINTF(" pwr/stack/dcdc OK\r\n"); /*────────────────────────────────────────────────────────────── - * Phase 3: 내장 플래시 설정 (BLE 스택 이후에 초기화해야 함) + * Phase 3: Internal flash config (must init after BLE stack) * - * - FDS(Flash Data Storage) 초기화 - * - 플래시에서 저장된 설정 읽기 (시리얼, 패스키, 피에조 등) - * - 기본값 위에 플래시 값 덮어쓰기 + * - FDS (Flash Data Storage) init + * - Read stored config from flash (serial, passkey, piezo, etc.) + * - Overwrite defaults with flash values *────────────────────────────────────────────────────────────*/ DBG_PRINTF("[3] FDS\r\n"); fs_storage_init(); @@ -1786,14 +1786,14 @@ int main(void) DBG_PRINTF(" fds OK\r\n"); /*────────────────────────────────────────────────────────────── - * Phase 4: BLE 프로토콜 설정 + * Phase 4: BLE protocol setup * - * - GAP: 디바이스 이름(시리얼 번호), 연결 파라미터, 패스키 - * - GATT: MTU 크기 협상 - * - 서비스: NUS(Nordic UART Service), QWR, DFU - * - Advertising: 광고 데이터 구성 (이름, UUID) - * - 연결 파라미터 협상 모듈 - * - 보안: Peer Manager (본딩/패스키) + * - GAP: device name (serial number), connection params, passkey + * - GATT: MTU size negotiation + * - Services: NUS (Nordic UART Service), QWR, DFU + * - Advertising: advertising data (name, UUID) + * - Connection parameter negotiation module + * - Security: Peer Manager (bonding/passkey) *────────────────────────────────────────────────────────────*/ DBG_PRINTF("[4] BLE Protocol\r\n"); gap_params_init(); @@ -1804,7 +1804,7 @@ int main(void) #if FEATURE_SECURE_CONNECTION peer_manager_init(); - /* 부팅 시점의 본딩 상태 로그 */ + /* Log bond state at boot time */ { uint32_t bond_count = pm_peer_count(); DBG_PRINTF("[BOOT] Bond state: count=%lu flag_delete=%d erase_bonds=%d\r\n", @@ -1829,24 +1829,24 @@ int main(void) } /*────────────────────────────────────────────────────────────── - * Phase 5: 애플리케이션 초기화 + * Phase 5: Application init * - * - 명령 파서: BLE 수신 명령 처리 (로그, BLE 전송, CRC) - * - 피에조 드라이버: 초음파 측정용 GPIO/Timer/PPI 설정 - * - IMU(ICM42670P)는 여기서 초기화하지 않음 - * → msp? 명령 시 imu_read_direct()가 매번 자체 설정/읽기/슬립 처리 + * - Command parser: process BLE received commands (log, BLE TX, CRC) + * - Piezo driver: GPIO/Timer/PPI setup for ultrasound measurement + * - IMU (ICM42670P) is NOT initialized here + * -> imu_read_direct() self-configures/reads/sleeps on each msp? command *────────────────────────────────────────────────────────────*/ DBG_PRINTF("[5] App\r\n"); g_plat.log = log_printf; g_plat.tx_bin = dr_binary_tx_safe; g_plat.crc_check = true; g_log_enable = true; - cmd_table_init(); /* 명령 테이블을 파서에 주입 */ + cmd_table_init(); /* Inject command table into parser */ dr_piezo_init(); DBG_PRINTF(" parser/piezo OK\r\n"); /*────────────────────────────────────────────────────────────── - * Boot 완료 → 전원 버튼 상태머신 시작 + * Boot complete -> start power button state machine *────────────────────────────────────────────────────────────*/ DBG_PRINTF("\r\n========================================\r\n"); DBG_PRINTF(" READY [%s]\r\n", SERIAL_NO); @@ -1857,7 +1857,7 @@ int main(void) // MAIN LOOP for (;;) { - /* BLE 콜백에서 수신된 명령이 있으면 여기서 처리 (블로킹 방지) */ + /* Process command received in BLE callback here (prevents blocking) */ if (pending_cmd_len > 0) { uint8_t len = pending_cmd_len; pending_cmd_len = 0; diff --git a/project/ble_peripheral/ble_app_bladder_patch/main.h b/project/ble_peripheral/ble_app_bladder_patch/main.h index b637582..d694fd5 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/main.h +++ b/project/ble_peripheral/ble_app_bladder_patch/main.h @@ -3,43 +3,43 @@ * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 - * @brief VesiScan BASIC 메인 헤더 파일 + * @brief VesiScan BASIC main header file * - * [시스템 개요] - * VesiScan BASIC은 nRF52840 기반 BLE 방광 모니터링 패치 디바이스이다. - * 본 헤더는 시스템 전역에서 사용하는 열거형, 함수 선언, 전역 변수를 정의한다. + * [System Overview] + * VesiScan BASIC is an nRF52840-based BLE bladder monitoring patch device. + * This header defines enums, function declarations, and global variables used system-wide. * - * [통신 방식] - * - BLE NUS (Nordic UART Service): 스마트폰 앱과 바이너리 프로토콜 통신 - * - 물리 UART (1Mbps): 디버그 및 공장 테스트용 + * [Communication] + * - BLE NUS (Nordic UART Service): binary protocol with smartphone app + * - Physical UART (1Mbps): debug and factory testing * - * [데이터 전송 흐름] - * 1. 앱/UART에서 명령 수신 → received_command_process() - * 2. 센서 데이터 수집 (배터리, 온도, IMU, 압력) - * 3. format_data() 계열 함수로 바이너리 패킷 생성 - * 4. dr_binary_tx_safe()로 CRC16 추가 후 BLE 전송 + * [Data Transmission Flow] + * 1. Receive command from app/UART -> received_command_process() + * 2. Collect sensor data (battery, temperature, IMU, pressure) + * 3. Build binary packets via format_data() family + * 4. Transmit over BLE with CRC16 appended via dr_binary_tx_safe() ******************************************************************************/ #ifndef MAIN_H__ #define MAIN_H__ /* ------------------------------------------------------------------------- -* Firmware 식별 코드 -* - VBTFW0100 = 개발(시험)용 Ver 1.00 -* - VB0FW0100 = 양산용 Ver 1.00 +* Firmware Identification Code +* - VBTFW0100 = Development (test) build Ver 1.00 +* - VB0FW0100 = Production build Ver 1.00 * * Firmware Version Update History -* - VBTFW0101 : reb+red 패킷 병합 (채널당 단일 패킷), 260330 jhChun -* - VBTFW0102 : LED 상태 설정 명령(msl) 추가 및 재페어링 허용 등 260331 jhChun +* - VBTFW0101 : Merged reb+red packets (single packet per channel), 260330 jhChun +* - VBTFW0102 : Added LED state command (msl) and re-pairing support, 260331 jhChun ------------------------------------------------------------------------- */ #define FIRMWARE_VERSION "VBTFW0102" /*============================================================================== - * 데이터 길이 상수 + * Data Length Constants *============================================================================*/ -#define SERIAL_NO_LENGTH 12 /* 시리얼 번호 길이 (예: "VB026030000") */ -#define HW_NO_LENGTH 12 /* 하드웨어 번호(버전) 길이 */ -#define PASSKEY_LENGTH 6 /* BLE 페어링 패스키 길이 (숫자 6자리) */ +#define SERIAL_NO_LENGTH 12 /* Serial number length (e.g. "VB026030000") */ +#define HW_NO_LENGTH 12 /* Hardware number (version) length */ +#define PASSKEY_LENGTH 6 /* BLE pairing passkey length (6 digits) */ #include #include @@ -49,128 +49,128 @@ #include "boards.h" /*============================================================================== - * 열거형 정의 + * Enum Definitions *============================================================================*/ -/* 디바이스 ON/OFF 제어용 열거형 (EEPROM, 전원 등) */ +/* Device ON/OFF control enum (EEPROM, power, etc.) */ typedef enum { - OFF = 0, /* 꺼짐 */ - ON = 1 /* 켜짐 */ + OFF = 0, /* Off */ + ON = 1 /* On */ }on_off_cont_t; -/* 명령 수신 경로 구분 (BLE 또는 UART) */ +/* Command source identifier (BLE or UART) */ typedef enum { - CMD_BLE = 0, /* BLE NUS를 통해 수신된 명령 */ - CMD_UART = 1 /* 물리 UART를 통해 수신된 명령 */ + CMD_BLE = 0, /* Command received via BLE NUS */ + CMD_UART = 1 /* Command received via physical UART */ }which_cmd_t; -/* 챔버 자동 테스트 모드 (FEATURE_CHAMBER_AUTO_TEST 활성 시) */ +/* Chamber auto-test mode (when FEATURE_CHAMBER_AUTO_TEST enabled) */ #if FEATURE_CHAMBER_AUTO_TEST typedef enum { - SIMPLE_AUTO_MODE = 0, /* 간단 자동 모드 */ - HALF_AUTO_MODE = 1, /* 반자동 모드 */ - FULL_AUTO_MODE = 2, /* 전체 자동 모드 */ - NONE_AUTO_MODE = 3 /* 자동 모드 없음 */ + SIMPLE_AUTO_MODE = 0, /* Simple auto mode */ + HALF_AUTO_MODE = 1, /* Semi-auto mode */ + FULL_AUTO_MODE = 2, /* Full auto mode */ + NONE_AUTO_MODE = 3 /* No auto mode */ }auto_meas_mode_t; #endif -/* BLE 연결 상태 */ +/* BLE connection state */ typedef enum { - BLE_DISCONNECTED_ST = 0, /* BLE 미연결 */ - BLE_CONNECTED_ST = 1 /* BLE 연결됨 */ + BLE_DISCONNECTED_ST = 0, /* BLE disconnected */ + BLE_CONNECTED_ST = 1 /* BLE connected */ }ble_status_t; /*============================================================================== - * 함수 선언 + * Function Declarations *============================================================================*/ #if FEATURE_SECURE_CONNECTION -/* BLE 광고 시작 (erase_bonds=true이면 본딩 정보 삭제 후 시작) */ +/* Start BLE advertising (if erase_bonds=true, delete bond info first) */ static void advertising_start(bool erase_bonds); #endif -/* 슬립 모드 진입: LED 표시 후 POWER_OFF_DELAY(3초) 후 전원 차단 */ +/* Enter sleep mode: show LED, then power off after POWER_OFF_DELAY (3s) */ void sleep_mode_enter(void); -/* 전원 OFF 타이머 콜백: POWER_OFF_DELAY 경과 후 실제 전원 차단 */ +/* Power-off timer callback: physically cut power after POWER_OFF_DELAY */ static void t_power_off_timeout_handler(void * p_context); -/* 디바이스 전원 OFF: LED 표시 후 타이머로 지연 전원 차단 */ +/* Device power off: show LED, then delayed power cut via timer */ void device_power_off(void); -/* 전원 제어 핸들러: POWER_HOLD 핀으로 물리적 전원 ON/OFF */ +/* Power control handler: physical power ON/OFF via POWER_HOLD pin */ static void power_control_handler(on_off_cont_t device_power_st); -/* 전원 버튼 상태머신 (타이머 콜백, 5ms 간격): - * - 짧은 눌림(<1.5초): 전원 OFF - * - 중간 눌림(1.5초~10초): 부팅 시퀀스 시작 - * - 긴 눌림(>10초): 공장 초기화 (패스키 리셋 + 전원 OFF) */ +/* Power button state machine (timer callback, 5ms interval): + * - Short press (<1.5s): power OFF + * - Medium press (1.5s~10s): start boot sequence + * - Long press (>10s): factory reset (passkey reset + power OFF) */ static void main_s(void * p_context); -/* Peer Manager 타이머 콜백: reset_status==5이면 BLE 연결 강제 해제 */ +/* Peer Manager timer callback: force BLE disconnect if reset_status==5 */ static void PM_s(void * p_context); //static void main_re(void * p_context); -/* 메인 루틴 핸들러 (레거시, 현재 미사용) */ +/* Main routine handler (legacy, currently unused) */ static void main_routine_handler(void * p_context); /*------------------------------------------------------------------------------ - * 데이터 전송 함수 + * Data Transmission Functions *----------------------------------------------------------------------------*/ -/* ASCII 텍스트 BLE 전송 ('\r'까지 전송, CRC16 자동 추가) */ +/* Send ASCII text over BLE (up to '\r', CRC16 appended automatically) */ void data_tx_handler(char const *p_data_to_send); -/* 바이너리 데이터 BLE 안전 전송 (CRC16 자동 추가, 재시도 로직 포함) - * @param ble_bin_buff 전송할 바이너리 버퍼 - * @param length 데이터 길이 (uint16_t 워드 단위, 실제 바이트 = length × 2) */ +/* Safe binary data BLE transmission (CRC16 appended, with retry logic) + * @param ble_bin_buff Binary buffer to transmit + * @param length Data length in uint16_t words (actual bytes = length x 2) */ void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length); -/* SoftDevice 호환 딜레이 (nrf_delay_ms 래퍼) */ +/* SoftDevice-compatible delay (nrf_delay_ms wrapper) */ void dr_sd_delay_ms(uint32_t ms); /*------------------------------------------------------------------------------ - * 바이너리 패킷 포맷 함수 - * 패킷 구조: [4바이트 태그][데이터][2바이트 CRC16] + * Binary Packet Format Functions + * Packet structure: [4-byte tag][data][2-byte CRC16] *----------------------------------------------------------------------------*/ -/* 단일 uint16_t 값 포맷: [tag 4B][value 2B] */ +/* Format single uint16_t value: [tag 4B][value 2B] */ void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value) ; -/* uint16_t 배열 포맷: [tag 4B][data0 2B][data1 2B]... */ +/* Format uint16_t array: [tag 4B][data0 2B][data1 2B]... */ void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length); -/* uint8_t 바이트 배열 포맷: [tag 4B][byte0][byte1]... */ +/* Format uint8_t byte array: [tag 4B][byte0][byte1]... */ void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length); -/* ASCII 문자열 포맷: [tag 4B][char0][char1]... */ +/* Format ASCII string: [tag 4B][char0][char1]... */ void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length); /*============================================================================== - * 전역 변수 (extern) + * Global Variables (extern) *============================================================================*/ -extern volatile bool data_tx_in_progress; /* BLE TX 전송 진행 중 플래그 */ -extern volatile bool ble_connection_st; /* BLE 연결 상태 (0=미연결, 1=연결) */ -extern volatile bool processing; /* 센서 데이터 처리 중 플래그 (중복 명령 방지) */ +extern volatile bool data_tx_in_progress; /* BLE TX in progress flag */ +extern volatile bool ble_connection_st; /* BLE connection state (0=disconnected, 1=connected) */ +extern volatile bool processing; /* Sensor data processing flag (prevents duplicate commands) */ -/* 2026-03-17: cmd_parse.c에서 main.c로 이동한 전역변수 */ -extern char SERIAL_NO[SERIAL_NO_LENGTH]; /* 시리얼 번호 */ -extern char HW_NO[HW_NO_LENGTH]; /* 하드웨어 번호 */ -extern char m_static_passkey[PASSKEY_LENGTH]; /* BLE 정적 패스키 */ -extern bool bond_data_delete; /* 본딩 데이터 삭제 요청 플래그 */ -extern uint32_t m_life_cycle; /* 디바이스 수명 사이클 카운터 */ -extern uint8_t resetCount; /* 통신 타임아웃 카운터 */ -extern bool info4; /* 추가 정보 포함 측정 플래그 */ -extern uint8_t m_reset_status; /* 리셋 상태 코드 */ +/* 2026-03-17: Global variables moved from cmd_parse.c to main.c */ +extern char SERIAL_NO[SERIAL_NO_LENGTH]; /* Serial number */ +extern char HW_NO[HW_NO_LENGTH]; /* Hardware number */ +extern char m_static_passkey[PASSKEY_LENGTH]; /* BLE static passkey */ +extern bool bond_data_delete; /* Bond data delete request flag */ +extern uint32_t m_life_cycle; /* Device life cycle counter */ +extern uint8_t resetCount; /* Communication timeout counter */ +extern bool info4; /* Measurement with extra info flag */ +extern uint8_t m_reset_status; /* Reset status code */ -extern uint8_t ble_bin_buffer[]; /* BLE 바이너리 응답 버퍼 */ +extern uint8_t ble_bin_buffer[]; /* BLE binary response buffer */ -/* 에러 응답 전송 */ +/* Send error response */ void param_error(const char *cmd); #endif //MAIN_H__ diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.c b/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.c index 17836a2..0a73abb 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.c +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.c @@ -167,7 +167,10 @@ dr_adc_err_t dr_adc_init(void) void dr_adc_uninit(void) { - if (!m_initialized) return; + if (!m_initialized) + { + return; + } nrfx_spim_uninit(&m_spim); @@ -187,8 +190,14 @@ bool dr_adc_is_initialized(void) dr_adc_err_t dr_adc_read_raw(uint16_t *raw_value) { - if (!m_initialized) return DR_ADC_ERR_NOT_INIT; - if (raw_value == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (!m_initialized) + { + return DR_ADC_ERR_NOT_INIT; + } + if (raw_value == NULL) + { + return DR_ADC_ERR_INVALID_PARAM; + } *raw_value = spim_read_raw(); @@ -197,12 +206,21 @@ dr_adc_err_t dr_adc_read_raw(uint16_t *raw_value) dr_adc_err_t dr_adc_read(dr_adc_result_t *result) { - if (!m_initialized) return DR_ADC_ERR_NOT_INIT; - if (result == NULL) return DR_ADC_ERR_INVALID_PARAM; - + if (!m_initialized) + { + return DR_ADC_ERR_NOT_INIT; + } + if (result == NULL) + { + return DR_ADC_ERR_INVALID_PARAM; + } + dr_adc_err_t err = dr_adc_read_raw(&result->raw); - if (err != DR_ADC_OK) return err; - + if (err != DR_ADC_OK) + { + return err; + } + result->voltage_mv = dr_adc_raw_to_mv(result->raw); return DR_ADC_OK; @@ -210,18 +228,29 @@ dr_adc_err_t dr_adc_read(dr_adc_result_t *result) dr_adc_err_t dr_adc_read_averaged(dr_adc_result_t *result, uint16_t num_samples) { - if (!m_initialized) return DR_ADC_ERR_NOT_INIT; - if (result == NULL) return DR_ADC_ERR_INVALID_PARAM; - if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + if (!m_initialized) + { + return DR_ADC_ERR_NOT_INIT; + } + if (result == NULL) + { return DR_ADC_ERR_INVALID_PARAM; - + } + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + { + return DR_ADC_ERR_INVALID_PARAM; + } + uint32_t sum = 0; uint16_t raw; for (uint16_t i = 0; i < num_samples; i++) { dr_adc_err_t err = dr_adc_read_raw(&raw); - if (err != DR_ADC_OK) return err; + if (err != DR_ADC_OK) + { + return err; + } sum += raw; } @@ -243,11 +272,18 @@ dr_adc_err_t dr_adc_capture_echo(uint16_t *buffer, uint16_t num_samples) uint16_t i; NRF_SPIM_Type *spim = m_spim.p_reg; - if (!m_initialized) return DR_ADC_ERR_NOT_INIT; - if (buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; - if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + if (!m_initialized) + { + return DR_ADC_ERR_NOT_INIT; + } + if (buffer == NULL) + { return DR_ADC_ERR_INVALID_PARAM; - + } + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + { + return DR_ADC_ERR_INVALID_PARAM; + } spim->RXD.MAXCNT = 2; for (i = 0; i < num_samples; i++) @@ -268,8 +304,14 @@ dr_adc_err_t dr_adc_capture_echo(uint16_t *buffer, uint16_t num_samples) dr_adc_err_t dr_adc_measure_echo(dr_adc_echo_t *echo, const dr_adc_echo_config_t *config) { - if (!m_initialized) return DR_ADC_ERR_NOT_INIT; - if (echo == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (!m_initialized) + { + return DR_ADC_ERR_NOT_INIT; + } + if (echo == NULL) + { + return DR_ADC_ERR_INVALID_PARAM; + } /* Use default config if not provided */ dr_adc_echo_config_t cfg; @@ -286,8 +328,9 @@ dr_adc_err_t dr_adc_measure_echo(dr_adc_echo_t *echo, const dr_adc_echo_config_t /* Validate config */ if (cfg.num_samples == 0 || cfg.num_samples > DR_ADC_ECHO_SAMPLES_MAX) + { return DR_ADC_ERR_INVALID_PARAM; - + } /* Use module-level buffer (16KB too large for stack) */ /* Optional delay before capture */ @@ -298,7 +341,10 @@ dr_adc_err_t dr_adc_measure_echo(dr_adc_echo_t *echo, const dr_adc_echo_config_t /* Capture echo samples into module-level buffer */ dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, cfg.num_samples); - if (err != DR_ADC_OK) return err; + if (err != DR_ADC_OK) + { + return err; + } /* Analyze captured data */ return dr_adc_analyze_echo(m_echo_buffer, cfg.num_samples, echo, cfg.threshold_raw); @@ -309,9 +355,14 @@ dr_adc_err_t dr_adc_burst_and_capture(uint8_t cycles, uint16_t delay_us, { (void)cycles; /* Not used - ADC test only */ - if (echo == NULL) return DR_ADC_ERR_INVALID_PARAM; - if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + if (echo == NULL) + { return DR_ADC_ERR_INVALID_PARAM; + } + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + { + return DR_ADC_ERR_INVALID_PARAM; + } /* Initialize echo structure */ echo->peak_raw = 0; @@ -325,7 +376,10 @@ dr_adc_err_t dr_adc_burst_and_capture(uint8_t cycles, uint16_t delay_us, if (!m_initialized) { dr_adc_err_t init_err = dr_adc_init(); - if (init_err != DR_ADC_OK) return init_err; + if (init_err != DR_ADC_OK) + { + return init_err; + } } /* Settling time for ADC power */ @@ -339,7 +393,10 @@ dr_adc_err_t dr_adc_burst_and_capture(uint8_t cycles, uint16_t delay_us, /* Capture ADC samples into module-level buffer */ dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); - if (err != DR_ADC_OK) return err; + if (err != DR_ADC_OK) + { + return err; + } /* Analyze captured data */ return dr_adc_analyze_echo(m_echo_buffer, num_samples, echo, 100); @@ -353,9 +410,15 @@ const uint16_t* dr_adc_get_echo_buffer(void) dr_adc_err_t dr_adc_analyze_echo(const uint16_t *buffer, uint16_t num_samples, dr_adc_echo_t *echo, uint16_t threshold) { - if (buffer == NULL || echo == NULL) return DR_ADC_ERR_INVALID_PARAM; - if (num_samples == 0) return DR_ADC_ERR_INVALID_PARAM; - + if (buffer == NULL || echo == NULL) + { + return DR_ADC_ERR_INVALID_PARAM; + } + if (num_samples == 0) + { + return DR_ADC_ERR_INVALID_PARAM; + } + /* Calculate baseline from first few samples */ uint16_t baseline_samples = (num_samples > 8) ? 8 : num_samples; echo->baseline_raw = dr_adc_calc_baseline(buffer, baseline_samples); @@ -371,8 +434,7 @@ dr_adc_err_t dr_adc_analyze_echo(const uint16_t *buffer, uint16_t num_samples, echo->num_samples = num_samples; /* Check if valid echo detected */ - if (echo->peak_raw < threshold || - echo->peak_raw <= echo->baseline_raw) + if (echo->peak_raw < threshold || echo->peak_raw <= echo->baseline_raw) { return DR_ADC_ERR_NO_ECHO; } @@ -380,8 +442,7 @@ dr_adc_err_t dr_adc_analyze_echo(const uint16_t *buffer, uint16_t num_samples, return DR_ADC_OK; } -void dr_adc_find_peak(const uint16_t *buffer, uint16_t num_samples, - uint16_t *peak_value, uint16_t *peak_index) +void dr_adc_find_peak(const uint16_t *buffer, uint16_t num_samples, uint16_t *peak_value, uint16_t *peak_index) { /* * Posterior wall detection for bladder measurement @@ -403,8 +464,10 @@ void dr_adc_find_peak(const uint16_t *buffer, uint16_t num_samples, { uint16_t val = buffer[i]; /* Skip abnormally high values (noise/saturation) */ - if (val > MAX_VALID_VALUE) continue; - + if (val > MAX_VALID_VALUE) + { + continue; + } if (val > max_val) { max_val = val; @@ -412,14 +475,23 @@ void dr_adc_find_peak(const uint16_t *buffer, uint16_t num_samples, } } - if (peak_value != NULL) *peak_value = max_val; - if (peak_index != NULL) *peak_index = max_idx; + if (peak_value != NULL) + { + *peak_value = max_val; + } + if (peak_index != NULL) + { + *peak_index = max_idx; + } } uint16_t dr_adc_calc_baseline(const uint16_t *buffer, uint16_t num_samples) { - if (buffer == NULL || num_samples == 0) return 0; - + if (buffer == NULL || num_samples == 0) + { + return 0; + } + uint32_t sum = 0; for (uint16_t i = 0; i < num_samples; i++) { @@ -484,7 +556,10 @@ bool dr_adc_test(void) void dr_adc_print_buffer(const uint16_t *buffer, uint16_t num_samples) { #ifdef DEBUG_ADC - if (buffer == NULL || num_samples == 0) return; + if (buffer == NULL || num_samples == 0) + { + return; + } ADC_LOG("Echo buffer (%d samples):", num_samples); @@ -560,30 +635,50 @@ extern void dr_piezo_select_channel(uint8_t channel); /** * @brief Integrated burst + capture + BLE transmit (16-bit raw data) * - * reb+red merged protocol: header와 데이터를 첫 패킷에 합침 + * reb+red merged protocol: header and data merged into first packet * * Response format: * Packet 1 (reb:): num_samples(2) + raw_data(up to 238 bytes = 119 samples) - * Packet 2~N (red:): pkt_idx(2) + raw_data(up to 238 bytes) — 119샘플 초과 시만 - * Final (raa:): status(2) — skip_raa=0 일 때만 + * Packet 2~N (red:): pkt_idx(2) + raw_data(up to 238 bytes) — only if > 119 samples + * Final (raa:): status(2) — only when skip_raa=0 * - * With 100 samples: 200 bytes = reb:(6+200) = 206 bytes (단일 패킷) + * With 100 samples: 200 bytes = reb:(6+200) = 206 bytes (single packet) */ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_us, uint16_t num_samples, uint8_t cycles, uint16_t averaging, uint8_t piezo_ch, uint8_t *ble_buffer, uint8_t skip_raa) { - if (ble_buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; - if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + if (ble_buffer == NULL) + { return DR_ADC_ERR_INVALID_PARAM; - if (freq_option > 9) freq_option = 0; /* Invalid -> default 1.8MHz */ - if (cycles < 3 || cycles > 7) cycles = 5; /* Valid range: 3~7, default 5 */ - if (averaging == 0) averaging = 1; /* Minimum 1 */ - if (averaging > 1000) averaging = 1000; /* Maximum 1000 */ - if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; /* 채널 범위 검증 */ + } + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + { + return DR_ADC_ERR_INVALID_PARAM; + } + if (freq_option > 9) + { + freq_option = 0; /* Invalid -> default 1.8MHz */ + } + if (cycles < 3 || cycles > 7) + { + cycles = 5; /* Valid range: 3~7, default 5 */ + } + if (averaging == 0) + { + averaging = 1; /* Minimum 1 */ + } + if (averaging > 1000) + { + averaging = 1000; /* Maximum 1000 */ + } + if (piezo_ch >= MAA_NUM_CHANNELS) + { + piezo_ch = 0; /* clamp channel range */ + } - /* echo 분석은 PC에서 raw 데이터로 직접 수행 */ + /* echo analysis is done on the PC from raw data */ /* Accumulator buffer for averaging (32-bit to prevent overflow) */ static uint32_t accum_buffer[DR_ADC_ECHO_SAMPLES_MAX]; @@ -594,7 +689,8 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u if (!m_initialized) { dr_adc_err_t init_err = dr_adc_init(); - if (init_err != DR_ADC_OK) { + if (init_err != DR_ADC_OK) + { return init_err; } } @@ -605,15 +701,17 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u /*--- Step 2: Select piezo channel ---*/ dr_piezo_select_channel(piezo_ch); - /* MUX 전환 후 S/H 커패시터 안정화를 위한 dummy read */ + /* dummy read after MUX switch to settle S/H capacitor */ (void)spim_read_raw(); /*--- Step 3~5: Burst + Delay + Capture ---*/ - if (averaging == 1) { + if (averaging == 1) + { /*=== SINGLE MEASUREMENT (no averaging) - direct path ===*/ /* Execute piezo burst based on frequency option */ - switch (freq_option) { + switch (freq_option) + { case 0: default: dr_piezo_burst_sw_18mhz(cycles); /* 1.8MHz (default) */ @@ -636,25 +734,32 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u } /* Delay before capture (configurable, default 20us) */ - if (delay_us > 0) { + if (delay_us > 0) + { nrf_delay_us(delay_us); } /* Capture ADC samples directly to m_echo_buffer */ dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); - if (err != DR_ADC_OK) return err; + if (err != DR_ADC_OK) + { + return err; + } //ADC_LOG("mec: single measurement (no averaging)"); } - else { + else + { /*=== MULTIPLE MEASUREMENTS (with averaging) ===*/ /* Note: accum_buffer already cleared by memset at function start */ - for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++) { + for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++) + { /* Wait for previous echo to decay before next measurement * 1ms = ~77cm round-trip decay time (sound speed 1.54mm/us) * Skip delay on first iteration */ - if (avg_iter > 0) { + if (avg_iter > 0) + { nrf_delay_us(500); /* 500us between measurements */ } @@ -663,7 +768,8 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u dr_piezo_select_channel(piezo_ch); /* Execute piezo burst based on frequency option */ - switch (freq_option) { + switch (freq_option) + { case 0: default: dr_piezo_burst_sw_18mhz(cycles); /* 1.8MHz (default) */ @@ -686,22 +792,28 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u } /* Delay before capture (configurable, default 20us) */ - if (delay_us > 0) { + if (delay_us > 0) + { nrf_delay_us(delay_us); } /* Capture ADC samples */ dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); - if (err != DR_ADC_OK) return err; + if (err != DR_ADC_OK) + { + return err; + } /* Accumulate samples */ - for (uint16_t i = 0; i < num_samples; i++) { + for (uint16_t i = 0; i < num_samples; i++) + { accum_buffer[i] += m_echo_buffer[i]; } } /* Calculate average and store back to m_echo_buffer */ - for (uint16_t i = 0; i < num_samples; i++) { + for (uint16_t i = 0; i < num_samples; i++) + { m_echo_buffer[i] = (uint16_t)(accum_buffer[i] / averaging); } @@ -722,7 +834,8 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u uint16_t dst_idx = BLE_REB_HEADER_LEN; uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes; - while (src_idx < num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) { + while (src_idx < num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) + { uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF; ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); @@ -732,7 +845,8 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u /* Continuation packets (red:) — only if data didn't fit in reb: */ uint16_t pkt = 0; - while (src_idx < num_samples) { + while (src_idx < num_samples) + { dr_sd_delay_ms(BLE_PACKET_DELAY_MS); ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'd'; ble_buffer[3] = ':'; @@ -740,7 +854,8 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u ble_buffer[5] = (uint8_t)(pkt & 0xFF); dst_idx = BLE_RED_HEADER_LEN; - while (src_idx < num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) { + while (src_idx < num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) + { uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF; ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); @@ -751,7 +866,8 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u } /* raa: completion — skip_raa: 0=send (mec), 1=skip (maa - caller sends final raa) */ - if (!skip_raa) { + if (!skip_raa) + { dr_sd_delay_ms(BLE_PACKET_DELAY_MS); ble_buffer[0] = 'r'; ble_buffer[1] = 'a'; ble_buffer[2] = 'a'; ble_buffer[3] = ':'; ble_buffer[4] = 0x00; @@ -774,14 +890,34 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us, uint16_t averaging, uint8_t piezo_ch, dr_maa_channel_t *out_channel) { - if (out_channel == NULL) return DR_ADC_ERR_INVALID_PARAM; - if (num_samples == 0 || num_samples > MAA_SAMPLES_MAX) + if (out_channel == NULL) + { return DR_ADC_ERR_INVALID_PARAM; - if (freq_option > 3) freq_option = 0; - if (cycles < 3 || cycles > 7) cycles = 5; - if (averaging == 0) averaging = 1; - if (averaging > 1000) averaging = 1000; - if (piezo_ch > (MAA_NUM_CHANNELS - 1)) piezo_ch = 0; + } + if (num_samples == 0 || num_samples > MAA_SAMPLES_MAX) + { + return DR_ADC_ERR_INVALID_PARAM; + } + if (freq_option > 3) + { + freq_option = 0; + } + if (cycles < 3 || cycles > 7) + { + cycles = 5; + } + if (averaging == 0) + { + averaging = 1; + } + if (averaging > 1000) + { + averaging = 1000; + } + if (piezo_ch > (MAA_NUM_CHANNELS - 1)) + { + piezo_ch = 0; + } /* Accumulator buffer for averaging */ static uint32_t accum_buffer[MAA_SAMPLES_MAX]; @@ -790,22 +926,26 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us, if (!m_initialized) { dr_adc_err_t init_err = dr_adc_init(); - if (init_err != DR_ADC_OK) return init_err; + if (init_err != DR_ADC_OK) + { + return init_err; + } } //nrf_delay_us(100); - /* 채널 선택(MUX) : 1.3ms */ + /* channel select (MUX) : ~1.3 ms */ dr_piezo_select_channel(piezo_ch); - /* MUX 전환 후 S/H 커패시터 안정화를 위한 dummy read */ + /* dummy read after MUX switch to settle S/H capacitor */ (void)spim_read_raw(); - /* 채널당 반복 측정 횟수만큼 */ + /* repeat measurement 'averaging' times per channel */ for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++) { - /* TX 펄스 출력(Burst) */ - switch (freq_option) { + /* TX burst pulse */ + switch (freq_option) + { case 0: default: dr_piezo_burst_sw_18mhz(cycles); @@ -821,16 +961,19 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us, break; } - /* TX 펄스 출력 후 ADC 시작 시까지 대기 시간 */ + /* delay from TX burst to ADC start */ if (delay_us > 0) { nrf_delay_us(delay_us); } - /* 측정 ADC 샘플 수만큼 캡처 */ + /* capture num_samples ADC readings */ dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); - if (err != DR_ADC_OK) return err; + if (err != DR_ADC_OK) + { + return err; + } /* Accumulate */ for (uint16_t i = 0; i < num_samples; i++) @@ -846,7 +989,7 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us, } out_channel->num_samples = num_samples; - /* peak/baseline 분석은 PC에서 raw 데이터로 직접 수행 */ + /* peak/baseline analysis is done on the PC from raw data */ return DR_ADC_OK; } @@ -855,7 +998,8 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us, dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data, uint8_t *ble_buffer) { - if (ch_data == NULL || ble_buffer == NULL) { + if (ch_data == NULL || ble_buffer == NULL) + { return DR_ADC_ERR_INVALID_PARAM; } @@ -869,7 +1013,8 @@ dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data, uint16_t dst_idx = BLE_REB_HEADER_LEN; uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes; - while (src_idx < ch_data->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) { + while (src_idx < ch_data->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) + { uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF; ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); @@ -879,7 +1024,8 @@ dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data, /* Continuation packets (red:) — only if data didn't fit in reb: */ uint16_t pkt = 0; - while (src_idx < ch_data->num_samples) { + while (src_idx < ch_data->num_samples) + { dr_sd_delay_ms(BLE_PACKET_DELAY_MS); ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'd'; ble_buffer[3] = ':'; @@ -887,7 +1033,8 @@ dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data, ble_buffer[5] = (uint8_t)(pkt & 0xFF); dst_idx = BLE_RED_HEADER_LEN; - while (src_idx < ch_data->num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) { + while (src_idx < ch_data->num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) + { uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF; ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); @@ -909,8 +1056,11 @@ dr_adc_err_t dr_adc_delta_compress(const uint16_t *samples, uint16_t num_samples uint8_t *out_buffer, uint16_t *out_size) { if (samples == NULL || out_buffer == NULL || out_size == NULL) + { return DR_ADC_ERR_INVALID_PARAM; - if (num_samples == 0) { + } + if (num_samples == 0) + { *out_size = 0; return DR_ADC_OK; } @@ -922,13 +1072,17 @@ dr_adc_err_t dr_adc_delta_compress(const uint16_t *samples, uint16_t num_samples out_buffer[idx++] = (uint8_t)(samples[0] & 0xFF); /* Delta encode remaining samples */ - for (uint16_t i = 1; i < num_samples; i++) { + for (uint16_t i = 1; i < num_samples; i++) + { int16_t delta = (int16_t)samples[i] - (int16_t)samples[i-1]; - if (delta >= -127 && delta <= 127) { + if (delta >= -127 && delta <= 127) + { /* Normal delta: fits in signed 8-bit */ out_buffer[idx++] = (int8_t)delta; - } else { + } + else + { /* Out of range: escape + 16-bit raw value */ out_buffer[idx++] = DELTA_ESCAPE_BYTE; out_buffer[idx++] = (uint8_t)(samples[i] >> 8); @@ -944,7 +1098,10 @@ dr_adc_err_t dr_adc_delta_compress(const uint16_t *samples, uint16_t num_samples dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data, uint8_t *ble_buffer) { - if (ch_data == NULL || ble_buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (ch_data == NULL || ble_buffer == NULL) + { + return DR_ADC_ERR_INVALID_PARAM; + } /* Compress data first */ static uint8_t delta_buffer[400]; /* Worst case: 140*3 = 420 bytes */ @@ -952,7 +1109,10 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data, dr_adc_err_t err = dr_adc_delta_compress(ch_data->samples, ch_data->num_samples, delta_buffer, &compressed_size); - if (err != DR_ADC_OK) return err; + if (err != DR_ADC_OK) + { + return err; + } ADC_LOG("maa delta: %u samples -> %u bytes (%.0f%%)", ch_data->num_samples, compressed_size, @@ -971,17 +1131,22 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data, uint16_t dst_idx = BLE_RDB_HEADER_LEN; uint16_t src_idx = 0; uint16_t first_chunk = (compressed_size > rdb_data_cap) ? rdb_data_cap : compressed_size; - while (src_idx < first_chunk) { + while (src_idx < first_chunk) + { ble_buffer[dst_idx++] = delta_buffer[src_idx++]; } /* Pad to word boundary if needed */ - if (dst_idx & 1) ble_buffer[dst_idx++] = 0; + if (dst_idx & 1) + { + ble_buffer[dst_idx++] = 0; + } dr_binary_tx_safe(ble_buffer, dst_idx / 2); /* Continuation packets (rdd:) — only if data didn't fit */ uint16_t pkt = 0; - while (src_idx < compressed_size) { + while (src_idx < compressed_size) + { dr_sd_delay_ms(BLE_PACKET_DELAY_MS); ble_buffer[0] = 'r'; ble_buffer[1] = 'd'; ble_buffer[2] = 'd'; ble_buffer[3] = ':'; @@ -989,10 +1154,14 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data, ble_buffer[5] = (uint8_t)(pkt & 0xFF); dst_idx = BLE_RED_HEADER_LEN; - while (src_idx < compressed_size && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) { + while (src_idx < compressed_size && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) + { ble_buffer[dst_idx++] = delta_buffer[src_idx++]; } - if (dst_idx & 1) ble_buffer[dst_idx++] = 0; + if (dst_idx & 1) + { + ble_buffer[dst_idx++] = 0; + } dr_binary_tx_safe(ble_buffer, dst_idx / 2); pkt++; @@ -1012,14 +1181,17 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data, /* Global async context */ static maa_async_ctx_t g_maa_ctx = { .state = MAA_ASYNC_IDLE }; -/* (version marker 제거 — reb+red merged protocol) */ +/* (reb+red merged protocol) */ /** * @brief Capture one channel (internal helper) */ static dr_adc_err_t maa_async_capture_channel(uint8_t ch) { - if (ch > (MAA_NUM_CHANNELS - 1)) return DR_ADC_ERR_INVALID_PARAM; + if (ch > (MAA_NUM_CHANNELS - 1)) + { + return DR_ADC_ERR_INVALID_PARAM; + } dr_adc_err_t err = dr_adc_capture_channel_only( g_maa_ctx.freq_option, @@ -1038,7 +1210,7 @@ static dr_adc_err_t maa_async_capture_channel(uint8_t ch) * @brief Send reb: header + data merged for current channel * * reb: tag(4) + num_samples(2) + data(up to 238 bytes) - * 100샘플(200B) 이하면 이 패킷 하나로 채널 전송 완료 + * If <= 100 samples (200B), the channel completes in this single packet */ static void maa_async_send_header(void) { @@ -1055,22 +1227,26 @@ static void maa_async_send_header(void) uint16_t dst_idx = BLE_REB_HEADER_LEN; uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes; uint16_t src_idx = 0; - while (src_idx < ch->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) { + while (src_idx < ch->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) + { uint16_t sample = ch->samples[src_idx++]; buf[dst_idx++] = (uint8_t)(sample >> 8); buf[dst_idx++] = (uint8_t)(sample & 0xFF); } dr_binary_tx_safe(buf, dst_idx / 2); - dr_sd_delay_ms(5); /* dr_binary_tx_safe 내부 재시도(40ms)로 TX 완료 보장, 최소 딜레이만 */ + dr_sd_delay_ms(5); /* minimal delay; dr_binary_tx_safe retries internally (40 ms) */ g_maa_ctx.current_pkt = 0; - g_maa_ctx.data_offset = src_idx * 2; /* 이미 전송한 바이트 수 */ + g_maa_ctx.data_offset = src_idx * 2; /* bytes already sent */ - /* 데이터가 첫 패킷에 다 들어갔으면 바로 다음 채널로 */ - if (g_maa_ctx.data_offset >= total_data_bytes) { - g_maa_ctx.state = MAA_ASYNC_TX_DATA; /* send_data_packet()에서 false 반환 → 다음 채널 */ - } else { + /* if all data fit in the first packet, advance to next channel */ + if (g_maa_ctx.data_offset >= total_data_bytes) + { + g_maa_ctx.state = MAA_ASYNC_TX_DATA; /* send_data_packet() returns false -> next channel */ + } + else + { g_maa_ctx.state = MAA_ASYNC_TX_DATA; } } @@ -1138,17 +1314,17 @@ static void maa_async_send_completion(uint16_t status) //if (g_plat.log) g_plat.log("-------------------------------------------------------------------------------------\r\n"); - /* 캡처 완료 → Piezo TX/RX 전원 OFF */ + /* capture complete -> Piezo TX/RX power OFF */ dr_piezo_power_off(); g_maa_ctx.state = MAA_ASYNC_IDLE; //ADC_LOG("maa_async: complete, status=0x%04X", status); - /* 완료 콜백 호출 (mbb? 등에서 센서 측정 체인 트리거용) */ + /* completion callback (used by mbb? to trigger sensor chain) */ if (g_maa_ctx.on_complete_cb) { void (*cb)(void) = g_maa_ctx.on_complete_cb; - g_maa_ctx.on_complete_cb = NULL; /* 1회성: 재호출 방지 */ + g_maa_ctx.on_complete_cb = NULL; /* one-shot: prevent re-entry */ cb(); } } @@ -1168,9 +1344,14 @@ dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us, uint16_t nu return DR_ADC_ERR_NOT_INIT; /* Already running */ } - if (ble_buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; - if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + if (ble_buffer == NULL) + { return DR_ADC_ERR_INVALID_PARAM; + } + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + { + return DR_ADC_ERR_INVALID_PARAM; + } /* Initialize context */ g_maa_ctx.freq_option = freq_option; @@ -1182,9 +1363,9 @@ dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us, uint16_t nu g_maa_ctx.current_ch = 0; g_maa_ctx.current_pkt = 0; g_maa_ctx.data_offset = 0; - g_maa_ctx.on_complete_cb = NULL; /* 기본: 콜백 없음 (maa?) */ + g_maa_ctx.on_complete_cb = NULL; /* default: no callback (maa?) */ - /* 전채널 캡처: BLE 전송 없이 6채널 전부 캡처 완료 후 TX 시작 */ + /* capture all channels without BLE TX, then start transmission */ g_maa_ctx.state = MAA_ASYNC_CAPTURING; for (ch = 0; ch < MAA_NUM_CHANNELS; ch++) @@ -1192,16 +1373,19 @@ dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us, uint16_t nu err = maa_async_capture_channel(ch); if (err != DR_ADC_OK) { - if (g_plat.log) g_plat.log("[maa] maa_async_start: CH%u capture failed (%d)", ch, err); + if (g_plat.log) + { + g_plat.log("[maa] maa_async_start: CH%u capture failed (%d)", ch, err); + } maa_async_send_completion(0xFFF0 | ch); return err; } } - /* peak 로그와 raw 덤프 사이 구분 */ + /* separator between peak log and raw dump */ //if (g_plat.log) g_plat.log("\r\n"); - /* 캡처 완료 → Piezo TX/RX 전원 OFF (BLE 전송 중 불필요) */ + /* capture done -> Piezo TX/RX power OFF (not needed during BLE TX) */ dr_piezo_power_off(); /* Send CH0 header - this will trigger TX_RDY for subsequent packets */ @@ -1217,7 +1401,8 @@ bool maa_async_on_tx_ready(void) return false; } - switch (g_maa_ctx.state) { + switch (g_maa_ctx.state) + { case MAA_ASYNC_TX_DATA: /* Send next data packet */ if (!maa_async_send_data_packet()) @@ -1234,7 +1419,7 @@ bool maa_async_on_tx_ready(void) } else { - /* 이미 전채널 캡처됨, 바로 헤더 전송 */ + /* all channels already captured, send header directly */ g_maa_ctx.state = MAA_ASYNC_CAPTURING; maa_async_send_header(); } @@ -1274,16 +1459,18 @@ maa_async_state_t maa_async_get_state(void) } /*============================================================================== - * 비동기 측정 상태 IDLE로 초기화 - BLE 연결이 끊어지는 경우 먹통 현상 방지 + * maa_async_abort - Reset async state to IDLE + * + * Prevents hang when BLE disconnects mid-measurement. *============================================================================*/ void maa_async_abort(void) { if (g_maa_ctx.state != MAA_ASYNC_IDLE) { ADC_LOG("maa_async_abort: aborting from state %d", g_maa_ctx.state); - g_maa_ctx.state = MAA_ASYNC_IDLE; // 측정 상태 IDLE로 초기화 - g_maa_ctx.on_complete_cb = NULL; // abort 후 콜백 호출 방지 - dr_piezo_power_off(); // 전체 측정 중간에 끊기는 경우 Piezo TX/RX Sleep + g_maa_ctx.state = MAA_ASYNC_IDLE; + g_maa_ctx.on_complete_cb = NULL; + dr_piezo_power_off(); } } diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.h b/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.h index e75ba8a..3ef28ed 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.h +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.h @@ -450,8 +450,8 @@ typedef struct { uint16_t averaging; /**< Averaging count */ uint8_t *ble_buffer; /**< Working buffer for BLE packets */ dr_maa_channel_t channels[MAA_NUM_CHANNELS]; /**< Captured data for each channel */ - bool pre_capture_all; /**< true: 전채널 캡처 완료 후 일괄 전송 (mbb용) */ - void (*on_complete_cb)(void); /**< 비동기 캡처 완료 후 호출될 콜백 (NULL이면 미사용) */ + bool pre_capture_all; /**< true: capture all channels before transmitting (mbb) */ + void (*on_complete_cb)(void); /**< callback after async capture completes (NULL = none) */ } maa_async_ctx_t; /** @@ -500,15 +500,14 @@ maa_async_state_t maa_async_get_state(void); void maa_async_abort(void); /** - * @brief 자동 전원 플래그 설정 (완료 후 자동 power off) + * @brief Set auto power-off flag (power off after completion) */ void maa_async_set_auto_power(bool on); void maa_async_set_pre_capture_all(bool on); - /** - * @brief 비동기 캡처 완료 콜백 설정 - * raa: 전송 + 전원 OFF 이후 호출된다. NULL이면 콜백 없음. + * @brief Set async capture completion callback + * Called after raa: is transmitted and power-off. NULL = no callback. */ void maa_async_set_on_complete(void (*cb)(void)); diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/battery/battery_saadc.c b/project/ble_peripheral/ble_app_bladder_patch/measurement/battery/battery_saadc.c index 5f1dbe6..2f948ae 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/battery/battery_saadc.c +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/battery/battery_saadc.c @@ -1,25 +1,15 @@ -/******************************************************************************* - * @file battery_saadc.c - * @author CandyPops Co. - * @version V1.0.0 - * @date 2022-09-05 - * @brief - ******************************************************************************/ - -/******************************************************************************* - * [모듈 개요] 배터리 전압 ADC 측정 모듈 +/*============================================================================== + * battery_saadc.c - Battery voltage ADC measurement * - * nRF52840의 SAADC(Successive Approximation ADC)를 사용하여 배터리 전압을 측정: - * - AIN2 채널, 10bit 해상도 - * - 5초 주기 타이머(battery_loop)로 반복 측정 - * - 저전압(3500mV 이하) 10회 연속 감지 시 자동 전원 OFF - * - info4 모드(전체 센서 수집)에서는 info_batt에 저장 후 온도 측정으로 전환 + * Measures battery voltage via nRF52840 SAADC on AIN2: + * - 12-bit resolution, 4x oversampling + * - Periodic monitoring via battery_loop timer (60 s interval) + * - Auto power-off after 10 consecutive readings below 3500 mV + * - In info4 mode (bulk sensor collection): stores to info_batt * - * 배터리 전압 변환 공식: - * 전압(mV) = ADC값 x (600mV / 1023) x 6 x 1.42 (분압 저항 보정 계수) - * - * info4 모드 순서: 배터리 -> 온도(go_temp) -> IMU(motion_raw_data_enabled) - ******************************************************************************/ + * Voltage conversion: + * mV = ADC_VALUE * (600 / 4095) * 6 * 1.42 (resistor divider correction) + *============================================================================*/ #include "sdk_common.h" @@ -34,263 +24,214 @@ #include "nrf_log.h" #include "main.h" #include "app_timer.h" -//#include "fstorage.h" #include "battery_saadc.h" #include "main_timer.h" #include "main.h" #include "debug_print.h" -/* SAADC 내부 기준전압 600mV (부동소수점) */ -#define BATTERY_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */ +/* SAADC internal reference voltage (mV, float) */ +#define BATTERY_REF_VOLTAGE_IN_MILLIVOLTS 600.0f -/* 1/3 프리스케일링 보상 계수 (입력 전압을 1/3로 분압하므로 x3, 추가 x2 = 총 x6) (부동소수점) */ -#define BATTERY_PRE_SCALING_COMPENSATION 6.0f /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/ +/* 1/3 prescaling compensation (input divided by 3, then x2 = total x6) */ +#define BATTERY_PRE_SCALING_COMPENSATION 6.0f -/* 12비트 ADC 최대 디지털 값 (부동소수점) */ -#define BATTERY_ADC_RES_12BITS 4095.0f /**< Maximum digital value for 12-bit ADC conversion. */ +/* 12-bit ADC maximum digital value */ +#define BATTERY_ADC_RES_12BITS 4095.0f -/**@brief Macro to convert the result of ADC conversion in millivolts. - * - * @param[in] ADC_VALUE ADC result. - * - * @retval Result converted to millivolts. - */ +/* Convert raw ADC value to millivolts */ #define BATTERY_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\ ((((ADC_VALUE) * BATTERY_REF_VOLTAGE_IN_MILLIVOLTS) / BATTERY_ADC_RES_12BITS) * BATTERY_PRE_SCALING_COMPENSATION) -/* 배터리 측정용 싱글 버퍼 (1회 측정 후 uninit하므로 더블 버퍼 불필요) */ +/* Single ADC buffer (uninit after each measurement, no double-buffer needed) */ static nrf_saadc_value_t adc_buf; -//static nrf_saadc_value_t adc_bufs[2]; // 이전: 더블 버퍼 (연속 측정용) - -/* 배터리 모니터링 반복 타이머 정의 */ +/* Battery monitoring repeat timer */ APP_TIMER_DEF(m_battery_loop_timer_id); -/* 배터리 측정 주기: 5초 (밀리초 단위) */ +/* Battery monitoring interval (ms) */ #define BATTERY_LOOP_INTERVAL 60000 -/* 저전압 체크 플래그 — battery_loop에서 true로 설정, 핸들러에서 소비 */ +/* Low-battery check flag — set by battery_loop, consumed by handler */ bool low_battery_check = false; -/* SAADC 콜백 완료 플래그 — all_sensors()에서 배터리 측정 완료 대기용 */ +/* SAADC callback completion flag — used by all_sensors() to wait */ volatile bool battery_saadc_done = false; -/* info4: 전체 센서 데이터 수집 모드 플래그 */ -extern bool info4; // main.c - +/* info4: bulk sensor collection mode flag */ +extern bool info4; extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN]; - -/* true가 되면 main_timer에서 전원 OFF 시퀀스 실행 */ extern bool go_device_power_off; - -/* 다른 작업(IMU 등) 처리 중이면 true — 배터리 측정 스킵용 */ extern volatile bool processing; - -/* 현재 명령 소스: CMD_UART 또는 CMD_BLE */ extern which_cmd_t cmd_type_t; +extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN]; -extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ; +/* info4 mode: cached battery voltage (mV) */ + volatile uint16_t info_batt; -/* info4 모드에서 배터리 전압을 임시 저장 (mV 단위) */ - volatile uint16_t info_batt; //48_c - -/* info4 순차 측정 제어 플래그: go_batt→ go_temp → motion */ -extern bool go_temp; // -extern bool go_batt; //cmd_parse +/* info4 sequential measurement control flags */ +extern bool go_temp; +extern bool go_batt; extern bool motion_raw_data_enabled ; extern bool ble_got_new_data; extern bool motion_data_once ; -/**@brief Function for handling the ADC interrupt. - * - * @details This function will fetch the conversion result from the ADC, convert the value into - * percentage and send it to peer. - */ - -/** - * @brief 배터리 전압 ADC 완료 콜백 +/*============================================================================== + * battery_event_handler - SAADC conversion complete callback * - * SAADC 변환 완료 시 호출된다. - * ADC 값을 실제 배터리 전압(mV)으로 변환하고, 동작 모드에 따라: - * - 저전압 체크 모드: 3500mV 이하 10회 연속이면 자동 전원 OFF - * - info4 모드: info_batt에 저장 후 온도 측정(go_temp)으로 전환 - * - 일반 모드: BLE 또는 UART로 즉시 전송 - * - * 전압 변환: ADC값 x (600/1023) x 6 = 기본 전압, x 1.42 = 분압 보정 후 실제 전압 - */ + * Converts the raw ADC value to battery voltage (mV) and then: + * - Low-battery check mode: if <= 3500 mV for 10 consecutive times -> power OFF + * - info4 mode: store to info_batt (no BLE send) + * - Normal mode: send rsn: response over BLE or UART + *============================================================================*/ void battery_event_handler( nrf_drv_saadc_evt_t const * p_event ) { - /* 저전압 연속 감지 카운터 (static으로 호출 간 유지) */ - static uint8_t low_battery_cnt = 0; + static uint8_t low_battery_cnt = 0; - if (p_event->type == NRF_DRV_SAADC_EVT_DONE) - { - nrf_saadc_value_t register_val = 0; - float batt_lvl_in_milli_volt_0 = 0; /* 보정 전 전압 (부동소수점) */ - float batt_lvl_in_milli_volt_1 = 0; /* 분압 보정 후 최종 전압 (부동소수점) */ + if (p_event->type == NRF_DRV_SAADC_EVT_DONE) + { + nrf_saadc_value_t register_val = 0; + float batt_lvl_in_milli_volt_0 = 0; /* before divider correction */ + float batt_lvl_in_milli_volt_1 = 0; /* after divider correction */ - /* ADC 변환 결과 읽기 */ - register_val = p_event->data.done.p_buffer[0]; - - //err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 1); // 이전: 다음 변환을 위해 버퍼 재등록 - //APP_ERROR_CHECK(err_code); + register_val = p_event->data.done.p_buffer[0]; - /* SAADC 해제 — 다른 ADC 측정(온도, 압력)과 하드웨어 공유 */ - /* 1회 측정 후 해제이므로 buffer_convert(다음 버퍼 등록) 불필요 */ - nrf_drv_saadc_channel_uninit(0); - nrf_drv_saadc_uninit(); + /* Release SAADC — shared with temperature / pressure ADC */ + nrf_drv_saadc_channel_uninit(0); + nrf_drv_saadc_uninit(); - /* 콜백 완료 알림 (all_sensors 대기 해제용) */ - battery_saadc_done = true; + battery_saadc_done = true; - /* ADC값 → mV 변환 (매크로: ADC x 600/1023 x 6) */ - batt_lvl_in_milli_volt_0 = BATTERY_RESULT_IN_MILLI_VOLTS(register_val); - - /* 분압 저항 보정 계수 1.42 적용 → 실제 배터리 전압 */ - batt_lvl_in_milli_volt_1 = (batt_lvl_in_milli_volt_0) *1.42f; + /* ADC -> mV conversion */ + batt_lvl_in_milli_volt_0 = BATTERY_RESULT_IN_MILLI_VOLTS(register_val); - /* === 저전압 체크 모드 (battery_loop 타이머에서 설정) === */ - if(low_battery_check == true) - { - low_battery_check = false; + /* Resistor divider correction factor 1.42 */ + batt_lvl_in_milli_volt_1 = (batt_lvl_in_milli_volt_0) *1.42f; - /* 배터리 전압이 LOW_BATTERY_VOLTAGE(3500mV) 이하인지 확인 */ - if(batt_lvl_in_milli_volt_1 <= LOW_BATTERY_VOLTAGE) - { - /* 10회 연속 저전압 감지 시 전원 OFF 시퀀스 시작 */ - if(low_battery_cnt >= 10) - { - low_battery_cnt = 0; - /*go to power off and fds save */ - DBG_PRINTF("Save FDS parameters and then Power OFF\r\n"); - go_device_power_off = true; - main_timer_start(); - } - else - { - /* 아직 10회 미만 — 카운터 증가 후 경고 출력 */ - low_battery_cnt++; - DBG_PRINTF("WARNING!!! low_battery cnt = %d, Batt = %d(mV)\r\n", low_battery_cnt, (int)batt_lvl_in_milli_volt_1); - } - } - } + /* --- Low-battery check mode (set by battery_loop timer) --- */ + if(low_battery_check == true) + { + low_battery_check = false; - /* === info4 모드: 전체 센서 수집(mbb) 중 배터리 값 저장 === */ - else if (info4 == true) - { - info_batt = batt_lvl_in_milli_volt_1; - } + if(batt_lvl_in_milli_volt_1 <= LOW_BATTERY_VOLTAGE) + { + if(low_battery_cnt >= 10) + { + low_battery_cnt = 0; + DBG_PRINTF("Save FDS parameters and then Power OFF\r\n"); + go_device_power_off = true; + main_timer_start(); + } + else + { + low_battery_cnt++; + DBG_PRINTF("WARNING!!! low_battery cnt = %d, Batt = %d(mV)\r\n", low_battery_cnt, (int)batt_lvl_in_milli_volt_1); + } + } + } - /* === 일반 모드: 단독 배터리 측정 요청(msn)에 대한 응답 전송 === */ - else - { - if (cmd_type_t == CMD_UART) - { - DBG_PRINTF("Tn%d\r\n\r\n", (int)batt_lvl_in_milli_volt_1); - } - else if (cmd_type_t == CMD_BLE) - { - /* "rsn:" 헤더와 함께 배터리 전압을 바이너리로 BLE 전송 */ - single_format_data(ble_bin_buffer, "rsn:", batt_lvl_in_milli_volt_1); - dr_binary_tx_safe(ble_bin_buffer,3); - //data_tx_handler(ble_tx_buffer); - } - } - } + /* --- info4 mode: store value for mbb? bulk response --- */ + else if (info4 == true) + { + info_batt = batt_lvl_in_milli_volt_1; + } + + /* --- Normal mode: send rsn: BLE / UART response --- */ + else + { + if (cmd_type_t == CMD_UART) + { + DBG_PRINTF("Tn%d\r\n\r\n", (int)batt_lvl_in_milli_volt_1); + } + else if (cmd_type_t == CMD_BLE) + { + single_format_data(ble_bin_buffer, "rsn:", batt_lvl_in_milli_volt_1); + dr_binary_tx_safe(ble_bin_buffer,3); + } + } + } } -/** - * @brief SAADC를 배터리 전압 측정용으로 설정 +/*============================================================================== + * battery_configure - Set up SAADC for battery voltage measurement * - * AIN2 채널을 싱글엔드(SE) 모드, 1/3 프리스케일링으로 초기화한다. - * 더블 버퍼(adc_bufs[0], [1])를 등록하여 연속 측정이 가능하도록 한다. - * 콜백: battery_event_handler - */ + * AIN2, single-ended, 1/6 gain, 12-bit, 4x oversampling, burst enabled. + * Registers a single buffer (uninit after one conversion). + *============================================================================*/ static void battery_configure(void) { - /* SAADC 드라이버 초기화 (4x 오버샘플링으로 노이즈 저감) */ - nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG; - saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; // 10 -> 12bit - saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X; - ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, battery_event_handler); - if (err_code != NRF_SUCCESS) { - return; /* SAADC 사용 중 → 이번 측정 스킵, 다음 주기에 재시도 */ - } + nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG; + saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; + saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X; + ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, battery_event_handler); + if (err_code != NRF_SUCCESS) { + return; /* SAADC busy — skip this cycle, retry next */ + } - /* AIN2 채널 설정: 싱글엔드 입력, 1/6 gain, burst + TACQ 20μs */ - nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2); - config.burst = NRF_SAADC_BURST_ENABLED; - config.acq_time = NRF_SAADC_ACQTIME_10US; - err_code = nrf_drv_saadc_channel_init(0, &config); - APP_ERROR_CHECK(err_code); + nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2); + config.burst = NRF_SAADC_BURST_ENABLED; + config.acq_time = NRF_SAADC_ACQTIME_10US; + err_code = nrf_drv_saadc_channel_init(0, &config); + APP_ERROR_CHECK(err_code); - /* 싱글 버퍼 등록 (1회 측정 후 uninit) */ - err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1); - APP_ERROR_CHECK(err_code); - //err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[0], 1); // 이전: 더블 버퍼 - //APP_ERROR_CHECK(err_code); - //err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[1], 1); - //APP_ERROR_CHECK(err_code); + err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1); + APP_ERROR_CHECK(err_code); } -/** - * @brief 배터리 전압 1회 측정 시작 +/*============================================================================== + * battery_level_meas - Start a single battery voltage measurement * - * SAADC를 배터리용으로 설정 후 샘플링을 트리거한다. - * 결과는 battery_event_handler 콜백에서 비동기로 처리된다. - */ + * Configures SAADC and triggers sampling. Result arrives asynchronously + * via battery_event_handler. + *============================================================================*/ void battery_level_meas(void) { ret_code_t err_code; - battery_configure(); /* SAADC 배터리용 초기화 */ - err_code = nrf_drv_saadc_sample(); /* ADC 샘플링 트리거 (비동기) */ + battery_configure(); + err_code = nrf_drv_saadc_sample(); APP_ERROR_CHECK(err_code); } -/** - * @brief 배터리 모니터링 타이머 콜백 (5초 주기) - 주기 확인 필요(너무 짧음) +/*============================================================================== + * battery_loop - Periodic battery monitoring timer callback * - * 저전압 체크 플래그를 설정하고 배터리 측정을 시작한다. - * 다른 작업(IMU 등) 처리 중이면 측정을 건너뛴다. - */ -void battery_loop(void * p_context) /* For 1sec */ + * Sets the low-battery check flag and starts a measurement. + * Skips if another sensor (IMU / info4) is already running (SAADC conflict). + *============================================================================*/ +void battery_loop(void * p_context) { - UNUSED_PARAMETER(p_context); + UNUSED_PARAMETER(p_context); - /* 다른 센서 처리 중 또는 MBB 센서 수집 중이면 배터리 측정 스킵 (SAADC 충돌 방지) */ - if (processing == true || info4 == true) - { - processing = false ; // add 20241218 - //low_battery_check = true; - return; - } - else - { - low_battery_check = true; /* 저전압 감지 모드로 측정 */ - battery_level_meas(); /* 배터리 ADC 1회 측정 시작 */ - } + if (processing == true || info4 == true) + { + processing = false ; + return; + } + else + { + low_battery_check = true; + battery_level_meas(); + } } -/** @brief 배터리 모니터링 타이머 시작 (5초 반복) */ +/* Start the periodic battery monitoring timer. */ void battery_timer_start(void) { - APP_ERROR_CHECK(app_timer_start(m_battery_loop_timer_id, APP_TIMER_TICKS(BATTERY_LOOP_INTERVAL), NULL)); + APP_ERROR_CHECK(app_timer_start(m_battery_loop_timer_id, APP_TIMER_TICKS(BATTERY_LOOP_INTERVAL), NULL)); } -/** @brief 배터리 모니터링 타이머 정지 */ +/* Stop the battery monitoring timer. */ void battery_timer_stop(void) { - APP_ERROR_CHECK(app_timer_stop(m_battery_loop_timer_id)); + APP_ERROR_CHECK(app_timer_stop(m_battery_loop_timer_id)); } -/** @brief 배터리 모니터링 타이머 초기화 (반복 모드, 콜백: battery_loop) */ +/* Initialise the battery monitoring timer (repeated mode). */ void battery_timer_init(void) { APP_ERROR_CHECK(app_timer_create(&m_battery_loop_timer_id, APP_TIMER_MODE_REPEATED, battery_loop)); } - diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/battery/battery_saadc.h b/project/ble_peripheral/ble_app_bladder_patch/measurement/battery/battery_saadc.h index 6085fdc..dd6ece1 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/battery/battery_saadc.h +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/battery/battery_saadc.h @@ -1,41 +1,32 @@ -/******************************************************************************* - * @file battery_saadc.h - * @author CandyPops Co. - * @version V1.0.0 - * @date 2022-09-05 - * @brief - ******************************************************************************/ - -/******************************************************************************* - * [헤더 개요] 배터리 전압 SAADC 측정 인터페이스 +/*============================================================================== + * battery_saadc.h - Battery voltage SAADC measurement interface * - * nRF52840 SAADC를 이용한 배터리 전압 측정 API를 선언한다. + * Uses the nRF52840 SAADC to measure battery voltage on AIN2. * - * 주요 API: - * - battery_level_meas() : 배터리 전압 1회 측정 (AIN2) - * - battery_timer_init/start/stop() : 5초 주기 배터리 모니터링 타이머 제어 + * API: + * battery_level_meas() : one-shot measurement (async, result via callback) + * battery_timer_init/start/stop() : 5-second periodic monitoring timer * - * LOW_BATTERY_VOLTAGE(3500mV) 이하가 10회 연속 감지되면 자동 전원 OFF - ******************************************************************************/ + * Auto power-off is triggered after 10 consecutive readings below + * LOW_BATTERY_VOLTAGE (3500 mV). + *============================================================================*/ #ifndef _BATTERY_SAADC_H_ #define _BATTERY_SAADC_H_ -/* 저전압 판정 임계값 (mV) — 이 값 이하가 10회 연속이면 자동 전원 OFF */ -#define LOW_BATTERY_VOLTAGE 3500 /* Low Battery 임계값 */ +/* Low-battery threshold (mV) — 10 consecutive readings below this -> power OFF */ +#define LOW_BATTERY_VOLTAGE 3500 - -/** @brief 배터리 SAADC 콜백 완료 플래그 (all_sensors 대기용) */ +/* SAADC callback completion flag (used by all_sensors() to wait) */ extern volatile bool battery_saadc_done; -/** @brief 배터리 전압 1회 측정 시작 (비동기, 결과는 콜백에서 처리) */ +/* Start a single async battery measurement. Result handled in callback. */ void battery_level_meas(void); -/** @brief 배터리 모니터링 5초 반복 타이머 시작 */ +/* Start the 5-second periodic battery monitoring timer. */ void battery_timer_start(void); -/** @brief 배터리 모니터링 타이머 정지 */ +/* Stop the battery monitoring timer. */ void battery_timer_stop(void); -/** @brief 배터리 모니터링 타이머 초기화 (앱 시작 시 1회 호출) */ +/* Initialise the battery monitoring timer (call once at app start). */ void battery_timer_init(void); #endif //_BATTERY_SAADC_H_ - diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw.c b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw.c index 3e3ce57..6b3cbde 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw.c +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw.c @@ -7,28 +7,29 @@ ******************************************************************************/ /******************************************************************************* - * [모듈 개요] ICM42670P IMU 드라이버 상위 레이어 + * [Module overview] ICM42670P IMU driver application layer * - * ICM42670P IMU 센서의 초기화, 설정, 데이터 읽기를 담당하는 애플리케이션 레이어 모듈 - * InvenSense 드라이버 API를 래핑하여 사용 + * Application layer module responsible for initialization, configuration, + * and data reading of the ICM42670P IMU sensor. Wraps InvenSense driver API. * - * 주요 기능: - * 1) setup_imu_device() - IMU 초기화 및 WHOAMI 확인 (0x67 = ICM42670P) - * 2) configure_imu_device() - 센서 파라미터 설정 - * - 가속도계: ±4g FSR, 100Hz(저전력) 또는 800Hz(저잡음) - * - 자이로: ±2000dps FSR, 100Hz 또는 800Hz - * - FIFO 비활성화 (레지스터 직접 읽기 모드) - * 3) get_imu_data() - FIFO 또는 레지스터에서 센서 데이터 읽기 - * 4) imu_callback() - 센서 데이터 수신 콜백 - * - 마운팅 매트릭스 적용 (보드 방향 보정) - * - info4 모드: info_imu[6]에 데이터 저장 - * - BLE 모드: "rsp:" 태그로 6축 데이터 BLE 전송 - * - UART 모드: 텍스트 형식으로 시리얼 출력 - * 5) imu_read_direct() - 드라이버 API를 우회한 직접 I2C 레지스터 읽기 - * - 센서 설정 → 전원 ON → 80ms 대기 → 12바이트 읽기 → 슬립 + * Key functions: + * 1) setup_imu_device() - IMU init and WHOAMI verification (0x67 = ICM42670P) + * 2) configure_imu_device() - Sensor parameter configuration + * - Accelerometer: +/-4g FSR, 100Hz (low-power) or 800Hz (low-noise) + * - Gyroscope: +/-2000dps FSR, 100Hz or 800Hz + * - FIFO disabled (direct register read mode) + * 3) get_imu_data() - Read sensor data from FIFO or registers + * 4) imu_callback() - Sensor data receive callback + * - Applies mounting matrix (board orientation correction) + * - info4 mode: stores data in info_imu[6] + * - BLE mode: sends 6-axis data via BLE with "rsp:" tag + * - UART mode: outputs text format to serial + * 5) imu_read_direct() - Direct I2C register read bypassing driver API + * - Configure sensor -> power ON -> wait 80ms -> read 12 bytes -> sleep * - * 마운팅 매트릭스: - * Q30 고정소수점 형식의 3x3 회전 매트릭스로, 보드에 장착된 센서의물리적 방향을 소프트웨어 좌표계에 맞춰 보정 + * Mounting matrix: + * 3x3 rotation matrix in Q30 fixed-point format, correcting the sensor's + * physical mounting orientation to match the software coordinate system. ******************************************************************************/ #include "sdk_config.h" @@ -47,9 +48,9 @@ /* - * 데이터 출력 형식 선택 - * 0 : 원시 데이터 (raw accel, gyro, temp) 출력 - * 1 : 스케일링된 데이터 (g, dps, 섭씨) 출력 + * Data output format selection + * 0 : Raw data output (raw accel, gyro, temp) + * 1 : Scaled data output (g, dps, Celsius) */ #define SCALED_DATA_G_DPS 0 @@ -58,20 +59,20 @@ * Static and extern variables * -------------------------------------------------------------------------------------- */ -/* IMU 드라이버 객체 — 드라이버 API 호출 시 항상 이 구조체 전달 */ +/* IMU driver object — always passed to driver API calls */ static struct inv_imu_device icm_driver; -/* BLE 전송용 바이너리 버퍼 */ +/* Binary buffer for BLE transmission */ uint8_t imu_bin_buffer[BLE_NUS_MAX_DATA_LEN]; /* - * ICM42670P 마운팅 매트릭스 (Q30 고정소수점) + * ICM42670P mounting matrix (Q30 fixed-point) * - * 센서가 보드에 장착된 물리적 방향에 따라 좌표 변환 - * Q30 형식: 1.0 = (1 << 30) = 0x40000000 + * Coordinate transform based on the sensor's physical mounting orientation. + * Q30 format: 1.0 = (1 << 30) = 0x40000000 * - * SM_REVB_DB (개발보드): X→-Y, Y→X 변환 (90도 회전) - * 기본 (SmartMotion): 단위 행렬 (변환 없음) + * SM_REVB_DB (dev board): X->-Y, Y->X transform (90-degree rotation) + * Default (SmartMotion): identity matrix (no transform) */ #if (SM_BOARD_REV == SM_REVB_DB) /* when DB or EVB are used */ static int32_t icm_mounting_matrix[9] = { 0, -(1<<30), 0, @@ -83,13 +84,13 @@ static int32_t icm_mounting_matrix[9] = {(1<<30), 0, 0, 0, 0, (1<<30)}; #endif -bool custom_add_data; /* 커스텀 데이터 추가 플래그 (BLE 전송 제어) */ -extern bool motion_raw_data_enabled; /* 외부에서 원시 데이터 읽기를 요청하는 플래그 */ -extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN]; /* BLE 텍스트 전송 버퍼 */ -extern which_cmd_t cmd_type_t; /* 현재 명령 소스 (BLE 또는 UART) */ -uint16_t ssp_data[6]={0,}; /* BLE 전송용 6축 데이터 배열 (accel XYZ + gyro XYZ) */ -extern bool info4; /* info4 모드 플래그 (cmd_parse에서 설정) */ - volatile uint16_t info_imu[6]; /* info4 모드에서 IMU 데이터를 저장하는 전역 배열 */ +bool custom_add_data; /* Custom data append flag (BLE transmission control) */ +extern bool motion_raw_data_enabled; /* Flag requesting raw data read from external module */ +extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN]; /* BLE text transmit buffer */ +extern which_cmd_t cmd_type_t; /* Current command source (BLE or UART) */ +uint16_t ssp_data[6]={0,}; /* 6-axis data array for BLE (accel XYZ + gyro XYZ) */ +extern bool info4; /* info4 mode flag (set by cmd_parse) */ + volatile uint16_t info_imu[6]; /* Global array storing IMU data in info4 mode */ /* -------------------------------------------------------------------------------------- * static function declaration @@ -102,35 +103,35 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3]); /* * setup_imu_device() - * IMU 디바이스 초기화 및 식별 확인 + * IMU device initialization and identification verification. * - * 처리 흐름: - * 1) inv_imu_init()으로 드라이버 초기화 (시리얼 인터페이스 + 콜백 등록) - * 2) WHOAMI 레지스터 읽기로 디바이스 확인 - * 3) WHOAMI 값이 ICM_WHOAMI(0x67)와 일치하는지 검증 + * Flow: + * 1) Initialize driver via inv_imu_init() (serial interface + callback registration) + * 2) Read WHOAMI register for device identification + * 3) Verify WHOAMI value matches ICM_WHOAMI (0x67) * - * 반환값: 0=성공, 음수=에러 + * Returns: 0=success, negative=error */ int setup_imu_device(struct inv_imu_serif *icm_serif) { int rc = 0; uint8_t who_am_i; - /* IMU 드라이버 초기화 — 시리얼 인터페이스 연결 및 콜백 함수 등록 */ + /* Initialize IMU driver — connect serial interface and register callback */ rc = inv_imu_init(&icm_driver, icm_serif, imu_callback); if (rc != INV_ERROR_SUCCESS) { DBG_PRINTF("!!! ERROR : Failed to initialize IMU!\r\n"); return rc; } - /* WHOAMI 레지스터 읽기 — 디바이스 존재 및 통신 확인 */ + /* Read WHOAMI register — verify device presence and communication */ rc = inv_imu_get_who_am_i(&icm_driver, &who_am_i); if (rc != INV_ERROR_SUCCESS) { DBG_PRINTF("!!! ERROR : Failed to read whoami!\r\n"); return rc; } - /* WHOAMI 값 검증 — ICM42670P의 경우 0x67이어야 함 */ + /* Verify WHOAMI value — must be 0x67 for ICM42670P */ if (who_am_i != ICM_WHOAMI) { DBG_PRINTF("!!! ERROR : Bad WHOAMI value! Read 0x%02x, expected 0x%02x\r\n", who_am_i, ICM_WHOAMI); return INV_ERROR; @@ -141,53 +142,53 @@ int setup_imu_device(struct inv_imu_serif *icm_serif) /* * configure_imu_device() - * IMU 센서 동작 파라미터 설정 + * Configures IMU sensor operating parameters. * - * 설정 항목: - * - FIFO: 비활성화 (USE_FIFO=0일 때, 레지스터 직접 읽기 모드) - * - 가속도계 FSR: ±4g (USE_HIGH_RES_MODE=0일 때) - * - 자이로 FSR: ±2000dps - * - ODR(출력 데이터율): - * - 저잡음 모드(USE_LOW_NOISE_MODE=1): 800Hz - * - 저전력 모드(USE_LOW_NOISE_MODE=0): 100Hz - * - 자이로는 항상 저잡음 모드로 동작 - * - FIFO 미사용 시 자이로 스타트업 시간만큼 대기 + * Settings: + * - FIFO: disabled (when USE_FIFO=0, direct register read mode) + * - Accel FSR: +/-4g (when USE_HIGH_RES_MODE=0) + * - Gyro FSR: +/-2000dps + * - ODR (output data rate): + * - Low-noise mode (USE_LOW_NOISE_MODE=1): 800Hz + * - Low-power mode (USE_LOW_NOISE_MODE=0): 100Hz + * - Gyro always operates in low-noise mode + * - Waits for gyro startup time when FIFO is not used * - * 반환값: 0=성공, 음수=에러 + * Returns: 0=success, negative=error */ int configure_imu_device(void) { int rc = 0; - /* FIFO 비활성화 — 레지스터에서 직접 데이터를 읽기 */ + /* Disable FIFO — read data directly from registers */ if (!USE_FIFO) rc |= inv_imu_configure_fifo(&icm_driver, INV_IMU_FIFO_DISABLED); if (USE_HIGH_RES_MODE) { - /* 고해상도 FIFO 모드: 20비트 데이터, FSR은 16g/2000dps로 고정됨 */ + /* High-resolution FIFO mode: 20-bit data, FSR locked to 16g/2000dps */ rc |= inv_imu_enable_high_resolution_fifo(&icm_driver); } else { - /* 표준 모드: 가속도계 ±4g, 자이로 ±2000dps FSR 설정 */ + /* Standard mode: accel +/-4g, gyro +/-2000dps FSR */ rc |= inv_imu_set_accel_fsr(&icm_driver, ACCEL_CONFIG0_FS_SEL_4g); rc |= inv_imu_set_gyro_fsr(&icm_driver, GYRO_CONFIG0_FS_SEL_2000dps); } if (USE_LOW_NOISE_MODE) { - /* 저잡음 모드: 800Hz ODR, 가속도계 저잡음 모드 활성화 */ + /* Low-noise mode: 800Hz ODR, enable accel low-noise mode */ rc |= inv_imu_set_accel_frequency(&icm_driver, ACCEL_CONFIG0_ODR_800_HZ); rc |= inv_imu_set_gyro_frequency(&icm_driver, GYRO_CONFIG0_ODR_800_HZ); rc |= inv_imu_enable_accel_low_noise_mode(&icm_driver); } else { - /* 저전력 모드: 100Hz ODR, 가속도계 저전력 모드 활성화 */ + /* Low-power mode: 100Hz ODR, enable accel low-power mode */ rc |= inv_imu_set_accel_frequency(&icm_driver, ACCEL_CONFIG0_ODR_100_HZ); rc |= inv_imu_set_gyro_frequency(&icm_driver, GYRO_CONFIG0_ODR_100_HZ); rc |= inv_imu_enable_accel_low_power_mode(&icm_driver); } - /* 자이로는 모드에 관계없이 항상 저잡음 모드로 동작 */ + /* Gyro always operates in low-noise mode regardless of setting */ rc |= inv_imu_enable_gyro_low_noise_mode(&icm_driver); - /* FIFO 미사용 시 자이로 스타트업 시간만큼 대기 (첫 유효 데이터까지의 지연) */ + /* When FIFO is not used, wait for gyro startup time (delay until first valid data) */ if (!USE_FIFO) inv_imu_sleep_us(GYR_STARTUP_TIME_US); @@ -197,9 +198,9 @@ int configure_imu_device(void) /* * get_imu_data() - * IMU에서 센서 데이터 읽기 - * USE_FIFO 설정에 따라 FIFO 또는 레지스터에서 데이터를 가져옴 - * 읽은 데이터는 imu_callback()을 통해 처리 + * Reads sensor data from the IMU. + * Fetches data from FIFO or registers depending on USE_FIFO setting. + * Read data is processed via imu_callback(). */ int get_imu_data(void) { @@ -214,8 +215,8 @@ int get_imu_data(void) #if SCALED_DATA_G_DPS /* * get_accel_and_gyr_fsr() - * 현재 설정된 가속도계와 자이로의 FSR(Full Scale Range) 값을 가져온다. - * 스케일링된 데이터(g, dps) 변환에 사용된다. + * Retrieves the currently configured FSR (Full Scale Range) for accel and gyro. + * Used for converting to scaled data (g, dps). */ static void get_accel_and_gyr_fsr(int16_t * accel_fsr_g, int16_t * gyro_fsr_dps) { @@ -253,17 +254,17 @@ static void get_accel_and_gyr_fsr(int16_t * accel_fsr_g, int16_t * gyro_fsr_dps) /* * imu_callback() - * IMU 드라이버가 새 센서 데이터를 읽을 때마다 호출되는 콜백 함수 + * Callback invoked each time the IMU driver reads new sensor data. * - * 처리 흐름: - * 1) 이벤트에서 가속도/자이로 원시 데이터 추출 - * - FIFO 모드: 타임스탬프 롤오버 처리, 고해상도(20비트) 지원 - * - 레지스터 모드: 16비트 데이터 직접 사용 - * 2) 마운팅 매트릭스 적용 (보드 장착 방향 보정) - * 3) 데이터 출력 (모드에 따라 분기): - * - info4 모드: info_imu[6] 전역 배열에 저장 (외부에서 폴링) - * - UART 모드: "Tp" 접두사로 6축 데이터 텍스트 출력 - * - BLE 모드: "rsp:" 태그로 바이너리 패킷 전송 + UART 텍스트 동시 출력 + * Flow: + * 1) Extract raw accel/gyro data from event + * - FIFO mode: handles timestamp rollover, supports high-res (20-bit) + * - Register mode: uses 16-bit data directly + * 2) Apply mounting matrix (board orientation correction) + * 3) Output data (branches by mode): + * - info4 mode: stores in info_imu[6] global array (polled externally) + * - UART mode: text output with "Tp" prefix for 6-axis data + * - BLE mode: binary packet with "rsp:" tag + simultaneous UART output */ void imu_callback(inv_imu_sensor_event_t *event) { @@ -280,18 +281,18 @@ void imu_callback(inv_imu_sensor_event_t *event) static uint64_t last_fifo_timestamp = 0; static uint32_t rollover_num = 0; - /* FIFO 타임스탬프 롤오버 처리 (16비트 → 64비트 확장) */ + /* FIFO timestamp rollover handling (16-bit -> 64-bit extension) */ if (last_fifo_timestamp > event->timestamp_fsync) rollover_num++; last_fifo_timestamp = event->timestamp_fsync; - /* 타임스탬프를 마이크로초 단위로 변환 (Q24 해상도 적용) */ + /* Convert timestamp to microseconds (apply Q24 resolution) */ timestamp = event->timestamp_fsync + rollover_num * UINT16_MAX; timestamp *= inv_imu_get_fifo_timestamp_resolution_us_q24(&icm_driver); timestamp /= (1UL << 24); if (icm_driver.fifo_highres_enabled) { - /* 고해상도 모드: 16비트 데이터를 4비트 좌측 시프트 + 하위 4비트 추가 → 20비트 */ + /* High-res mode: left-shift 16-bit data by 4 + add lower 4 bits -> 20-bit */ accel[0] = (((int32_t)event->accel[0] << 4)) | event->accel_high_res[0]; accel[1] = (((int32_t)event->accel[1] << 4)) | event->accel_high_res[1]; accel[2] = (((int32_t)event->accel[2] << 4)) | event->accel_high_res[2]; @@ -301,7 +302,7 @@ void imu_callback(inv_imu_sensor_event_t *event) gyro[2] = (((int32_t)event->gyro[2] << 4)) | event->gyro_high_res[2]; } else { - /* 표준 해상도: 16비트 데이터 그대로 사용 */ + /* Standard resolution: use 16-bit data as-is */ accel[0] = event->accel[0]; accel[1] = event->accel[1]; accel[2] = event->accel[2]; @@ -312,7 +313,7 @@ void imu_callback(inv_imu_sensor_event_t *event) } #else - /* 레지스터 직접 읽기 모드: 16비트 원시 데이터 추출 */ + /* Direct register read mode: extract 16-bit raw data */ accel[0] = event->accel[0]; accel[1] = event->accel[1]; accel[2] = event->accel[2]; @@ -321,20 +322,20 @@ void imu_callback(inv_imu_sensor_event_t *event) gyro[1] = event->gyro[1]; gyro[2] = event->gyro[2]; - /* 레지스터 모드에서는 센서 마스크를 강제 설정하여 아래 출력 로직이 동작하도록 함 */ + /* In register mode, force sensor mask so the output logic below works */ event->sensor_mask |= (1 << INV_SENSOR_TEMPERATURE); event->sensor_mask |= (1 << INV_SENSOR_ACCEL); event->sensor_mask |= (1 << INV_SENSOR_GYRO); #endif - /* 마운팅 매트릭스 적용 — 센서의 물리적 장착 방향을 소프트웨어 좌표계로 보정 */ + /* Apply mounting matrix — correct sensor physical orientation to software coordinates */ apply_mounting_matrix(icm_mounting_matrix, accel); apply_mounting_matrix(icm_mounting_matrix, gyro); #if SCALED_DATA_G_DPS /* - * 원시 데이터를 물리 단위(g, dps)로 변환 - * 변환 공식: 물리값 = 원시값 * FSR / INT16_MAX + * Convert raw data to physical units (g, dps) + * Formula: physical_value = raw_value * FSR / INT16_MAX */ get_accel_and_gyr_fsr(&accel_fsr_g, &gyro_fsr_dps); accel_g[0] = (float)(accel[0] * accel_fsr_g) / INT16_MAX; @@ -344,14 +345,14 @@ void imu_callback(inv_imu_sensor_event_t *event) gyro_dps[1] = (float)(gyro[1] * gyro_fsr_dps) / INT16_MAX; gyro_dps[2] = (float)(gyro[2] * gyro_fsr_dps) / INT16_MAX; - /* 온도 변환: 고해상도/레지스터 모드는 /128, FIFO 표준 모드는 /2 */ + /* Temperature conversion: high-res/register mode uses /128, FIFO standard mode uses /2 */ if (USE_HIGH_RES_MODE || !USE_FIFO) temp_degc = 25 + ((float)event->temperature / 128); else temp_degc = 25 + ((float)event->temperature / 2); /* - * 스케일링된 데이터를 UART로 출력 + * Output scaled data via UART */ if (event->sensor_mask & (1 << INV_SENSOR_ACCEL) && event->sensor_mask & (1 << INV_SENSOR_GYRO)) DBG_PRINTF("%u: %.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f\r\n", @@ -362,13 +363,13 @@ void imu_callback(inv_imu_sensor_event_t *event) #else /* - * 원시 데이터 출력 — 명령 소스(info4/UART/BLE)에 따라 분기 + * Raw data output — branches by command source (info4/UART/BLE) */ if (event->sensor_mask & (1 << INV_SENSOR_ACCEL) && event->sensor_mask & (1 << INV_SENSOR_GYRO) || motion_raw_data_enabled) { motion_raw_data_enabled = false; - /* info4 모드: 전역 배열 info_imu[6]에 데이터 저장, 외부 모듈에서 이 배열을 폴링하여 데이터 사용 */ + /* info4 mode: store data in global array info_imu[6], polled by external modules */ if (info4 == true) { info_imu[0] = (uint16_t)accel[0]; @@ -379,16 +380,16 @@ void imu_callback(inv_imu_sensor_event_t *event) info_imu[5] = (uint16_t)gyro[2]; } - /* UART 모드: "Tp" 접두사로 6축 데이터를 텍스트 형식으로 출력 */ + /* UART mode: output 6-axis data in text format with "Tp" prefix */ else if(cmd_type_t == CMD_UART) { //DBG_PRINTF("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]); } /* - * BLE 모드: 6축 데이터를 바이너리 패킷으로 BLE 전송 - * ssp_data[0~2] = 가속도 XYZ, ssp_data[3~5] = 자이로 XYZ - * format_data()로 "rsp:" 태그 + 12바이트 데이터를 패킷화 - * dr_binary_tx_safe()로 8바이트 BLE 전송 + * BLE mode: send 6-axis data as binary packet via BLE + * ssp_data[0..2] = accel XYZ, ssp_data[3..5] = gyro XYZ + * format_data() packs "rsp:" tag + 12-byte data + * dr_binary_tx_safe() sends 8 bytes via BLE */ else if(cmd_type_t == CMD_BLE) { ssp_data[0] = (uint16_t)accel[0]; @@ -419,13 +420,13 @@ void imu_callback(inv_imu_sensor_event_t *event) /* * apply_mounting_matrix() - * Q30 고정소수점 회전 매트릭스를 3축 벡터에 적용한다. + * Applies a Q30 fixed-point rotation matrix to a 3-axis vector. * - * 계산 방식: + * Calculation: * result[i] = matrix[i*3+0]*raw[0] + matrix[i*3+1]*raw[1] + matrix[i*3+2]*raw[2] - * 결과를 30비트 우측 시프트하여 Q30 → 정수 변환 + * Right-shift result by 30 bits for Q30 -> integer conversion. * - * 이를 통해 센서의 물리적 장착 방향에 관계없이 일관된 좌표계를 사용할 수 있다. + * Ensures a consistent coordinate system regardless of physical sensor orientation. */ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3]) { @@ -437,7 +438,7 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3]) data_q30[i] += ((int64_t)matrix[3*i+1] * raw[1]); data_q30[i] += ((int64_t)matrix[3*i+2] * raw[2]); } - /* Q30 → 정수 변환: 30비트 우측 시프트 */ + /* Q30 -> integer conversion: right-shift by 30 bits */ raw[0] = (int32_t)(data_q30[0]>>30); raw[1] = (int32_t)(data_q30[1]>>30); raw[2] = (int32_t)(data_q30[2]>>30); @@ -446,22 +447,22 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3]) /* * imu_read_direct() - * 드라이버 API를 우회하여 직접 I2C 레지스터를 읽는 함수. - * DRDY(데이터 준비) 인터럽트를 기다리지 않고 즉시 데이터를 읽는다. + * Reads IMU registers directly via I2C, bypassing the driver API. + * Reads data immediately without waiting for DRDY interrupt. * - * 처리 흐름: - * 1) TWI 초기화 확인 (최초 1회만) - * 2) 자이로 설정: ±2000dps, 100Hz ODR (GYRO_CONFIG0 = 0x09) - * 3) 가속도 설정: ±4g, 100Hz ODR (ACCEL_CONFIG0 = 0x29) - * 4) 전원 ON: 가속도+자이로 저잡음 모드 (PWR_MGMT0 = 0x0F) - * 5) 80ms 대기 (자이로 스타트업: 최소 45ms + 여유) - * 6) ACCEL_DATA_X1(0x0B)부터 12바이트 연속 읽기 (accel 6 + gyro 6) - * 7) 빅엔디안 → int16_t 변환 - * 8) 마운팅 매트릭스 적용 - * 9) "rsp:" 태그로 BLE 전송 - * 10) IMU 슬립 모드로 전환 (전력 절감) + * Flow: + * 1) Check TWI initialization (first call only) + * 2) Gyro config: +/-2000dps, 100Hz ODR (GYRO_CONFIG0 = 0x09) + * 3) Accel config: +/-4g, 100Hz ODR (ACCEL_CONFIG0 = 0x29) + * 4) Power ON: accel+gyro low-noise mode (PWR_MGMT0 = 0x0F) + * 5) Wait 80ms (gyro startup: min 45ms + margin) + * 6) Read 12 consecutive bytes from ACCEL_DATA_X1 (0x0B) (accel 6 + gyro 6) + * 7) Big-endian -> int16_t conversion + * 8) Apply mounting matrix + * 9) Send via BLE with "rsp:" tag + * 10) Switch IMU to sleep mode (power saving) * - * 반환값: 0=성공, -1=TX 실패, -2=RX 실패 + * Returns: 0=success, -1=TX failure, -2=RX failure */ /* Raw I2C read from ICM42670P — bypasses driver API entirely */ #include "system_interface.h" @@ -470,51 +471,51 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3]) extern const nrfx_twi_t m_twi_icm42670; #define IMU_I2C_ADDR 0x68 -#define REG_ACCEL_X1 0x0B /* ACCEL_DATA_X1 — 가속도 X축 상위 바이트 레지스터 */ +#define REG_ACCEL_X1 0x0B /* ACCEL_DATA_X1 — accel X-axis upper byte register */ /* -------------------------------------------------------------------------------------- * Direct IMU register read — raw I2C, no DRDY, sends rsp: via BLE - * 직접 I2C로 레지스터 읽는 방식 (인터럽트 X, IMU 드라이버 API X) + * Direct I2C register read (no interrupt, no IMU driver API) * -------------------------------------------------------------------------------------- */ int imu_read_direct(void) { - uint8_t raw[12]; /* 가속도 6바이트 + 자이로 6바이트 */ + uint8_t raw[12]; /* accel 6 bytes + gyro 6 bytes */ int32_t accel[3], gyro[3]; uint8_t reg; uint32_t ret; static bool twi_ready = false; - /* TWI(I2C) 초기화 — 최초 1회만 수행 (재초기화로 클린 상태 보장) */ + /* TWI (I2C) init — performed only once (re-init ensures clean state) */ if (!twi_ready) { inv_i2c_master_uninitialize(); inv_i2c_master_initialize(); twi_ready = true; } - /* 자이로 설정: GYRO_CONFIG0(0x20) = 0x09 → ±2000dps FSR, 100Hz ODR */ + /* Gyro config: GYRO_CONFIG0(0x20) = 0x09 -> +/-2000dps FSR, 100Hz ODR */ { uint8_t gyro_cfg[2] = { 0x20, 0x09 }; icm42670_twi_tx(IMU_I2C_ADDR, gyro_cfg, 2, false); } - /* 가속도 설정: ACCEL_CONFIG0(0x21) = 0x29 → ±4g FSR, 100Hz ODR */ + /* Accel config: ACCEL_CONFIG0(0x21) = 0x29 -> +/-4g FSR, 100Hz ODR */ { uint8_t accel_cfg[2] = { 0x21, 0x29 }; icm42670_twi_tx(IMU_I2C_ADDR, accel_cfg, 2, false); } - /* 전원 ON: PWR_MGMT0(0x1F) = 0x0F → 가속도(저잡음) + 자이로(저잡음) 활성화 */ + /* Power ON: PWR_MGMT0(0x1F) = 0x0F -> accel (low-noise) + gyro (low-noise) enabled */ { uint8_t pwr_cmd[2] = { 0x1F, 0x0F }; /* reg=0x1F, val=0x0F */ icm42670_twi_tx(IMU_I2C_ADDR, pwr_cmd, 2, false); - //nrf_delay_ms(80); /* 자이로 스타트업: 최소 45ms + 안전 마진 */ + //nrf_delay_ms(80); /* Gyro startup: min 45ms + safety margin */ dr_sd_delay_ms(80); } - /* ACCEL_DATA_X1(0x0B)부터 12바이트 연속 읽기 (0x0B~0x16) */ + /* Read 12 consecutive bytes from ACCEL_DATA_X1 (0x0B~0x16) */ reg = REG_ACCEL_X1; - ret = icm42670_twi_tx(IMU_I2C_ADDR, ®, 1, true); /* 레지스터 주소 전송 (STOP 없음) */ + ret = icm42670_twi_tx(IMU_I2C_ADDR, ®, 1, true); /* Send register address (no STOP) */ if (ret) { @@ -522,7 +523,7 @@ int imu_read_direct(void) return -1; } - ret = icm42670_twi_rx(IMU_I2C_ADDR, raw, 12); /* 12바이트 데이터 수신 */ + ret = icm42670_twi_rx(IMU_I2C_ADDR, raw, 12); /* Receive 12 bytes of data */ if (ret) { @@ -531,9 +532,9 @@ int imu_read_direct(void) } /* - * 빅엔디안 레지스터 레이아웃을 int16_t로 변환 - * raw[0..5] = 가속도 X,Y,Z (각 2바이트, MSB first) - * raw[6..11] = 자이로 X,Y,Z (각 2바이트, MSB first) + * Convert big-endian register layout to int16_t + * raw[0..5] = accel X,Y,Z (2 bytes each, MSB first) + * raw[6..11] = gyro X,Y,Z (2 bytes each, MSB first) */ accel[0] = (int16_t)((raw[0] << 8) | raw[1]); accel[1] = (int16_t)((raw[2] << 8) | raw[3]); @@ -542,11 +543,11 @@ int imu_read_direct(void) gyro[1] = (int16_t)((raw[8] << 8) | raw[9]); gyro[2] = (int16_t)((raw[10] << 8) | raw[11]); - /* 마운팅 매트릭스 적용 — 보드 장착 방향 보정 */ + /* Apply mounting matrix — board orientation correction */ apply_mounting_matrix(icm_mounting_matrix, accel); apply_mounting_matrix(icm_mounting_matrix, gyro); - /* 데이터 패킹 */ + /* Pack data */ ssp_data[0] = (uint16_t)accel[0]; ssp_data[1] = (uint16_t)accel[1]; ssp_data[2] = (uint16_t)accel[2]; @@ -556,7 +557,7 @@ int imu_read_direct(void) if (info4 == true) { - /* info4 모드: 전역 배열에 저장 (mbb?에서 rbb: 패킷으로 일괄 전송) */ + /* info4 mode: store in global array (sent as rbb: packet by mbb?) */ info_imu[0] = ssp_data[0]; info_imu[1] = ssp_data[1]; info_imu[2] = ssp_data[2]; @@ -566,12 +567,12 @@ int imu_read_direct(void) } else { - /* 일반 모드: "rsp:" 태그로 BLE 즉시 전송 */ + /* Normal mode: send immediately via BLE with "rsp:" tag */ format_data(imu_bin_buffer, "rsp:", ssp_data, 12); dr_binary_tx_safe(imu_bin_buffer, 8); } - /* IMU 슬립 모드: PWR_MGMT0 = 0x00 → 가속도/자이로 모두 OFF (전력 절감) */ + /* IMU sleep mode: PWR_MGMT0 = 0x00 -> accel/gyro both OFF (power saving) */ { uint8_t pwr_off[2] = { 0x1F, 0x00 }; /* reg=PWR_MGMT0, val=0x00 */ icm42670_twi_tx(IMU_I2C_ADDR, pwr_off, 2, false); diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw.h b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw.h index c422909..5b9cd8d 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw.h +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw.h @@ -7,16 +7,16 @@ ******************************************************************************/ /******************************************************************************* - * [헤더 개요] ICM42670P IMU 드라이버 상위 레이어 선언 + * [Header overview] ICM42670P IMU driver application layer declarations * - * IMU 센서의 초기화, 설정, 데이터 읽기를 위한 함수 프로토타입과 - * 동작 모드 설정 매크로를 정의한다. + * Function prototypes and operating mode configuration macros for + * IMU sensor initialization, configuration, and data reading. * - * 주요 설정 매크로: - * SERIF_TYPE - 통신 인터페이스 (UI_I2C) - * USE_LOW_NOISE_MODE - 1:저잡음(800Hz), 0:저전력(100Hz) - * USE_HIGH_RES_MODE - 1:20비트 고해상도, 0:16비트 표준 - * USE_FIFO - 1:FIFO 사용, 0:레지스터 직접 읽기 + * Key configuration macros: + * SERIF_TYPE - Communication interface (UI_I2C) + * USE_LOW_NOISE_MODE - 1: low-noise (800Hz), 0: low-power (100Hz) + * USE_HIGH_RES_MODE - 1: 20-bit high-res, 0: 16-bit standard + * USE_FIFO - 1: use FIFO, 0: direct register read ******************************************************************************/ #ifndef _APP_RAW_H_ @@ -29,71 +29,71 @@ #include "inv_imu_driver.h" -/*** 설정 매크로 ***/ +/*** Configuration macros ***/ /* - * MCU와 IMU 간 통신 인터페이스 선택 - * UI_I2C: I2C 통신 사용 (기본) + * MCU-IMU communication interface selection + * UI_I2C: use I2C communication (default) */ #define SERIF_TYPE UI_I2C /* - * 전원 모드 설정 - * 1: 저잡음 모드 — 800Hz ODR, 높은 정밀도, 높은 전력 소모 - * 0: 저전력 모드 — 100Hz ODR, 낮은 전력 소모 - * 주의: 12.5Hz 미만 ODR에서는 저잡음 모드 사용 불가 + * Power mode selection + * 1: Low-noise mode — 800Hz ODR, high precision, higher power consumption + * 0: Low-power mode — 100Hz ODR, lower power consumption + * Note: Low-noise mode cannot be used with ODR below 12.5Hz */ #define USE_LOW_NOISE_MODE 1 /* - * FIFO 해상도 모드 선택 - * 0: 저해상도 — 16비트 데이터 (기본) - * 1: 고해상도 — 20비트 데이터 (FSR이 16g/2000dps로 강제 고정됨) + * FIFO resolution mode selection + * 0: Low resolution — 16-bit data (default) + * 1: High resolution — 20-bit data (FSR locked to 16g/2000dps) */ #define USE_HIGH_RES_MODE 0 /* - * 데이터 읽기 방식 선택 - * 0: 레지스터 직접 읽기 (현재 사용 중) - * 1: FIFO에서 읽기 + * Data read method selection + * 0: Direct register read (currently in use) + * 1: Read from FIFO */ #define USE_FIFO 0 /** - * \brief IMU 디바이스를 리셋하고 초기화한다. WHOAMI 확인 포함. - * 다른 IMU 접근 함수 호출 전에 반드시 성공적으로 실행되어야 한다. + * \brief Resets and initializes the IMU device. Includes WHOAMI verification. + * Must complete successfully before calling any other IMU access functions. * - * \return 0=성공, 음수=에러 + * \return 0=success, negative=error */ int setup_imu_device(struct inv_imu_serif *icm_serif); /** - * \brief 자이로 및 가속도계 출력을 위한 디바이스 설정을 수행한다. - * FSR, ODR, 전원 모드, FIFO 설정 등을 적용한다. - * \return 0=성공, 음수=에러 + * \brief Configures the device for gyro and accel output. + * Applies FSR, ODR, power mode, and FIFO settings. + * \return 0=success, negative=error */ int configure_imu_device(void); /** - * \brief FIFO 또는 레지스터에서 IMU 데이터를 추출한다. - * 내부적으로 imu_callback()이 호출되어 데이터를 처리한다. - * \return 0=성공, 음수=에러 + * \brief Retrieves IMU data from FIFO or registers. + * Internally triggers imu_callback() for data processing. + * \return 0=success, negative=error */ int get_imu_data(void); /** - * \brief 센서 데이터 수신 콜백. 마운팅 매트릭스 적용 후 - * info4/BLE/UART 모드에 따라 데이터를 출력한다. - * \param[in] event 하나의 센서 데이터 패킷을 담은 구조체 + * \brief Sensor data receive callback. Applies mounting matrix then + * outputs data according to info4/BLE/UART mode. + * \param[in] event Structure containing one sensor data packet */ void imu_callback(inv_imu_sensor_event_t *event); /** - * \brief 드라이버 API를 우회한 직접 I2C 레지스터 읽기. - * DRDY 인터럽트 없이 즉시 센서 데이터를 읽어 BLE로 전송한다. - * 읽기 후 IMU를 슬립 모드로 전환하여 전력을 절감한다. - * \return 0=성공, 음수=에러 + * \brief Direct I2C register read bypassing the driver API. + * Reads sensor data immediately without DRDY interrupt and sends via BLE. + * Switches IMU to sleep mode after reading to save power. + * \return 0=success, negative=error */ int imu_read_direct(void); diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw_main.c b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw_main.c index 077d94d..3d6ed4f 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw_main.c +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw_main.c @@ -8,30 +8,31 @@ /******************************************************************************* * 2026.03.26 jhChun - * 현재 이 파일은 실제 런타임에 실행되지 않고 있음 - * 인터럽트 방식 대신 app_raw.c imu_read_direct()에서 직접 레지스터 읽는 방식 사용 중 - * 추후 필요 여부에 따라 정리 예정 + * This file is currently not executed at runtime. + * Instead of interrupt-driven reads, imu_read_direct() in app_raw.c + * reads registers directly. May be cleaned up later as needed. ******************************************************************************/ /******************************************************************************* - * [모듈 개요] ICM42670P 메인 초기화 및 폴링 루프 + * [Module overview] ICM42670P main initialization and polling loop * - * ICM42670P IMU 센서의 전체 초기화 시퀀스와 메인 루프를 담당한다. + * Handles the full initialization sequence and main loop for the ICM42670P + * IMU sensor. * - * 초기화 흐름 (icm42670_init): - * 1) setup_mcu() - I2C 시리얼 인터페이스 구조체 설정 및 TWI 초기화 - * 2) setup_imu_device() - IMU 드라이버 초기화 + WHOAMI 확인 - * 3) configure_imu_device() - 센서 파라미터 설정 (FSR, ODR, 전원 모드) - * 4) inv_gpio_sensor_irq_init() - INT1(P1.13) GPIO 인터럽트 설정 + * Init flow (icm42670_init): + * 1) setup_mcu() - Configure I2C serial interface struct and TWI init + * 2) setup_imu_device() - IMU driver init + WHOAMI verification + * 3) configure_imu_device() - Sensor parameter config (FSR, ODR, power mode) + * 4) inv_gpio_sensor_irq_init() - INT1 (P1.13) GPIO interrupt setup * - * 메인 루프 (icm42670_main): - * - INT1 인터럽트 발생 시 irq_from_device 플래그가 세팅됨 - * - 메인 루프에서 플래그를 확인하고, 세팅되어 있으면 센서 데이터를 읽음 - * - 인터럽트는 하강 에지(HITOLO)에서 발생 (INT1 핀 풀업 설정) + * Main loop (icm42670_main): + * - irq_from_device flag is set when INT1 interrupt fires + * - Main loop checks the flag and reads sensor data when set + * - Interrupt triggers on falling edge (HITOLO) with INT1 pin pulled up * - * 보조 함수: - * - inv_imu_sleep_us() - nrf_delay_us 래퍼 (IMU 드라이버가 사용) - * - inv_imu_get_time_us() - RTC1 카운터로 타임스탬프 제공 + * Helper functions: + * - inv_imu_sleep_us() - nrf_delay_us wrapper (used by IMU driver) + * - inv_imu_get_time_us() - Provides timestamp via RTC1 counter ******************************************************************************/ #include "sdk_config.h" #include "app_raw.h" @@ -50,7 +51,7 @@ #include "nrf_delay.h" #include "app_util_platform.h" -#include "main.h" /* 2026-03-17: cmd_parse.h 삭제 → main.h */ +#include "main.h" /* 2026-03-17: removed cmd_parse.h, using main.h */ #include "i2c_manager.h" /* -------------------------------------------------------------------------------------- * Global variables @@ -61,10 +62,10 @@ * -------------------------------------------------------------------------------------- */ /* - * IMU 인터럽트 플래그 - * INT1 핀의 하강 에지 인터럽트 발생 시 1로 세팅된다. - * 메인 루프에서 이 플래그를 확인 후 데이터를 읽고 0으로 클리어한다. - * volatile: ISR에서 변경되므로 컴파일러 최적화 방지 + * IMU interrupt flag + * Set to 1 on falling-edge interrupt of INT1 pin. + * Main loop checks this flag, reads data, and clears it to 0. + * volatile: modified by ISR, prevents compiler optimization. */ static volatile int irq_from_device; @@ -90,9 +91,9 @@ static int setup_mcu(struct inv_imu_serif *icm_serif); */ /* * inv_gpio_sensor_interrupt_handler() - * INT1 핀 인터럽트 핸들러 (ISR). - * 센서가 새 데이터를 준비했을 때 호출되며, 플래그만 세팅하고 즉시 반환한다. - * 실제 데이터 처리는 메인 루프(icm42670_main)에서 수행한다. + * INT1 pin interrupt handler (ISR). + * Called when the sensor has new data ready; sets a flag and returns immediately. + * Actual data processing is done in the main loop (icm42670_main). */ static void inv_gpio_sensor_interrupt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { @@ -102,52 +103,52 @@ static void inv_gpio_sensor_interrupt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_ /* * inv_gpio_sensor_irq_init() - * INT1(P1.13) GPIO 인터럽트를 초기화한다. + * Initializes INT1 (P1.13) GPIO interrupt. * - * 설정: - * - 트리거: 하강 에지 (HITOLO) — 센서가 INT를 Low로 끌어내릴 때 - * - 풀업 저항: 내부 풀업 활성화 - * - 핸들러: inv_gpio_sensor_interrupt_handler - * - GPIOTE 모듈이 미초기화 상태이면 먼저 초기화 + * Configuration: + * - Trigger: falling edge (HITOLO) — when the sensor pulls INT low + * - Pull-up: internal pull-up enabled + * - Handler: inv_gpio_sensor_interrupt_handler + * - Initializes GPIOTE module first if not already initialized */ void inv_gpio_sensor_irq_init(void) { ret_code_t err_code; - /* GPIOTE 모듈 초기화 (이미 초기화되어 있으면 건너뜀) */ + /* Initialize GPIOTE module (skip if already initialized) */ if (!nrfx_gpiote_is_init()) { err_code = nrfx_gpiote_init(); APP_ERROR_CHECK(err_code); } - /* 하강 에지 인터럽트 설정: High→Low 전환 시 트리거, 내부 풀업 사용 */ + /* Falling-edge interrupt: trigger on High->Low transition, internal pull-up */ nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true); in_config.pull = NRF_GPIO_PIN_PULLUP; - /* INT1 핀에 인터럽트 핸들러 등록 */ + /* Register interrupt handler for INT1 pin */ err_code = nrfx_gpiote_in_init(ICM42670_INT1_PIN, &in_config, inv_gpio_sensor_interrupt_handler); APP_ERROR_CHECK(err_code); - /* 인터럽트 이벤트 활성화 */ + /* Enable interrupt event */ nrfx_gpiote_in_event_enable(ICM42670_INT1_PIN, true); } /* * inv_gpio_sensor_irq_uninit() - * INT1 GPIO 인터럽트를 비활성화하고 해제한다. - * 센서 비활성화 시 또는 재초기화 전에 호출된다. + * Disables and releases the INT1 GPIO interrupt. + * Called when deactivating the sensor or before re-initialization. */ void inv_gpio_sensor_irq_uninit(void) { - /* 인터럽트 이벤트 비활성화 */ + /* Disable interrupt event */ nrfx_gpiote_in_event_disable(ICM42670_INT1_PIN); - /* INT1 핀 인터럽트 설정 해제 */ + /* Release INT1 pin interrupt configuration */ nrfx_gpiote_in_uninit(ICM42670_INT1_PIN); - /* GPIOTE 모듈 해제 (초기화된 경우에만) */ + /* Release GPIOTE module (only if initialized) */ if (nrfx_gpiote_is_init()) { nrfx_gpiote_uninit(); @@ -161,15 +162,15 @@ void inv_gpio_sensor_irq_uninit(void) /* * icm42670_init() - * ICM42670P 전체 초기화 시퀀스를 수행한다. + * Performs the full ICM42670P initialization sequence. * - * 초기화 순서: - * 1) setup_mcu() - I2C 인터페이스 구조체 설정 및 TWI 하드웨어 초기화 - * 2) setup_imu_device() - IMU 드라이버 초기화, WHOAMI(0x67) 확인 - * 3) configure_imu_device() - FSR, ODR, 전원 모드 설정 - * 4) inv_gpio_sensor_irq_init() - INT1 인터럽트 활성화 (데이터 준비 알림) + * Init order: + * 1) setup_mcu() - Configure I2C interface struct and TWI hardware init + * 2) setup_imu_device() - IMU driver init, WHOAMI (0x67) verification + * 3) configure_imu_device() - FSR, ODR, power mode configuration + * 4) inv_gpio_sensor_irq_init() - Enable INT1 interrupt (data-ready notification) * - * 반환값: 0=성공, -1=초기화 실패 + * Returns: 0=success, -1=initialization failure */ int icm42670_init(void) { @@ -185,7 +186,7 @@ int icm42670_init(void) return -1; } - /* 초기화 성공 후 INT1 인터럽트 활성화 — 이후 데이터 준비 시 ISR이 호출됨 */ + /* Enable INT1 interrupt after successful init — ISR fires on data ready */ inv_gpio_sensor_irq_init(); return rc; @@ -194,22 +195,23 @@ int icm42670_init(void) /* * icm42670_main() - * ICM42670P 메인 폴링 루프. - * 메인 애플리케이션 루프에서 주기적으로 호출되어야 한다. + * ICM42670P main polling loop. + * Must be called periodically from the main application loop. * - * 동작: - * 1) I2C 하드웨어가 초기화되었는지 확인 (hw_i2c_init_once) - * 2) irq_from_device 플래그 확인 (ISR에서 세팅됨) - * 3) 플래그가 세팅되어 있으면 센서 데이터 읽기 (get_imu_data) - * 4) 데이터 읽기 완료 후 플래그 클리어 + * Operation: + * 1) Check if I2C hardware is initialized (hw_i2c_init_once) + * 2) Check irq_from_device flag (set by ISR) + * 3) If flag is set, read sensor data (get_imu_data) + * 4) Clear flag after data read completes * - * 참고: 인터럽트 기반 폴링 방식으로, ISR에서는 플래그만 세팅하고 실제 I2C 통신은 메인 컨텍스트에서 수행한다. + * Note: Interrupt-based polling — ISR only sets the flag; actual I2C + * communication is done in the main context. */ void icm42670_main(void) { int rc = 0; hw_i2c_init_once(); - /* 인터럽트 발생 여부 확인 후 데이터 읽기 */ + /* Check for interrupt and read data */ if (irq_from_device) { rc = get_imu_data(); @@ -218,7 +220,7 @@ void icm42670_main(void) printf("error while getting data\r\n"); } - /* 플래그 클리어 — 다음 인터럽트까지 대기 */ + /* Clear flag — wait for next interrupt */ irq_from_device = 0; } @@ -231,28 +233,28 @@ void icm42670_main(void) /* * setup_mcu() - * MCU 측 시리얼 인터페이스를 설정한다. + * Configures the MCU-side serial interface. * - * inv_imu_serif 구조체에 다음을 등록: - * - read_reg / write_reg : I2C 읽기/쓰기 콜백 함수 (system_interface.c에서 구현) - * - max_read / max_write : 최대 전송 크기 (32KB) - * - serif_type : 통신 타입 (UI_I2C) + * Registers the following in the inv_imu_serif struct: + * - read_reg / write_reg : I2C read/write callbacks (implemented in system_interface.c) + * - max_read / max_write : Max transfer size (32KB) + * - serif_type : Communication type (UI_I2C) * - * 설정 후 inv_io_hal_init()을 호출하여 실제 TWI 하드웨어를 초기화한다. + * After configuration, calls inv_io_hal_init() to initialize the TWI hardware. */ static int setup_mcu(struct inv_imu_serif *icm_serif) { int rc = 0; - /* IMU 드라이버용 시리얼 인터페이스 구조체 설정 */ - icm_serif->context = 0; /* 컨텍스트 미사용 */ - icm_serif->read_reg = inv_io_hal_read_reg; /* 레지스터 읽기 콜백 */ - icm_serif->write_reg = inv_io_hal_write_reg; /* 레지스터 쓰기 콜백 */ - icm_serif->max_read = 1024*32; /* 1회 읽기 최대 바이트 수 */ - icm_serif->max_write = 1024*32; /* 1회 쓰기 최대 바이트 수 */ - icm_serif->serif_type = SERIF_TYPE; /* UI_I2C (app_raw.h에서 정의) */ + /* Configure serial interface struct for IMU driver */ + icm_serif->context = 0; /* Context unused */ + icm_serif->read_reg = inv_io_hal_read_reg; /* Register read callback */ + icm_serif->write_reg = inv_io_hal_write_reg; /* Register write callback */ + icm_serif->max_read = 1024*32; /* Max bytes per read */ + icm_serif->max_write = 1024*32; /* Max bytes per write */ + icm_serif->serif_type = SERIF_TYPE; /* UI_I2C (defined in app_raw.h) */ - /* TWI 하드웨어 초기화 */ + /* Initialize TWI hardware */ rc |= inv_io_hal_init(icm_serif); return rc; @@ -265,9 +267,9 @@ static int setup_mcu(struct inv_imu_serif *icm_serif) /* * inv_imu_sleep_us() - * IMU 드라이버가 사용하는 마이크로초 단위 슬립 함수. - * nrf_delay_us()를 래핑하여 플랫폼 독립적 인터페이스를 제공한다. - * 예: 자이로 스타트업 대기(GYR_STARTUP_TIME_US) 시 사용 + * Microsecond sleep function used by the IMU driver. + * Wraps nrf_delay_us() to provide a platform-independent interface. + * Example: used for gyro startup delay (GYR_STARTUP_TIME_US). */ void inv_imu_sleep_us(uint32_t us) { @@ -277,12 +279,12 @@ void inv_imu_sleep_us(uint32_t us) /* * inv_imu_get_time_us() - * IMU 드라이버가 사용하는 타임스탬프 함수. - * nRF52840의 RTC1 카운터 값을 반환한다. + * Timestamp function used by the IMU driver. + * Returns the nRF52840 RTC1 counter value. * - * 주의: RTC1은 32.768kHz로 동작하므로, 반환값의 단위는 엄밀히 - * 마이크로초가 아닌 RTC 틱(약 30.5us/tick)이다. - * 드라이버 내부에서 상대적 시간 비교 용도로 사용된다. + * Note: RTC1 runs at 32.768kHz, so the returned value is technically + * in RTC ticks (~30.5us/tick), not microseconds. + * Used for relative time comparisons within the driver. */ uint64_t inv_imu_get_time_us(void) { diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw_main.h b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw_main.h index f725804..d8afc25 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw_main.h +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/app_raw/app_raw_main.h @@ -7,25 +7,26 @@ ******************************************************************************/ /******************************************************************************* - * [헤더 개요] ICM42670P 메인 초기화/폴링 루프 선언 + * [Header overview] ICM42670P main initialization/polling loop declarations * - * ICM42670P IMU 센서의 전체 초기화 및 메인 루프 함수를 선언한다. - * - icm42670_init() : 전체 초기화 (MCU설정 → IMU초기화 → 센서설정 → 인터럽트 활성화) - * - icm42670_main() : 메인 폴링 루프 (INT1 인터럽트 확인 → 데이터 읽기) - * - icm42670_uninit() : 해제 (프로토타입만 선언, 구현은 별도) + * Declares the full initialization and main loop functions for the + * ICM42670P IMU sensor. + * - icm42670_init() : Full init (MCU config -> IMU init -> sensor config -> enable IRQ) + * - icm42670_main() : Main polling loop (check INT1 interrupt -> read data) + * - icm42670_uninit() : Release (prototype only, implementation elsewhere) ******************************************************************************/ #ifndef _APP_RAW_MAIN_H_ #define _APP_RAW_MAIN_H_ #include "sdk_config.h" -/* ICM42670P 전체 초기화 — MCU I2C 설정 → IMU 드라이버 초기화 → 센서 설정 → 인터럽트 활성화 */ +/* ICM42670P full init — MCU I2C config -> IMU driver init -> sensor config -> enable IRQ */ int icm42670_init(void); -/* ICM42670P 메인 폴링 루프 — INT1 인터럽트 플래그 확인 후 센서 데이터 읽기 */ +/* ICM42670P main polling loop — check INT1 interrupt flag, then read sensor data */ void icm42670_main(void); -/* ICM42670P 해제 (프로토타입 선언) */ +/* ICM42670P release (prototype declaration) */ int icm42670_uninit(void); #endif /* !_APP_RAW_MAIN_H_ */ diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/system_interface.c b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/system_interface.c index 95a493e..a5e2e9e 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/system_interface.c +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/system_interface.c @@ -7,25 +7,25 @@ ******************************************************************************/ /******************************************************************************* - * [모듈 개요] ICM42670P IMU 센서 I2C 통신 인터페이스 + * [Module overview] ICM42670P IMU sensor I2C communication interface * - * nRF52840의 TWI(I2C) 하드웨어를 사용하여 ICM42670P IMU 센서와 통신하는 - * 저수준 인터페이스 모듈이다. + * Low-level interface module for communicating with the ICM42670P IMU sensor + * via the nRF52840 TWI (I2C) hardware. * - * - I2C 슬레이브 주소: 0x68 (ICM42670P 기본 주소) - * - I2C 핀 설정: SCL=P1.14, SDA=P1.15 (system_interface.h에서 정의) - * - TWI 인스턴스: NRFX_TWI_INSTANCE(0) 사용 - * - 통신 속도: 100kHz (NRF_TWI_FREQ_100K) + * - I2C slave address: 0x68 (ICM42670P default) + * - I2C pin config: SCL=P1.14, SDA=P1.15 (defined in system_interface.h) + * - TWI instance: NRFX_TWI_INSTANCE(0) + * - Bus speed: 100kHz (NRF_TWI_FREQ_100K) * - * 주요 함수 흐름: - * inv_io_hal_init() → I2C 또는 SPI 초기화 (현재 I2C만 구현) - * inv_io_hal_read_reg() → 레지스터 읽기 (TX로 주소 전송 → RX로 데이터 수신) - * inv_io_hal_write_reg() → 레지스터 쓰기 (주소+데이터를 한번에 TX) + * Main function flow: + * inv_io_hal_init() -> Initialize I2C or SPI (only I2C implemented) + * inv_io_hal_read_reg() -> Register read (TX address -> RX data) + * inv_io_hal_write_reg() -> Register write (TX address+data at once) * - * 에러 처리: 모든 I2C 읽기/쓰기에서 실패 시 1회 재시도 후 에러 출력 + * Error handling: All I2C read/write operations retry once on failure * - * 참고: SPI4 인터페이스 코드 경로도 존재하지만 현재 미구현 상태이며, - * 실제 운용에서는 I2C(UI_I2C)만 사용한다. + * Note: SPI4 code path exists but is not implemented; + * only I2C (UI_I2C) is used in production. ******************************************************************************/ /* board driver */ @@ -43,17 +43,17 @@ #include "system_interface.h" #include "nrf_delay.h" -/* ICM42670P I2C 슬레이브 주소 및 직렬 쓰기 최대 바이트 수 */ +/* ICM42670P I2C slave address and max serial write byte count */ #define ICM_I2C_ADDR 0x68 #define INV_MAX_SERIAL_WRITE 16 -/* TWI(I2C) 인스턴스 생성 — system_interface.h의 ICM42670_I2C_INSTANCE(0)을 사용 */ +/* TWI (I2C) instance — uses ICM42670_I2C_INSTANCE(0) from system_interface.h */ const nrfx_twi_t m_twi_icm42670 = NRFX_TWI_INSTANCE(ICM42670_I2C_INSTANCE); /* * inv_i2c_master_uninitialize() - * I2C 버스를 비활성화하고 TWI 인스턴스를 해제한다. - * 슬립 모드 진입 전 또는 재초기화 전에 호출된다. + * Disables the I2C bus and releases the TWI instance. + * Called before entering sleep mode or before re-initialization. */ void inv_i2c_master_uninitialize(void){ nrfx_twi_disable(&m_twi_icm42670); @@ -62,11 +62,11 @@ void inv_i2c_master_uninitialize(void){ /* * inv_i2c_master_initialize() - * nRF52840 TWI 하드웨어를 초기화하고 활성화한다. + * Initializes and enables the nRF52840 TWI hardware. * - SCL: P1.14, SDA: P1.15 - * - 속도: 100kHz - * - 인터럽트 우선순위: 최고 (APP_IRQ_PRIORITY_HIGH) - * - 이벤트 핸들러 없음 (블로킹 모드로 동작) + * - Speed: 100kHz + * - Interrupt priority: highest (APP_IRQ_PRIORITY_HIGH) + * - No event handler (blocking mode) */ void inv_i2c_master_initialize(void){ ret_code_t err_code; @@ -78,19 +78,19 @@ void inv_i2c_master_initialize(void){ .interrupt_priority = APP_IRQ_PRIORITY_HIGH, }; - /* TWI 드라이버 초기화 (이벤트 핸들러=NULL → 블로킹 모드) */ + /* Initialize TWI driver (event handler=NULL -> blocking mode) */ err_code = nrfx_twi_init(&m_twi_icm42670, &twi_icm42670_config, NULL, NULL); APP_ERROR_CHECK(err_code); - /* TWI 하드웨어 활성화 — 이후 tx/rx 가능 */ + /* Enable TWI hardware — tx/rx available after this */ nrfx_twi_enable(&m_twi_icm42670); } /* * icm42670_twi_tx() - * I2C 전송 래퍼 함수. nrfx_twi_tx를 호출하여 데이터를 송신한다. - * no_stop=true이면 STOP 컨디션을 보내지 않음 (Repeated START를 위해 사용) + * I2C transmit wrapper. Calls nrfx_twi_tx to send data. + * If no_stop=true, STOP condition is omitted (used for Repeated START). */ uint32_t icm42670_twi_tx( uint8_t device_id, uint8_t const * p_data, @@ -105,7 +105,7 @@ uint32_t icm42670_twi_tx( uint8_t device_id, /* * icm42670_twi_rx() - * I2C 수신 래퍼 함수. nrfx_twi_rx를 호출하여 데이터를 수신한다. + * I2C receive wrapper. Calls nrfx_twi_rx to receive data. */ uint32_t icm42670_twi_rx( uint8_t device_id, uint8_t * p_data, @@ -119,33 +119,33 @@ uint32_t icm42670_twi_rx( uint8_t device_id, /* * inv_i2c_master_read_register() - * ICM42670P의 특정 레지스터에서 데이터를 읽는다. + * Reads data from a specific ICM42670P register. * - * 동작 순서: - * 1) TX: 레지스터 주소 1바이트 전송 (no_stop=true → Repeated START 준비) - * 2) RX: 지정된 길이만큼 데이터 수신 + * Sequence: + * 1) TX: Send 1-byte register address (no_stop=true -> prepare Repeated START) + * 2) RX: Receive data of specified length * - * 에러 처리: TX, RX 각각 실패 시 1회 재시도한다. + * Error handling: Retries once on TX or RX failure. */ static unsigned long inv_i2c_master_read_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, unsigned char *RegisterValue){ //ret_code_t ret; uint32_t ret; uint8_t addr8 = (uint8_t)RegisterAddr; - /* 1단계: 읽을 레지스터 주소를 전송 (STOP 없이 → Repeated START 사용) */ + /* Step 1: Send register address to read (no STOP -> uses Repeated START) */ ret = icm42670_twi_tx(Address, &addr8, 1, true); if(ret != NRF_SUCCESS) { - /* 실패 시 1회 재시도 */ + /* Retry once on failure */ ret = icm42670_twi_tx(Address, &addr8, 1, true); if(ret != NRF_SUCCESS) { printf("ERR! i2c read-1\r\n"); } } - /* 2단계: 해당 레지스터에서 데이터 수신 */ + /* Step 2: Receive data from the register */ ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen); if(ret != NRF_SUCCESS) { - /* 실패 시 1회 재시도 */ + /* Retry once on failure */ ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen); if(ret != NRF_SUCCESS) { printf("ERR! i2c read-2\r\n"); @@ -157,26 +157,26 @@ static unsigned long inv_i2c_master_read_register(unsigned char Address, unsigne /* * inv_i2c_master_write_register() - * ICM42670P의 특정 레지스터에 데이터를 쓴다. + * Writes data to a specific ICM42670P register. * - * 동작 순서: - * 1) 버퍼[0]에 레지스터 주소, 버퍼[1~N]에 쓸 데이터를 배치 - * 2) TX: 주소+데이터를 한번에 전송 (no_stop=false → STOP 컨디션 포함) + * Sequence: + * 1) Place register address in buffer[0], data in buffer[1..N] + * 2) TX: Send address+data at once (no_stop=false -> includes STOP condition) * - * 에러 처리: 실패 시 1회 재시도한다. + * Error handling: Retries once on failure. */ static unsigned long inv_i2c_master_write_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, const unsigned char *RegisterValue){ uint32_t ret; - uint8_t buffer[1 + INV_MAX_SERIAL_WRITE]; /* 레지스터 주소(1) + 데이터(최대 16바이트) */ + uint8_t buffer[1 + INV_MAX_SERIAL_WRITE]; /* register address (1) + data (max 16 bytes) */ - /* 버퍼 구성: [레지스터 주소][데이터 바이트들] */ + /* Buffer layout: [register address][data bytes] */ buffer[0] = (uint8_t)RegisterAddr; memcpy(buffer+1, RegisterValue, RegisterLen); - /* 주소+데이터를 한번에 전송 */ + /* Send address+data at once */ ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false); if(ret != NRF_SUCCESS) { - /* 실패 시 1회 재시도 */ + /* Retry once on failure */ ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false); if(ret != NRF_SUCCESS) { printf("ERR! i2c write\r\n"); @@ -190,9 +190,9 @@ static unsigned long inv_i2c_master_write_register(unsigned char Address, unsign /* * inv_io_hal_init() - * IMU 드라이버가 사용하는 시리얼 인터페이스(I2C 또는 SPI)를 초기화한다. - * serif->serif_type에 따라 분기하며, 현재는 I2C만 구현되어 있다. - * 반환값: 0=성공, -1=지원하지 않는 인터페이스 타입 + * Initializes the serial interface (I2C or SPI) used by the IMU driver. + * Branches based on serif->serif_type; only I2C is currently implemented. + * Returns: 0=success, -1=unsupported interface type */ int inv_io_hal_init(struct inv_imu_serif *serif) @@ -201,7 +201,7 @@ int inv_io_hal_init(struct inv_imu_serif *serif) switch (serif->serif_type) { case UI_SPI4: { - /* SPI4 초기화 — 현재 미구현 (I2C만 사용) */ + /* SPI4 init — not implemented (only I2C is used) */ break; } @@ -219,8 +219,8 @@ int inv_io_hal_init(struct inv_imu_serif *serif) /* * inv_io_hal_read_reg() - * IMU 드라이버 콜백: 지정된 레지스터에서 데이터를 읽는다. - * 시리얼 타입에 따라 I2C 또는 SPI 읽기를 수행한다. + * IMU driver callback: reads data from the specified register. + * Performs I2C or SPI read depending on the serial type. */ int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen) { @@ -238,8 +238,8 @@ int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuf /* * inv_io_hal_write_reg() - * IMU 드라이버 콜백: 지정된 레지스터에 데이터를 쓴다. - * 시리얼 타입에 따라 I2C 또는 SPI 쓰기를 수행한다. + * IMU driver callback: writes data to the specified register. + * Performs I2C or SPI write depending on the serial type. */ int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen) { @@ -257,9 +257,9 @@ int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t /* * cat_read() - * 범용 I2C 읽기 함수 (디버그/레거시용). - * 8바이트를 읽어 첫 번째 바이트를 반환하고, 읽은 데이터를 콘솔에 출력한다. - * 참고: 현재 실제 운용에서는 사용되지 않으며, 디버그 목적으로 남아 있다. + * Generic I2C read function (debug/legacy). + * Reads 8 bytes, returns the first byte, and prints the data to console. + * Note: Not used in production; kept for debug purposes. */ uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data) { @@ -270,14 +270,14 @@ uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data) //address = 1|(address<<1); address = (address & 0xFF); - /* 레지스터 주소 전송 (STOP 없이, Repeated START 준비) */ + /* Send register address (no STOP, prepare Repeated START) */ err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, &address, 1, true); if (err_code != NRF_SUCCESS) { // Handle error // return; } - /* 8바이트 데이터 수신 */ + /* Receive 8 bytes of data */ err_code = nrfx_twi_rx(&m_twi_icm42670, device_id, data, 8); if (err_code != NRF_SUCCESS) { // Handle error @@ -298,9 +298,9 @@ uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data) /* * cat_write() - * 범용 I2C 쓰기 함수 (디버그/레거시용). - * 주소 1바이트 + 데이터 1바이트를 전송한다. - * 참고: buffer에 6바이트를 복사하지만, 실제 전송은 2바이트만 수행한다. + * Generic I2C write function (debug/legacy). + * Sends 1 byte address + 1 byte data. + * Note: Copies 6 bytes into buffer, but only transmits 2 bytes. */ void cat_write(uint8_t device_id, uint8_t address, uint8_t *data){ @@ -314,7 +314,7 @@ void cat_write(uint8_t device_id, uint8_t address, uint8_t *data){ ret_code_t err_code; //err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, 0x00, 1, false); - /* 주소(1바이트) + 데이터(1바이트) = 2바이트 전송 */ + /* Address (1 byte) + data (1 byte) = 2 bytes transmitted */ err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, buffer, 2, false); // err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 2, false); // nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length); diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/system_interface.h b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/system_interface.h index 9d963a6..4559e35 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/system_interface.h +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/imu/system_interface.h @@ -7,17 +7,18 @@ ******************************************************************************/ /******************************************************************************* - * [헤더 개요] ICM42670P I2C 통신 인터페이스 선언 + * [Header overview] ICM42670P I2C communication interface declarations * - * nRF52840 TWI 하드웨어를 통해 ICM42670P IMU 센서와 통신하기 위한 핀 정의, 함수 프로토타입 선언 + * Pin definitions and function prototypes for communicating with the + * ICM42670P IMU sensor via nRF52840 TWI hardware. * - * 핀 배치: + * Pin assignment: * - I2C SCL : P1.14 * - I2C SDA : P1.15 - * - INT1 : P1.13 (데이터 준비 인터럽트) - * - INT2 : P0.26 (보조 인터럽트, 현재 미사용) + * - INT1 : P1.13 (data-ready interrupt) + * - INT2 : P0.26 (auxiliary interrupt, currently unused) * - * TWI 인스턴스: 0번 사용 + * TWI instance: 0 ******************************************************************************/ #ifndef _SYSTEM_INTERFACE_H_ @@ -31,42 +32,42 @@ #endif -#define ICM42670_I2C_INSTANCE 0 /**< I2C(TWI) 인스턴스 인덱스 */ -#define ICM42670_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15) /**< SDA 핀: P1.15 */ -#define ICM42670_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14) /**< SCL 핀: P1.14 */ -#define ICM42670_INT1_PIN NRF_GPIO_PIN_MAP(1,13) /**< INT1 핀: P1.13 (데이터 준비 인터럽트) */ -#define ICM42670_INT2_PIN NRF_GPIO_PIN_MAP(0,26) /**< INT2 핀: P0.26 (보조, 현재 미사용) */ +#define ICM42670_I2C_INSTANCE 0 /**< I2C (TWI) instance index */ +#define ICM42670_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15) /**< SDA pin: P1.15 */ +#define ICM42670_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14) /**< SCL pin: P1.14 */ +#define ICM42670_INT1_PIN NRF_GPIO_PIN_MAP(1,13) /**< INT1 pin: P1.13 (data-ready interrupt) */ +#define ICM42670_INT2_PIN NRF_GPIO_PIN_MAP(0,26) /**< INT2 pin: P0.26 (auxiliary, currently unused) */ -/* I2C 전송 래퍼 — no_stop=true이면 Repeated START를 위해 STOP 컨디션 생략 */ +/* I2C transmit wrapper — if no_stop=true, STOP condition is omitted for Repeated START */ uint32_t icm42670_twi_tx( uint8_t device_id, uint8_t const * p_data, uint8_t length, bool no_stop); -/* I2C 수신 래퍼 */ +/* I2C receive wrapper */ uint32_t icm42670_twi_rx( uint8_t device_id, uint8_t * p_data, uint8_t length); -/* 범용 I2C 읽기 (디버그/레거시용) — 8바이트를 읽어 첫 바이트 반환 */ +/* Generic I2C read (debug/legacy) — reads 8 bytes and returns the first byte */ uint8_t cat_read (uint8_t device_id, uint8_t address, uint8_t *data); -/* 범용 I2C 쓰기 (디버그/레거시용) — 주소+데이터 2바이트 전송 */ +/* Generic I2C write (debug/legacy) — sends address+data 2 bytes */ void cat_write (uint8_t device_id, uint8_t address, uint8_t *data); -/* I2C 하드웨어 해제 (슬립 또는 재초기화 전 호출) */ +/* Release I2C hardware (called before sleep or re-initialization) */ void inv_i2c_master_uninitialize(void); -/* I2C 하드웨어 초기화 (100kHz, 블로킹 모드) */ +/* Initialize I2C hardware (100kHz, blocking mode) */ void inv_i2c_master_initialize(void); -/* IMU 드라이버용 시리얼 인터페이스 초기화 (I2C/SPI 분기) */ +/* Initialize serial interface for IMU driver (I2C/SPI branch) */ int inv_io_hal_init(struct inv_imu_serif *serif); -/* IMU 드라이버 콜백: 레지스터 읽기 */ +/* IMU driver callback: register read */ int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen); -/* IMU 드라이버 콜백: 레지스터 쓰기 */ +/* IMU driver callback: register write */ int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen); #endif /* !_SYSTEM_INTERFACE_H_ */ diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.c b/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.c index c20a8b1..b2c4608 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.c +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.c @@ -6,16 +6,16 @@ * * @details Uses Timer2 + GPIOTE + PPI for CPU-free 2MHz waveform generation * - * Timing Diagram (???): + * Timing Diagram (TX sequence): * * |<----------- PE HIGH ----------->| - * PE ___/?????????????????????????????????\___ - * P_OUT ___/?\_/?\_/?\_/?\_/?\________________\___ - * N_OUT ___\_/?\_/?\_/?\_/?\_/________________\___ - * DMP _________________________/?????\_________ + * PE ___/-------------------------------------\___ + * P_OUT ___/-\_/-\_/-\_/-\_/-\________________\___ + * N_OUT ___\_/-\_/-\_/-\_/-\_/________________\___ + * DMP _________________________/-----\_________ * |<-- 3~5 cycles -->| || * - * P_OUT? N_OUT? ?? ?? ?? (???) + * P_OUT and N_OUT toggle in anti-phase (complementary) * * 2MHz = 500ns period = 250ns half-period * Timer @ 16MHz: 1 tick = 62.5ns @@ -23,68 +23,47 @@ * Full-period = 500ns = 8 ticks ******************************************************************************/ -/******************************************************************************* - * [한국어 설명] 피에조 초음파 트랜스듀서 드라이버 +/*============================================================================== + * Module overview — Piezo ultrasound transducer driver * - * === 개요 === - * 방광 측정용 2MHz 초음파 송신 신호를 생성하는 드라이버. - * nRF52840의 하드웨어 주변장치(Timer2 + GPIOTE + PPI)를 활용하여 - * CPU 개입 없이 정밀한 2MHz 파형을 자동으로 생성한다. + * Generates 2 MHz ultrasound TX signals for bladder measurement using + * nRF52840 hardware peripherals (Timer2 + GPIOTE + PPI) for CPU-free + * precise waveform generation. * - * === 초음파 TX 시퀀스 === - * 1단계: PE(Pulse Enable) = HIGH -> MOSFET 드라이버 활성화 - * 2단계: P_OUT/N_OUT 교번 펄스 생성 (2MHz, 3~7 사이클) - * - P_OUT과 N_OUT은 역상(반대 위상)으로 동작 - * - 피에조 소자 양단에 +/-20V 교번 전압 인가 - * 3단계: DMP(Dump) = HIGH -> 펄스 완료 후 피에조에 남은 잔류 에너지 방전 - * 4단계: DMP = LOW -> 방전 완료 - * 5단계: PE = LOW -> MOSFET 드라이버 비활성화, 유휴 상태 복귀 + * TX sequence: + * 1. PE = HIGH -> enable MOSFET driver + * 2. P_OUT / N_OUT alternating pulses (2 MHz, 3..7 cycles) + * P_OUT and N_OUT are anti-phase, applying +/-20V across the piezo + * 3. DMP = HIGH -> dump residual piezo energy + * 4. DMP = LOW -> discharge complete + * 5. PE = LOW -> return to idle * - * === 하드웨어 아키텍처 === - * [Timer2] --- CC[0](반주기=4틱) ---> [PPI CH8,9] ---> [GPIOTE CH4,5] -> P_OUT/N_OUT 토글 - * |-- CC[1](전체주기=8틱) --> [PPI CH10,11] --> [GPIOTE CH4,5] -> P_OUT/N_OUT 토글 - * |-- CC[2](전체주기=8틱) --> 타이머 자동 클리어 + 인터럽트(잔여 사이클 카운트) + * HW architecture: + * Timer2 CC[0] (half-period = 4 ticks) -> PPI CH8,9 -> GPIOTE CH4,5 toggle + * Timer2 CC[1] (full-period = 8 ticks) -> PPI CH10,11 -> GPIOTE CH4,5 toggle + * Timer2 CC[2] (full-period = 8 ticks) -> auto-clear + IRQ (cycle counter) * - * - Timer2: 16MHz 클럭, 16비트 모드 - * - CC[0] = 4틱(250ns) -> 반주기 시점에서 P_OUT/N_OUT 토글 - * - CC[1] = 8틱(500ns) -> 전체주기 시점에서 P_OUT/N_OUT 토글 - * - CC[2] = 8틱(500ns) -> 인터럽트 발생 + 타이머 자동 클리어(SHORT) + * Power: DR_PIEZO_PWR_EN (P1.9) -> DC/DC -> +/-20V * - * - GPIOTE CH4: P_OUT 핀 토글 모드 - * - GPIOTE CH5: N_OUT 핀 토글 모드 - * - * - PPI CH8: CC[0] 이벤트 -> P_OUT 토글 태스크 - * - PPI CH9: CC[0] 이벤트 -> N_OUT 토글 태스크 - * - PPI CH10: CC[1] 이벤트 -> P_OUT 토글 태스크 - * - PPI CH11: CC[1] 이벤트 -> N_OUT 토글 태스크 - * - * === 전원 === - * DR_PIEZO_PWR_EN(P1.9) -> DC/DC 컨버터 활성화 -> +/-20V 고전압 생성 - * - * === 소프트웨어 버스트 모드 (dr_piezo_burst_sw 계열) === - * Timer/PPI 대신 CPU에서 직접 GPIO를 제어하는 방식. - * 인터럽트를 비활성화(__disable_irq)하고 NOP 명령어로 정밀 타이밍 생성. - * 포트 레지스터(NRF_P1->OUT)에 직접 접근하여 여러 핀을 동시에 제어. - * 주파수별(1.7/1.8/1.9/2.0/2.1/2.2 MHz) NOP 개수가 다름. - * - * NOP 타이밍 계산법: - * CPU 클럭 64MHz -> 1 NOP = 15.625ns - * 목표 반주기(ns) = 1,000,000 / (목표주파수MHz * 2) - * 필요 NOP 수 = (목표 반주기 - 레지스터 쓰기 시간(~30ns)) / 15.625 - * 루프 오버헤드(~47ns = 3 NOP)는 두 번째 반주기에서 차감 - ******************************************************************************/ + * SW burst mode (dr_piezo_burst_sw_* family): + * Direct GPIO via port register (NRF_P1->OUT), interrupts disabled, + * NOP-based timing. Per-frequency NOP count differs. + * NOP timing: CPU 64 MHz -> 1 NOP = 15.625 ns + * half_period_ns = 1 000 000 / (freq_MHz * 2) + * NOP_count = (half_period - register_write ~30 ns) / 15.625 + * loop overhead (~47 ns = 3 NOP) subtracted from second half-period + *============================================================================*/ -/* 헤더 포함 */ #include "dr_piezo.h" -#include "nrf_gpio.h" /* GPIO 제어 (핀 설정, 출력) */ -#include "nrf_timer.h" /* 타이머 주변장치 제어 */ -#include "nrf_gpiote.h" /* GPIOTE (GPIO Tasks and Events) 제어 */ -#include "nrf_ppi.h" /* PPI (Programmable Peripheral Interconnect) 제어 */ -#include "nrf_delay.h" /* 지연(딜레이) 함수 */ -#include "power_control.h" /* 전원 관리 */ -#include "app_util_platform.h" /* 인터럽트 우선순위 등 플랫폼 유틸리티 */ +#include "nrf_gpio.h" +#include "nrf_timer.h" +#include "nrf_gpiote.h" +#include "nrf_ppi.h" +#include "nrf_delay.h" +#include "power_control.h" +#include "app_util_platform.h" -/* 조건부 디버그 출력: FEATURE_PRINTF 정의 시 SEGGER RTT로 출력 */ +/* Debug output: enabled when FEATURE_PRINTF is defined */ #ifdef FEATURE_PRINTF #include "debug_print.h" #else @@ -92,76 +71,72 @@ #endif /*============================================================================== - * 하드웨어 리소스 할당 - * - Timer2: 2MHz 파형 생성의 시간 기준 (16MHz 클럭) - * - GPIOTE CH4/5: P_OUT/N_OUT 핀의 하드웨어 토글 - * - PPI CH8~11: 타이머 비교 이벤트 -> GPIOTE 토글 태스크 자동 연결 + * Hardware resource allocation + * Timer2: 16 MHz clock, time base for 2 MHz waveform + * GPIOTE CH4/5: P_OUT / N_OUT hardware toggle + * PPI CH8..11: timer compare events -> GPIOTE toggle tasks *============================================================================*/ -#define PIEZO_TIMER NRF_TIMER2 /* 사용할 타이머 인스턴스 */ -#define PIEZO_TIMER_IRQn TIMER2_IRQn /* 타이머2 인터럽트 번호 */ -#define PIEZO_TIMER_IRQ_PRIORITY 6 /* 인터럽트 우선순위 (6 = 중간) */ +#define PIEZO_TIMER NRF_TIMER2 +#define PIEZO_TIMER_IRQn TIMER2_IRQn +#define PIEZO_TIMER_IRQ_PRIORITY 6 -/* GPIOTE 채널 할당 - 핀 토글 제어용 */ -#define GPIOTE_CH_P_OUT 4 /* P_OUT(양극 출력) 토글용 GPIOTE 채널 */ -#define GPIOTE_CH_N_OUT 5 /* N_OUT(음극 출력) 토글용 GPIOTE 채널 */ +#define GPIOTE_CH_P_OUT 4 /* P_OUT toggle */ +#define GPIOTE_CH_N_OUT 5 /* N_OUT toggle */ -/* PPI 채널 할당 - 타이머 이벤트 -> GPIOTE 태스크 연결 */ -#define PPI_CH_P_OUT_TOGGLE_0 8 /* CC[0](반주기) -> P_OUT 토글 */ -#define PPI_CH_N_OUT_TOGGLE_0 9 /* CC[0](반주기) -> N_OUT 토글 */ -#define PPI_CH_P_OUT_TOGGLE_1 10 /* CC[1](전체주기) -> P_OUT 토글 */ -#define PPI_CH_N_OUT_TOGGLE_1 11 /* CC[1](전체주기) -> N_OUT 토글 */ +#define PPI_CH_P_OUT_TOGGLE_0 8 /* CC[0] (half-period) -> P_OUT */ +#define PPI_CH_N_OUT_TOGGLE_0 9 /* CC[0] (half-period) -> N_OUT */ +#define PPI_CH_P_OUT_TOGGLE_1 10 /* CC[1] (full-period) -> P_OUT */ +#define PPI_CH_N_OUT_TOGGLE_1 11 /* CC[1] (full-period) -> N_OUT */ /*============================================================================== - * 타이밍 상수 - * Timer2 클럭: 16MHz -> 1틱 = 62.5ns - * 2MHz 신호: 주기 500ns(8틱), 반주기 250ns(4틱) + * Timing constants + * Timer2 @ 16 MHz: 1 tick = 62.5 ns + * 2 MHz signal: period 500 ns (8 ticks), half-period 250 ns (4 ticks) *============================================================================*/ -#define TIMER_FREQ_MHZ 16 /* 타이머 클럭 주파수 (MHz) */ -#define TICK_NS (1000 / TIMER_FREQ_MHZ) /* 1틱 = 62.5ns */ +#define TIMER_FREQ_MHZ 16 +#define TICK_NS (1000 / TIMER_FREQ_MHZ) /* 62.5 ns */ -/* 2MHz 기준 타이밍: 주기 = 500ns, 반주기 = 250ns */ -#define PERIOD_TICKS_2MHZ 8 /* 전체 주기: 500ns / 62.5ns = 8틱 */ -#define HALF_PERIOD_TICKS 4 /* 반주기: 250ns / 62.5ns = 4틱 */ +/* 2 MHz reference timing: period = 500 ns, half-period = 250 ns */ +#define PERIOD_TICKS_2MHZ 8 /* full period: 500 ns / 62.5 ns = 8 ticks */ +#define HALF_PERIOD_TICKS 4 /* half period: 250 ns / 62.5 ns = 4 ticks */ /*============================================================================== - * 피에조 동작 주파수 설정 - *============================================================================*/ -/* - * 목표 피에조 주파수: 2.1 MHz (하드웨어 버스트 모드용) + * Piezo operating frequency * - * 소프트웨어 버스트 모드의 타이밍은 각 주파수별 함수에 NOP 개수로 하드코딩됨. - * 안정적인 파형 생성을 위해 컴파일 타임에 고정. + * Target: 2.1 MHz (HW burst mode). + * SW burst timing is hard-coded per-frequency via NOP count. * - * NOP 기반 타이밍 계산 (CPU 64MHz, 1 NOP = 15.625ns): - * 1.7 MHz: 반주기 294ns -> 첫 반주기 18 NOP, 둘째 반주기 10 NOP + 루프 오버헤드 - * 1.8 MHz: 반주기 278ns -> 첫 반주기 17 NOP, 둘째 반주기 11 NOP + 루프 오버헤드 - * 1.9 MHz: 반주기 263ns -> 첫 반주기 15 NOP, 둘째 반주기 9 NOP + 루프 오버헤드 - * 2.0 MHz: 반주기 250ns -> 첫 반주기 15 NOP, 둘째 반주기 10 NOP + 루프 오버헤드 - * 2.1 MHz: 반주기 238ns -> 첫 반주기 14 NOP, 둘째 반주기 9 NOP + 루프 오버헤드 - * 2.2 MHz: 반주기 227ns -> 첫 반주기 13 NOP, 둘째 반주기 8 NOP + 루프 오버헤드 + * NOP timing (CPU 64 MHz, 1 NOP = 15.625 ns): + * 1.7 MHz: half 294 ns -> 1st half 18 NOP, 2nd half 10 NOP + loop overhead + * 1.8 MHz: half 278 ns -> 1st half 17 NOP, 2nd half 11 NOP + loop overhead + * 1.9 MHz: half 263 ns -> 1st half 15 NOP, 2nd half 9 NOP + loop overhead + * 2.0 MHz: half 250 ns -> 1st half 15 NOP, 2nd half 10 NOP + loop overhead + * 2.1 MHz: half 238 ns -> 1st half 14 NOP, 2nd half 9 NOP + loop overhead + * 2.2 MHz: half 227 ns -> 1st half 13 NOP, 2nd half 8 NOP + loop overhead * - * 주파수를 변경하려면 dr_piezo_burst_sw_XXmhz() 함수의 NOP 수를 수정할 것. - */ -#define PIEZO_FREQ_MHZ 2.1f /* 기본 동작 주파수 (MHz) */ - -/*============================================================================== - * 정적 변수 + * To change frequency, modify NOP count in dr_piezo_burst_sw_XXmhz(). *============================================================================*/ -static volatile bool m_tx_active = false; /* TX 송신 중 플래그 (인터럽트에서 변경) */ -static volatile uint8_t m_remaining_cycles = 0; /* 남은 펄스 사이클 수 (인터럽트에서 감소) */ -static uint32_t m_period_ticks = PERIOD_TICKS_2MHZ; /* 현재 주기 (타이머 틱 단위) */ -static bool m_power_enabled = false; /* DC/DC 컨버터 전원 상태 */ -static bool m_initialized = false; /* 드라이버 초기화 완료 여부 */ +#define PIEZO_FREQ_MHZ 2.1f /* default operating frequency (MHz) */ /*============================================================================== - * 타이머2 인터럽트 핸들러 - * 매 주기(CC[2])마다 호출되어 잔여 사이클을 감소시킨다. - * 잔여 사이클이 0이 되면: - * 1) 타이머 정지 및 클리어 - * 2) GPIOTE 비활성화 (P_OUT/N_OUT 토글 중단) - * 3) GPIO를 출력 모드로 재설정 후 LOW로 초기화 - * 4) DMP 펄스 발생 (피에조 잔류 에너지 방전) - * 5) PE = LOW (MOSFET 드라이버 비활성화) + * Static variables + *============================================================================*/ +static volatile bool m_tx_active = false; /* TX in progress (modified in IRQ) */ +static volatile uint8_t m_remaining_cycles = 0; /* remaining pulse cycles (decremented in IRQ) */ +static uint32_t m_period_ticks = PERIOD_TICKS_2MHZ; /* current period (timer ticks) */ +static bool m_power_enabled = false; /* DC/DC converter state */ +static bool m_initialized = false; /* driver initialised flag */ + +/*============================================================================== + * Timer2 IRQ handler + * + * Called every period (CC[2]) to decrement the remaining cycle count. + * When zero: + * 1) Stop and clear timer + * 2) Disable GPIOTE (stop P_OUT/N_OUT toggling) + * 3) Reconfigure GPIOs as push-pull output, drive LOW + * 4) Generate DMP pulse (discharge residual piezo energy) + * 5) PE = LOW (disable MOSFET driver) *============================================================================*/ void TIMER2_IRQHandler(void) { @@ -180,11 +155,11 @@ void TIMER2_IRQHandler(void) nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP); nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_CLEAR); - /* Step 2: Disable GPIOTE (GPIO ??? ??) */ + /* Step 2: Disable GPIOTE (return GPIO to SW control) */ nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); - /* Step 3: GPIO? ?? ? idle ?? */ + /* Step 3: Reconfigure GPIOs and set idle state */ nrf_gpio_cfg_output(DR_PIEZO_PIN_P_OUT); nrf_gpio_cfg_output(DR_PIEZO_PIN_N_OUT); nrf_gpio_pin_clear(DR_PIEZO_PIN_P_OUT); @@ -207,12 +182,10 @@ void TIMER2_IRQHandler(void) } /*============================================================================== - * 전원 제어 함수 - * DC/DC 컨버터(±20V)를 ON/OFF하여 피에조 구동 전압을 제어한다. - * 전원 안정화에 약 10ms 필요. + * Power control + * Switches the DC/DC converter (+/-20V) for piezo driving voltage. + * ~10 ms needed for power stabilisation. *============================================================================*/ - -/* 피에조 전원 ON: DC/DC 컨버터 활성화 → ±20V 생성 */ void dr_piezo_power_on(void) { //nrf_delay_ms(20); @@ -228,12 +201,12 @@ void dr_piezo_power_on(void) //DBG_PRINTF("[PIEZO] TX/RX Active: +/-20V ready\r\n"); } -/* 피에조 전원 OFF: TX 비활성화 → MUX 비활성화 → DC/DC 컨버터 차단 */ +/* Power OFF: disable TX -> disable MUX -> shut down DC/DC */ void dr_piezo_power_off(void) { dr_piezo_disable(); - /* MUX enable 핀 클리어: select_channel() 이후 HIGH로 남은 핀 해제 */ + /* clear MUX enable pins left HIGH by select_channel() */ nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); @@ -246,17 +219,17 @@ void dr_piezo_power_off(void) //DBG_PRINTF("[PIEZO] Power OFF\r\n"); } -/* 피에조 전원 상태 확인 */ +/* Check piezo power state */ bool dr_piezo_is_power_on(void) { return m_power_enabled; } /*============================================================================== - * 내부(private) 초기화 함수 + * Internal (private) initialisation *============================================================================*/ -/* GPIO 초기화: 모든 신호 핀을 출력 모드로 설정하고 LOW(유휴)로 초기화 */ +/* Configure all signal pins as push-pull output, drive LOW (idle) */ static void dr_piezo_gpio_init(void) { nrf_gpio_cfg_output(DR_PIEZO_PIN_PE); @@ -274,7 +247,7 @@ static void dr_piezo_gpio_init(void) dr_piezo_mux_init(); } -/* GPIOTE 초기화: P_OUT/N_OUT을 토글 모드로 설정 (PPI 연결 대상) */ +/* GPIOTE init: configure P_OUT/N_OUT in toggle mode (PPI targets) */ static void dr_piezo_gpiote_init(void) { /* P_OUT: Toggle mode, initial LOW */ @@ -297,10 +270,10 @@ static void dr_piezo_gpiote_init(void) } /* - * 타이머 초기화: 16MHz 클럭, 16비트 모드 - * CC[0]=반주기(4틱): 반주기 시점 토글 이벤트 - * CC[1]=전체주기(8틱): 전체주기 시점 토글 이벤트 - * CC[2]=전체주기(8틱): 인터럽트 발생 + 타이머 자동 클리어(SHORT) + * Timer init: 16 MHz clock, 16-bit mode + * CC[0] = half-period (4 ticks): toggle event at half-period + * CC[1] = full-period (8 ticks): toggle event at full-period + * CC[2] = full-period (8 ticks): IRQ + auto-clear (SHORT) */ static void dr_piezo_timer_init(void) { @@ -312,31 +285,31 @@ static void dr_piezo_timer_init(void) nrf_timer_frequency_set(PIEZO_TIMER, NRF_TIMER_FREQ_16MHz); /* - * ??? ??? ?? ???: - * - CC[0] = 4 (half period): P_OUT ??, N_OUT ?? - * - CC[1] = 8 (full period): P_OUT ??, N_OUT ?? - * - CC[2] = 8 (full period): ??? ??? + CLEAR - * - * ?? ??: P=HIGH, N=LOW - * t=4: ? ? ?? -> P=LOW, N=HIGH - * t=8: ? ? ?? -> P=HIGH, N=LOW (+ CLEAR) - * t=12: ? ? ?? -> P=LOW, N=HIGH + * Compare channel assignment: + * - CC[0] = 4 (half period): toggle P_OUT, toggle N_OUT + * - CC[1] = 8 (full period): toggle P_OUT, toggle N_OUT + * - CC[2] = 8 (full period): cycle count IRQ + CLEAR + * + * Initial state: P=HIGH, N=LOW + * t=4: 1st toggle -> P=LOW, N=HIGH + * t=8: 2nd toggle -> P=HIGH, N=LOW (+ CLEAR) + * t=12: 1st toggle -> P=LOW, N=HIGH * ... */ - /* CC[0]: ??? ?? */ + /* CC[0]: half-period toggle */ nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL0, HALF_PERIOD_TICKS); // 4 - /* CC[1]: ? ?? ?? */ + /* CC[1]: full-period toggle */ nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL1, m_period_ticks); // 8 - /* CC[2]: ? ?? - ??? ???? */ + /* CC[2]: full-period - cycle count IRQ source */ nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL2, m_period_ticks); // 8 - /* CC[2]?? ?? CLEAR */ + /* Auto-CLEAR on CC[2] match */ nrf_timer_shorts_enable(PIEZO_TIMER, NRF_TIMER_SHORT_COMPARE2_CLEAR_MASK); - /* CC[2] ???? (??? ???) */ + /* CC[2] interrupt enable (cycle counter) */ nrf_timer_int_enable(PIEZO_TIMER, NRF_TIMER_INT_COMPARE2_MASK); NVIC_SetPriority(PIEZO_TIMER_IRQn, PIEZO_TIMER_IRQ_PRIORITY); @@ -344,17 +317,17 @@ static void dr_piezo_timer_init(void) } /* - * PPI 초기화: 타이머 비교 이벤트 → GPIOTE 토글 태스크 연결 (4채널) - * CC[0] 이벤트(반주기) → P_OUT 토글 + N_OUT 토글 - * CC[1] 이벤트(전체주기) → P_OUT 토글 + N_OUT 토글 - * 이로써 P_OUT과 N_OUT은 항상 역상으로 동작함. + * PPI init: connect timer compare events to GPIOTE toggle tasks (4 channels) + * CC[0] event (half-period) -> P_OUT toggle + N_OUT toggle + * CC[1] event (full-period) -> P_OUT toggle + N_OUT toggle + * This ensures P_OUT and N_OUT always operate in anti-phase. */ static void dr_piezo_ppi_init(void) { /* - * ??? ??: - * CC[0] (t=4): P_OUT ??, N_OUT ?? - * CC[1] (t=8): P_OUT ??, N_OUT ?? + * Connection map: + * CC[0] (t=4): P_OUT toggle, N_OUT toggle + * CC[1] (t=8): P_OUT toggle, N_OUT toggle */ /* CC[0] -> P_OUT toggle */ @@ -391,10 +364,10 @@ static void dr_piezo_ppi_init(void) } /*============================================================================== - * TX 드라이버 공개 함수 + * TX driver public functions *============================================================================*/ -/* TX 드라이버 초기화: GPIO → GPIOTE → Timer → PPI 순서로 설정 */ +/* TX driver init: configure GPIO -> GPIOTE -> Timer -> PPI in order */ void dr_piezo_init(void) { dr_piezo_gpio_init(); @@ -407,7 +380,7 @@ void dr_piezo_init(void) m_initialized = true; } -/* TX 드라이버 해제: 타이머 정지, PPI/GPIOTE 비활성화, 모든 핀 LOW */ +/* TX driver deinit: stop timer, disable PPI/GPIOTE, all pins LOW */ void dr_piezo_uninit(void) { nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP); @@ -432,11 +405,11 @@ void dr_piezo_uninit(void) } /* - * 하드웨어 기반 버스트 송신 (Timer + PPI + GPIOTE 사용) - * 1) GPIOTE를 재설정: P_OUT=HIGH 시작, N_OUT=LOW 시작 (역상) - * 2) PE = HIGH → MOSFET 드라이버 활성화 - * 3) 타이머 시작 → PPI가 자동으로 P_OUT/N_OUT 토글 - * 4) 인터럽트 핸들러에서 잔여 사이클 관리 및 종료 처리 + * Hardware-based burst transmit (using Timer + PPI + GPIOTE) + * 1) Reconfigure GPIOTE: P_OUT starts HIGH, N_OUT starts LOW (anti-phase) + * 2) PE = HIGH -> enable MOSFET driver + * 3) Start timer -> PPI automatically toggles P_OUT/N_OUT + * 4) IRQ handler manages remaining cycles and shutdown sequence */ void dr_piezo_burst(uint8_t cycles) { @@ -460,7 +433,7 @@ void dr_piezo_burst(uint8_t cycles) m_remaining_cycles = cycles; m_tx_active = true; - /* GPIOTE ??? */ + /* Reconfigure GPIOTE */ nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); @@ -491,23 +464,23 @@ void dr_piezo_burst(uint8_t cycles) nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE1); nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE2); - /* PE = HIGH (??? ??) */ + /* PE = HIGH (enable driver) */ nrf_gpio_pin_set(DR_PIEZO_PIN_PE); - /* ??? ?? ? ??? ?? */ + /* Settling delay before starting timer */ __NOP(); __NOP(); __NOP(); __NOP(); - /* Timer START -> PPI? ???? P_OUT/N_OUT ?? */ + /* Timer START -> PPI automatically toggles P_OUT/N_OUT */ nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_START); } -/* 기본 사이클 수(5)로 버스트 송신 */ +/* Burst transmit with default cycle count (5) */ void dr_piezo_pulse(void) { dr_piezo_burst(DR_PIEZO_DEFAULT_CYCLES); } -/* TX 출력 활성화: 수동으로 초기 상태 설정 (PE=HIGH, P_OUT=HIGH, N_OUT=LOW) */ +/* Enable TX output: manually set initial state (PE=HIGH, P_OUT=HIGH, N_OUT=LOW) */ void dr_piezo_enable(void) { nrf_gpio_pin_set(DR_PIEZO_PIN_P_OUT); @@ -517,7 +490,7 @@ void dr_piezo_enable(void) DBG_PRINTF("[DR_PIEZO] TX enabled\r\n"); } -/* TX 출력 비활성화: 모든 신호 핀 LOW로 복귀 (유휴 상태) */ +/* Disable TX output: all signal pins return to LOW (idle) */ void dr_piezo_disable(void) { nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); @@ -526,13 +499,13 @@ void dr_piezo_disable(void) nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); } -/* TX 송신 중 여부 확인 (인터럽트 핸들러에서 false로 전환) */ +/* Check if TX is in progress (cleared to false in IRQ handler) */ bool dr_piezo_is_busy(void) { return m_tx_active; } -/* 동작 주파수 변경 (100kHz~4MHz): 타이머 CC 레지스터 재설정 */ +/* Change operating frequency (100kHz~4MHz): reconfigure timer CC registers */ void dr_piezo_set_frequency(uint32_t freq_hz) { if (freq_hz < 100000 || freq_hz > 4000000) @@ -552,14 +525,14 @@ void dr_piezo_set_frequency(uint32_t freq_hz) } /*============================================================================== - * MUX 제어 함수 - * 8채널 아날로그 MUX로 피에조 에코 신호 경로를 선택한다. - * MUXA: CH0~CH3 담당, MUXB: CH4~CH7 담당 - * SEL0, SEL1: MUX 내부 채널 주소 선택 - * High Drive(H0H1) 모드: MUX IC의 빠른 스위칭을 위해 강한 출력 구동력 사용 + * MUX control functions + * 8-channel analog MUX selects piezo echo signal path. + * MUXA: CH0~CH3, MUXB: CH4~CH7 + * SEL0, SEL1: MUX internal channel address selection + * High Drive (H0H1) mode: strong output drive for fast MUX IC switching *============================================================================*/ -/* MUX 제어 핀 초기화: 4개 핀 모두 High Drive 출력으로 설정, 기본값 LOW */ +/* MUX control pin init: all 4 pins as High Drive output, default LOW */ void dr_piezo_mux_init(void) { /* Configure pins as output with high drive strength */ @@ -607,17 +580,17 @@ void dr_piezo_mux_init(void) /* - * 피에조 채널 선택 (0~7) - * 채널 매핑 (EN_MUXA, EN_MUXB, SEL0, SEL1): - * CH0 = MUXA 입력0 (1,0,0,0) CH4 = MUXB 입력0 (0,1,1,1) - * CH1 = MUXA 입력2 (1,0,1,0) CH5 = MUXB 입력1 (0,1,0,1) - * CH2 = MUXA 입력1 (1,0,0,1) CH6 = MUXB 입력2 (0,1,1,0) - * CH3 = MUXA 입력3 (1,0,1,1) CH7 = MUXB 입력3 (0,1,0,0) - * 채널 전환 후 MUX 안정화 대기 시간(1.3ms) 필요. + * Select piezo channel (0~7) + * Channel mapping (EN_MUXA, EN_MUXB, SEL0, SEL1): + * CH0 = MUXA input0 (1,0,0,0) CH4 = MUXB input0 (0,1,1,1) + * CH1 = MUXA input2 (1,0,1,0) CH5 = MUXB input1 (0,1,0,1) + * CH2 = MUXA input1 (1,0,0,1) CH6 = MUXB input2 (0,1,1,0) + * CH3 = MUXA input3 (1,0,1,1) CH7 = MUXB input3 (0,1,0,0) + * MUX settling time (~1.3ms) required after channel switch. */ void dr_piezo_select_channel(uint8_t channel) { - channel = channel & 0x07; /* 0~7 범위로 마스킹 */ + channel = channel & 0x07; /* Mask to 0~7 range */ switch (channel) { // EN_A EN_B SEL0 SEL1 @@ -655,11 +628,11 @@ void dr_piezo_select_channel(uint8_t channel) break; } - /* 채널 변경 시 MUX 안정화 시간 필요 (> 1.2ms) */ + /* MUX settling time required after channel change (> 1.2ms) */ nrf_delay_us(DR_PIEZO_MUX_SETTLING_US); } -/* 핀 테스트: 각 신호 핀을 순서대로 HIGH/LOW 토글 (오실로스코프 확인용) */ +/* Pin test: toggle each signal pin HIGH/LOW in sequence (for oscilloscope verification) */ void dr_piezo_test_pins(void) { DBG_PRINTF("[DR_PIEZO] Pin test...\r\n"); @@ -700,24 +673,24 @@ void dr_piezo_test_pins(void) } /*============================================================================== - * 시스템 함수 (전원 + TX 드라이버 통합 제어) + * System functions (power + TX driver unified control) *============================================================================*/ -/* 시스템 전체 초기화: TX 드라이버 초기화 → 전원 ON (부팅 시 1회만 호출) */ +/* Full system init: TX driver init -> power ON (call once at boot) */ void dr_piezo_system_init(void) { - dr_piezo_init(); /* GPIO/GPIOTE/Timer/PPI 먼저 안전 상태로 설정 */ - dr_piezo_power_on(); /* 그 다음 DC/DC 전원 인가 */ + dr_piezo_init(); /* Set GPIO/GPIOTE/Timer/PPI to safe state first */ + dr_piezo_power_on(); /* Then apply DC/DC power */ } -/* 시스템 전체 종료: TX 드라이버 해제 → 전원 OFF */ +/* Full system shutdown: deinit TX driver -> power OFF */ void dr_piezo_system_uninit(void) { dr_piezo_uninit(); dr_piezo_power_off(); } -/* 전원 확인 후 버스트 송신 (블로킹: 송신 완료까지 대기) */ +/* Burst transmit with power check (blocking: waits until TX complete) */ void dr_piezo_transmit(uint8_t cycles) { if (!m_power_enabled) @@ -732,73 +705,74 @@ void dr_piezo_transmit(uint8_t cycles) } /*============================================================================== - * 소프트웨어 기반 버스트 모드 + * Software-based burst mode * 2025-12-11 Charles KWON *============================================================================== * - * Timer/PPI/GPIOTE 하드웨어 대신 CPU에서 직접 GPIO 레지스터를 조작하여 - * 초음파 펄스를 생성하는 방식. 인터럽트를 비활성화하여 정확한 타이밍 보장. + * Generates ultrasound pulses by directly manipulating GPIO registers from + * the CPU, instead of using Timer/PPI/GPIOTE hardware. Interrupts are + * disabled to guarantee precise timing. * - * === 타이밍 다이어그램 === + * === Timing diagram === * - * |<-마진->|<----- 펄스들 ----->|<-- DMP -->|<-마진->| + * |<-margin->|<---- pulses ----->|<-- DMP -->|<-margin->| * * PE ___/--------------------------------------------------\___ * P_OUT ___________/-\_/-\_/-\_/-\_/-\____________________\_______ * N_OUT ___________\_/-\_/-\_/-\_/-\_/____________________\_______ * DMP __________________________________/----------\____________ * - * === 신호 설명 === - * - PE (Pulse Enable): 전체 시퀀스를 감싸는 활성화 신호 (전후 마진 포함) - * - P_OUT: 양극 출력, N_OUT과 역상으로 2MHz 토글 - * - N_OUT: 음극 출력, P_OUT과 역상으로 2MHz 토글 - * - DMP (Dump): 펄스 완료 후 피에조 잔류 에너지 방전 + * === Signal description === + * - PE (Pulse Enable): activation signal wrapping the entire sequence (with margins) + * - P_OUT: positive output, toggles at 2MHz in anti-phase with N_OUT + * - N_OUT: negative output, toggles at 2MHz in anti-phase with P_OUT + * - DMP (Dump): discharges residual piezo energy after pulse completion * - * === 동작 원리 === - * 1) __disable_irq()로 인터럽트 차단 → 타이밍 흔들림 방지 - * 2) PE ON (P0.25 OUTSET 레지스터 사용 → 다른 P0 핀 영향 없음) - * 3) NOP 마진 후 for 루프로 P_OUT/N_OUT 교번 출력 - * 4) DMP 펄스 (32 NOP ≒ 500ns) - * 5) PE OFF 후 __enable_irq()로 인터럽트 복원 + * === Operating principle === + * 1) __disable_irq() blocks interrupts -> prevents timing jitter + * 2) PE ON (via P0.25 OUTSET register -> no effect on other P0 pins) + * 3) NOP margin, then for-loop alternates P_OUT/N_OUT + * 4) DMP pulse (32 NOP ~ 500ns) + * 5) PE OFF, then __enable_irq() restores interrupts * - * === 포트 레지스터 직접 접근 === - * NRF_P1->OUT 레지스터에 미리 계산된 비트 마스크를 직접 기록. - * 이 방식으로 P_OUT, N_OUT, DMP를 동시에 제어하면서도 - * 채널 선택 핀(MUX SEL)은 보존한다 (P1_CTRL_MASK로 제어 핀만 변경). - * PE는 P0 포트에 있으므로 OUTSET/OUTCLR 레지스터로 별도 제어. + * === Direct port register access === + * Pre-calculated bitmasks are written directly to NRF_P1->OUT register. + * This allows simultaneous control of P_OUT, N_OUT, DMP while preserving + * channel select pins (MUX SEL) via P1_CTRL_MASK (only control pins change). + * PE is on P0 port, so it is controlled separately via OUTSET/OUTCLR registers. *============================================================================*/ -/* 핀 번호에서 포트 내 비트 위치 추출 (하위 5비트 = 0~31) */ +/* Extract bit position within port from pin number (lower 5 bits = 0~31) */ #define PIN_NUM(pin) ((pin) & 0x1F) -/* P1 포트 핀의 비트 마스크 - dr_piezo.h의 핀 정의에서 자동 생성 +/* P1 port pin bitmasks - auto-generated from pin definitions in dr_piezo.h * - * 경고: 핀 번호를 절대 하드코딩하지 말 것! - * 하드코딩은 개발자의 시간을 일시적으로 절약해줄 수 있지만, - * 동시에 개발자의 수명을 단축시킬 것이다. + * WARNING: Never hardcode pin numbers! + * Hardcoding may save a developer's time temporarily, + * but it will also shorten that developer's lifespan. * - Charles KWON * - * 각 마스크는 해당 핀의 포트 레지스터 내 비트 위치를 나타낸다. - * NRF_P1->OUT에 직접 쓸 때 사용되며, 여러 핀을 동시에 제어 가능. + * Each mask represents the bit position within the port register. + * Used for direct writes to NRF_P1->OUT, enabling simultaneous multi-pin control. */ -#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.07 양극 출력 */ -#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.06 음극 출력 */ -#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P0.25 펄스 활성화 */ -#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.00 방전 제어 */ +#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.07 positive output */ +#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.06 negative output */ +#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P0.25 pulse enable */ +#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.00 dump control */ -/* P1 포트에서 피에조 제어에 사용하는 핀들의 결합 마스크 (채널 선택 핀 제외) */ +/* Combined mask of piezo control pins on P1 port (excludes channel select pins) */ #define P1_CTRL_MASK (P_OUT_MASK | N_OUT_MASK | DMP_MASK) /* - * 소프트웨어 버스트 - 기본 주파수 2.1MHz + * Software burst - default frequency 2.1MHz * - * NOP 타이밍 계산: - * 2.1MHz → 주기 476ns, 반주기 238ns - * CPU 64MHz → 1 NOP = 15.625ns - * 첫 반주기: 14 NOP(≒219ns) + 레지스터 쓰기(≒30ns) = ≒249ns - * 둘째 반주기: 9 NOP(≒141ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒218ns - * 합계: ≒436~476ns (≒2.1MHz) + * NOP timing calculation: + * 2.1MHz -> period 476ns, half-period 238ns + * CPU 64MHz -> 1 NOP = 15.625ns + * 1st half: 14 NOP (~219ns) + register write (~30ns) = ~249ns + * 2nd half: 9 NOP (~141ns) + loop overhead (~47ns) + register write (~30ns) = ~218ns + * Total: ~436-476ns (~2.1MHz) */ void dr_piezo_burst_sw(uint8_t cycles) { @@ -839,7 +813,7 @@ void dr_piezo_burst_sw(uint8_t cycles) * IMPORTANT: Channel select pins (P1.11, P1.12) are preserved in all states *------------------------------------------------------------------------*/ - // PE는 OUTSET/OUTCLR 사용 (다른 P0 핀 영향 방지) + // PE uses OUTSET/OUTCLR (prevents affecting other P0 pins) uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK; uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK; @@ -852,11 +826,11 @@ void dr_piezo_burst_sw(uint8_t cycles) __disable_irq(); /* Initialize: Set all signals to LOW */ - NRF_P0->OUTCLR = PE_MASK; // PE OFF (OUTCLR로 다른 핀 영향 없음) + NRF_P0->OUTCLR = PE_MASK; // PE OFF (OUTCLR does not affect other pins) NRF_P1->OUT = p1_all_low; /* PE rises first with margin before pulses start */ - NRF_P0->OUTSET = PE_MASK; // PE ON (OUTSET로 다른 핀 영향 없음) + NRF_P0->OUTSET = PE_MASK; // PE ON (OUTSET does not affect other pins) __NOP(); __NOP(); __NOP(); /* ~47ns margin */ __NOP(); __NOP(); __NOP(); /* ~47ns margin */ @@ -947,11 +921,11 @@ void dr_piezo_burst_sw(uint8_t cycles) * Second half: 12 NOPs (~188ns) + loop overhead */ /* - * 소프트웨어 버스트 - 1.8MHz - * NOP 타이밍: 반주기 278ns - * 첫 반주기: 17 NOP(≒266ns) + 레지스터 쓰기(≒30ns) = ≒296ns - * 둘째 반주기: 11 NOP(≒172ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒249ns - * 합계: ≒499~556ns (≒1.8MHz) + * Software burst - 1.8MHz + * NOP timing: half-period 278ns + * 1st half: 17 NOP (~266ns) + register write (~30ns) = ~296ns + * 2nd half: 11 NOP (~172ns) + loop overhead (~47ns) + register write (~30ns) = ~249ns + * Total: ~499-556ns (~1.8MHz) */ void dr_piezo_burst_sw_18mhz(uint8_t cycles) { @@ -979,7 +953,7 @@ void dr_piezo_burst_sw_18mhz(uint8_t cycles) //NRF_P0->OUT = saved_p0_out; - /* Pre-calculate P1 output states (PE는 P0.25 - OUTSET/OUTCLR로 제어) */ + /* Pre-calculate P1 output states (PE on P0.25 - controlled via OUTSET/OUTCLR) */ uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK; uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK; uint32_t p1_P_low_N_high = p1_all_low | N_OUT_MASK; @@ -1076,11 +1050,11 @@ void dr_piezo_burst_sw_18mhz(uint8_t cycles) * Total: ~468-500ns per cycle */ /* - * 소프트웨어 버스트 - 2.0MHz - * NOP 타이밍: 반주기 250ns - * 첫 반주기: 15 NOP(≒234ns) + 레지스터 쓰기(≒30ns) = ≒264ns - * 둘째 반주기: 10 NOP(≒156ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒233ns - * 합계: ≒468~500ns (≒2.0MHz) + * Software burst - 2.0MHz + * NOP timing: half-period 250ns + * 1st half: 15 NOP (~234ns) + register write (~30ns) = ~264ns + * 2nd half: 10 NOP (~156ns) + loop overhead (~47ns) + register write (~30ns) = ~233ns + * Total: ~468-500ns (~2.0MHz) */ void dr_piezo_burst_sw_20mhz(uint8_t cycles) { @@ -1107,7 +1081,7 @@ void dr_piezo_burst_sw_20mhz(uint8_t cycles) //NRF_P0->OUT = saved_p0_out; NRF_P0->OUT = (NRF_P0->OUT & ~PE_MASK) | (saved_p0_out & PE_MASK); - /* Pre-calculate P1 output states (PE는 P0.25 - OUTSET/OUTCLR로 제어) */ + /* Pre-calculate P1 output states (PE on P0.25 - controlled via OUTSET/OUTCLR) */ uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK; uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK; uint32_t p1_P_low_N_high = p1_all_low | N_OUT_MASK; @@ -1206,11 +1180,11 @@ void dr_piezo_burst_sw_20mhz(uint8_t cycles) * Total: ~468-500ns per cycle */ /* - * 소프트웨어 버스트 - 1.9MHz - * NOP 타이밍: 반주기 263ns - * 첫 반주기: 15 NOP(≒234ns) + 레지스터 쓰기(≒30ns) = ≒264ns - * 둘째 반주기: 9 NOP(≒141ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒218ns - * 합계: ≒468~500ns (≒1.9MHz) + * Software burst - 1.9MHz + * NOP timing: half-period 263ns + * 1st half: 15 NOP (~234ns) + register write (~30ns) = ~264ns + * 2nd half: 9 NOP (~141ns) + loop overhead (~47ns) + register write (~30ns) = ~218ns + * Total: ~468-500ns (~1.9MHz) */ void dr_piezo_burst_sw_19mhz(uint8_t cycles) { @@ -1237,7 +1211,7 @@ void dr_piezo_burst_sw_19mhz(uint8_t cycles) //NRF_P0->OUT = saved_p0_out; NRF_P0->OUT = (NRF_P0->OUT & ~PE_MASK) | (saved_p0_out & PE_MASK); - /* Pre-calculate P1 output states (PE는 P0.25 - OUTSET/OUTCLR로 제어) */ + /* Pre-calculate P1 output states (PE on P0.25 - controlled via OUTSET/OUTCLR) */ uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK; uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK; uint32_t p1_P_low_N_high = p1_all_low | N_OUT_MASK; @@ -1338,11 +1312,11 @@ void dr_piezo_burst_sw_19mhz(uint8_t cycles) * Total: ~452ns per cycle (~2.21 MHz) */ /* - * 소프트웨어 버스트 - 2.2MHz - * NOP 타이밍: 반주기 227ns - * 첫 반주기: 13 NOP(≒203ns) + 레지스터 쓰기(≒30ns) = ≒233ns - * 둘째 반주기: 8 NOP(≒125ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒202ns - * 합계: ≒435~454ns (≒2.2MHz) + * Software burst - 2.2MHz + * NOP timing: half-period 227ns + * 1st half: 13 NOP (~203ns) + register write (~30ns) = ~233ns + * 2nd half: 8 NOP (~125ns) + loop overhead (~47ns) + register write (~30ns) = ~202ns + * Total: ~435-454ns (~2.2MHz) */ void dr_piezo_burst_sw_22mhz(uint8_t cycles) { @@ -1464,11 +1438,11 @@ void dr_piezo_burst_sw_22mhz(uint8_t cycles) * Total: ~532-588ns per cycle */ /* - * 소프트웨어 버스트 - 1.7MHz - * NOP 타이밍: 반주기 294ns - * 첫 반주기: 18 NOP(≒281ns) + 레지스터 쓰기(≒30ns) = ≒311ns - * 둘째 반주기: 10 NOP(≒156ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒233ns - * 합계: ≒532~588ns (≒1.7MHz) + * Software burst - 1.7MHz + * NOP timing: half-period 294ns + * 1st half: 18 NOP (~281ns) + register write (~30ns) = ~311ns + * 2nd half: 10 NOP (~156ns) + loop overhead (~47ns) + register write (~30ns) = ~233ns + * Total: ~532-588ns (~1.7MHz) */ void dr_piezo_burst_sw_17mhz(uint8_t cycles) { @@ -1494,7 +1468,7 @@ void dr_piezo_burst_sw_17mhz(uint8_t cycles) NRF_P1->OUT = saved_p1_out; NRF_P0->OUT = (NRF_P0->OUT & ~PE_MASK) | (saved_p0_out & PE_MASK); - /* Pre-calculate P1 output states (PE는 P0에서 OUTSET/OUTCLR로 제어) */ + /* Pre-calculate P1 output states (PE on P0 - controlled via OUTSET/OUTCLR) */ uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK; uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK; uint32_t p1_P_low_N_high = p1_all_low | N_OUT_MASK; diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.h b/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.h index 229369d..4aaeaa4 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.h +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.h @@ -1,61 +1,30 @@ -/******************************************************************************* - * @file dr_piezo.h - * @brief Piezo Transducer Driver (2MHz Signal Generator) - * @author Charles KWON - * @date 2025-12-09 +/*============================================================================== + * dr_piezo.h - Piezo Transducer Driver (2 MHz Signal Generator) * - * @note Hardware: nRF52840 + MD1822K6-G MOSFET Driver + TC7920K6-G MOSFET - * Output: +/-20V at 2MHz, 3~5 cycles + * Hardware: nRF52840 + MD1822K6-G MOSFET Driver + TC7920K6-G MOSFET + * Output: +/-20V at 2 MHz, 3..7 cycles burst * - * @details Timing Sequence: - * 1. PE = HIGH (enable) - * 2. P_OUT/N_OUT = 2MHz pulses (3~5 cycles) - * 3. DMP = HIGH (dump) - * 4. DMP = LOW - * 5. PE = LOW (disable) + * Timing Sequence: + * 1. PE = HIGH (enable) + * 2. P_OUT/N_OUT = 2 MHz pulses (3..7 cycles) + * 3. DMP = HIGH (dump residual energy) + * 4. DMP = LOW + * 5. PE = LOW (disable) * - * All signals (P_OUT, N_OUT, DMP) operate within PE HIGH period. - ******************************************************************************/ - -/******************************************************************************* - * [한국어 설명] 피에조 초음파 트랜스듀서 드라이버 헤더 + * Pin assignment: + * Power: DR_PIEZO_PWR_EN (P1.9) — DC/DC +/-20V enable + * TX: PE (P0.25), DMP (P1.0), P_OUT (P1.7), N_OUT (P1.6) + * MUX: EN_MUXA (P0.21), EN_MUXB (P0.23), SEL0 (P1.10), SEL1 (P0.28) * - * === 개요 === - * 방광 측정용 초음파 송신기의 핀 할당, 설정값, 함수 선언을 정의. - * nRF52840 + MD1822K6-G(MOSFET 드라이버) + TC7920K6-G(MOSFET) 하드웨어 구성. - * 출력: +/-20V, 2MHz, 3~7 사이클 버스트. + * MUX channel mapping (8ch): + * CH0=A0(1,0,0,0) CH1=A2(1,0,1,0) CH2=A1(1,0,0,1) CH3=A3(1,0,1,1) + * CH4=B0(0,1,1,1) CH5=B1(0,1,0,1) CH6=B2(0,1,1,0) CH7=B3(0,1,0,0) * - * === 핀 할당 === - * 전원 제어: - * - DR_PIEZO_PWR_EN (P1.9): DC/DC 컨버터 활성화 -> +/-20V 고전압 생성 - * - * TX 신호 핀 (MOSFET 드라이버 제어): - * - PE (P0.25): Pulse Enable - 전체 시퀀스 활성화/비활성화 - * - DMP (P1.0): Dump - 펄스 후 피에조 잔류 에너지 방전 - * - P_OUT (P1.7): Positive Output - 피에조 양극 구동 - * - N_OUT (P1.6): Negative Output - 피에조 음극 구동 (P_OUT과 역상) - * - * MUX 제어 핀 (8채널 에코 신호 경로 선택): - * - EN_MUXA (P0.21): MUXA 활성화 (CH0~CH3 담당) - * - EN_MUXB (P0.23): MUXB 활성화 (CH4~CH7 담당) - * - SEL0 (P1.10): MUX 내부 채널 주소 비트 0 - * - SEL1 (P0.28): MUX 내부 채널 주소 비트 1 - * - * === MUX 채널 매핑 (8채널) === - * CH0 = MUXA 입력0: EN_A=1, EN_B=0, SEL0=0, SEL1=0 - * CH1 = MUXA 입력2: EN_A=1, EN_B=0, SEL0=1, SEL1=0 - * CH2 = MUXA 입력1: EN_A=1, EN_B=0, SEL0=0, SEL1=1 - * CH3 = MUXA 입력3: EN_A=1, EN_B=0, SEL0=1, SEL1=1 - * CH4 = MUXB 입력0: EN_A=0, EN_B=1, SEL0=1, SEL1=1 - * CH5 = MUXB 입력1: EN_A=0, EN_B=1, SEL0=0, SEL1=1 - * CH6 = MUXB 입력2: EN_A=0, EN_B=1, SEL0=1, SEL1=0 - * CH7 = MUXB 입력3: EN_A=0, EN_B=1, SEL0=0, SEL1=0 - * - * === 두 가지 버스트 모드 === - * 1) 하드웨어 버스트 (dr_piezo_burst): Timer2 + PPI + GPIOTE 사용, CPU 비의존적 - * 2) 소프트웨어 버스트 (dr_piezo_burst_sw_XXmhz): CPU NOP 기반 정밀 타이밍 - * - 주파수별 전용 함수: 1.7/1.8/1.9/2.0/2.1/2.2 MHz - ******************************************************************************/ + * Two burst modes: + * 1) HW burst (dr_piezo_burst): Timer2 + PPI + GPIOTE, CPU-independent + * 2) SW burst (dr_piezo_burst_sw_XXmhz): CPU NOP-based precise timing + * Per-frequency functions: 1.7 / 1.8 / 1.9 / 2.0 / 2.1 / 2.2 MHz + *============================================================================*/ #ifndef DR_PIEZO_H #define DR_PIEZO_H @@ -65,212 +34,92 @@ #include "nrf_gpio.h" /*============================================================================== - * 전원 제어 핀 (DC/DC 컨버터 +/-20V) - * DR_PIEZO_PWR_EN: HIGH로 설정 시 DC/DC 컨버터가 +/-20V 고전압 생성 + * Power control pin (+/-20V DC/DC converter) *============================================================================*/ -#define DR_PIEZO_PWR_EN NRF_GPIO_PIN_MAP(1, 9) /** Power Enable jhChun 0128 */ +#define DR_PIEZO_PWR_EN NRF_GPIO_PIN_MAP(1, 9) /*============================================================================== - * TX 신호 핀 (MOSFET 드라이버 제어) - * PE: Pulse Enable - 전체 TX 시퀀스 활성화/비활성화 - * DMP: Dump - 펄스 후 피에조 잔류 에너지 방전용 - * P_OUT: Positive Output - 피에조 양극 구동 (N_OUT과 역상) - * N_OUT: Negative Output - 피에조 음극 구동 (P_OUT과 역상) - * 주의: 이전 핀 할당(주석 처리)에서 새 보드 레이아웃으로 변경됨 (jhChun 0128) + * TX signal pins (MOSFET driver) + * PE: Pulse Enable — activates the entire TX sequence + * DMP: Dump — discharges residual piezo energy after burst + * P_OUT: Positive output — drives piezo positive terminal + * N_OUT: Negative output — drives piezo negative terminal (inverted P_OUT) *============================================================================*/ -#define DR_PIEZO_PIN_PE NRF_GPIO_PIN_MAP(0, 25) /**< Pulse Enable */ // P1.05 -> P0.25 -#define DR_PIEZO_PIN_DMP NRF_GPIO_PIN_MAP(1, 0) /**< Dump control */ // P1.9 -> P1.0 -#define DR_PIEZO_PIN_P_OUT NRF_GPIO_PIN_MAP(1, 7) /**< Positive output */ // P1.3 -> P1.7 -#define DR_PIEZO_PIN_N_OUT NRF_GPIO_PIN_MAP(1, 6) /**< Negative output */ // P1.2 -> P1.6 jhChun 0128 - +#define DR_PIEZO_PIN_PE NRF_GPIO_PIN_MAP(0, 25) /**< Pulse Enable */ +#define DR_PIEZO_PIN_DMP NRF_GPIO_PIN_MAP(1, 0) /**< Dump control */ +#define DR_PIEZO_PIN_P_OUT NRF_GPIO_PIN_MAP(1, 7) /**< Positive output */ +#define DR_PIEZO_PIN_N_OUT NRF_GPIO_PIN_MAP(1, 6) /**< Negative output */ + /*============================================================================== - * MUX 제어 핀 (에코 신호 경로 선택) - * 8채널 아날로그 MUX로 피에조 센서 채널을 선택한다. - * MUXA(CH0~CH3)와 MUXB(CH4~CH7) 두 개의 4채널 MUX 사용. - * EN_MUXA/EN_MUXB: 각 MUX 활성화 (동시에 하나만 HIGH) - * SEL0/SEL1: MUX 내부 4채널 중 하나를 선택하는 주소 비트 + * MUX control pins (echo signal path selection, 8 channels) + * MUXA handles CH0..CH3, MUXB handles CH4..CH7. + * Only one MUX is enabled at a time. *============================================================================*/ -/* Piezo MUX pins (8ch) jhChun 0129 */ #define DR_PIEZO_EN_MUXA NRF_GPIO_PIN_MAP(0, 21) /**< MUXA Enable */ #define DR_PIEZO_EN_MUXB NRF_GPIO_PIN_MAP(0, 23) /**< MUXB Enable */ #define DR_PIEZO_MUX_SEL0 NRF_GPIO_PIN_MAP(1, 10) /**< MUX Select 0 */ #define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(0, 28) /**< MUX Select 1 */ /*============================================================================== - * 설정값 - * DR_PIEZO_FREQ_HZ: 목표 주파수 (실제 동작 주파수는 dr_piezo.c에서 결정) - * DR_PIEZO_DEFAULT_CYCLES: 기본 버스트 사이클 수 (5) - * DR_PIEZO_MIN/MAX_CYCLES: 허용 사이클 범위 (3~7) - * DR_PIEZO_MUX_SETTLING_US: MUX 채널 전환 후 아날로그 경로 안정화 대기 시간 + * Configuration *============================================================================*/ -/** - * @note Actual operating frequency is defined in dr_piezo.c as PIEZO_FREQ_MHZ. - * Change PIEZO_FREQ_MHZ in dr_piezo.c to adjust the burst frequency. - * Current setting: 2.1 MHz - */ #define DR_PIEZO_FREQ_HZ 2100000 /**< Target frequency (set PIEZO_FREQ_MHZ in .c) */ -#define DR_PIEZO_DEFAULT_CYCLES 5 /**< Default burst cycles (3~5) */ +#define DR_PIEZO_DEFAULT_CYCLES 5 /**< Default burst cycles */ #define DR_PIEZO_MIN_CYCLES 3 #define DR_PIEZO_MAX_CYCLES 7 #define DR_PIEZO_MUX_SETTLING_US 1300 /**< MUX settling delay (us) */ /*============================================================================== - * 전원 제어 함수 - * DC/DC 컨버터(+/-20V)를 ON/OFF하여 피에조 구동 고전압을 제어한다. + * Power control *============================================================================*/ -/** - * @brief Power ON piezo system (+/-20V DC/DC converter) - */ void dr_piezo_power_on(void); - -/** - * @brief Power OFF piezo system - */ void dr_piezo_power_off(void); -/** - * @brief 피에조 전원 상태 확인 - * @return true: 전원 ON, false: 전원 OFF - */ +/** @return true if power is ON */ bool dr_piezo_is_power_on(void); /*============================================================================== - * TX 드라이버 함수 - * 초음파 송신 관련: 초기화, 버스트 송신, 활성화/비활성화, 주파수 설정 + * TX driver *============================================================================*/ -/** - * @brief Initialize piezo TX driver (Timer + PPI + GPIOTE) - */ void dr_piezo_init(void); - -/** - * @brief Uninitialize piezo TX driver - */ void dr_piezo_uninit(void); - -/** - * @brief Transmit a burst of 2MHz pulses - * @param cycles Number of cycles to transmit (3~10) - */ void dr_piezo_burst(uint8_t cycles); - -/** - * @brief Transmit default burst (5 cycles) - */ void dr_piezo_pulse(void); - -/** - * @brief Enable TX output (prepare for transmission) - */ void dr_piezo_enable(void); - -/** - * @brief Disable TX output (return to idle state) - */ void dr_piezo_disable(void); - -/** - * @brief Check if TX is currently active - * @return true if transmitting - */ bool dr_piezo_is_busy(void); - -/** - * @brief Set TX frequency (for testing) - * @param freq_hz Frequency in Hz (100kHz ~ 4MHz) - */ void dr_piezo_set_frequency(uint32_t freq_hz); - -/** - * @brief Test all pins manually (for debugging with oscilloscope) - */ void dr_piezo_test_pins(void); - -/** - * @brief Initialize MUX control pins for echo signal path - */ void dr_piezo_mux_init(void); /** - * @brief Select piezo channel (0~7) via 8ch MUX - * @param channel Piezo channel number (0~7) - * - * Channel mapping (EN_MUXA, EN_MUXB, SEL0, SEL1): - * CH0=A0(1,0,0,0) CH1=A2(1,0,1,0) CH2=A1(1,0,0,1) CH3=A3(1,0,1,1) - * CH4=B0(0,1,1,1) CH5=B1(0,1,0,1) CH6=B2(0,1,1,0) CH7=B3(0,1,0,0) - * - * @note MUX settling time: 1.3ms delay after switching + * @brief Select piezo channel (0..7) via 8ch MUX + * @note MUX settling time: ~1.3 ms delay after switching */ void dr_piezo_select_channel(uint8_t channel); /*============================================================================== - * 시스템 함수 (전원 + TX 통합 제어) - * 전원 ON/OFF와 TX 드라이버 초기화/해제를 한 번에 수행하는 편의 함수. - * 소프트웨어 버스트(burst_sw) 계열: CPU NOP 기반 정밀 타이밍. - * - Timer/PPI 없이 CPU에서 직접 GPIO를 제어 - * - 인터럽트 비활성화 상태에서 동작하여 타이밍 정확도 보장 - * - 주파수별 전용 함수 제공 (NOP 개수가 다름) + * System functions (power + TX combined) *============================================================================*/ -/** - * @brief Full system initialization (power + TX driver) - */ void dr_piezo_system_init(void); - -/** - * @brief Full system shutdown - */ void dr_piezo_system_uninit(void); - -/** - * @brief Transmit with power check - * @param cycles Number of cycles - */ void dr_piezo_transmit(uint8_t cycles); -/** - * @brief Software-based burst (CPU-controlled, no Timer/PPI) - * @param cycles Number of cycles (1~20) - * @note Default frequency: 2.1 MHz - */ -void dr_piezo_burst_sw(uint8_t cycles); +/*============================================================================== + * Software burst — CPU NOP-based precise timing, no Timer/PPI + * + * Interrupts are disabled during burst for timing accuracy. + * Per-frequency functions (NOP count varies): + *============================================================================*/ -/** - * @brief Software-based burst at 1.8 MHz - * @param cycles Number of cycles (1~20) - * @note Fixed frequency: 1.8 MHz - */ -void dr_piezo_burst_sw_18mhz(uint8_t cycles); - -/** - * @brief Software-based burst at 2.0 MHz - * @param cycles Number of cycles (1~20) - * @note Fixed frequency: 2.0 MHz - */ -void dr_piezo_burst_sw_20mhz(uint8_t cycles); - -/** - * @brief Software-based burst at 2.2 MHz - * @param cycles Number of cycles (1~20) - * @note Fixed frequency: 2.2 MHz - */ -void dr_piezo_burst_sw_22mhz(uint8_t cycles); - -/** - * @brief Software-based burst at 1.7 MHz - * @param cycles Number of cycles (1~20) - * @note Fixed frequency: 1.7 MHz - */ -void dr_piezo_burst_sw_17mhz(uint8_t cycles); - -/** - * @brief Software-based burst at 1.9 MHz - * @param cycles Number of cycles (1~20) - * @note Fixed frequency: 1.9 MHz - */ -void dr_piezo_burst_sw_19mhz(uint8_t cycles); +void dr_piezo_burst_sw(uint8_t cycles); /**< 2.1 MHz (default) */ +void dr_piezo_burst_sw_18mhz(uint8_t cycles); /**< 1.8 MHz */ +void dr_piezo_burst_sw_20mhz(uint8_t cycles); /**< 2.0 MHz */ +void dr_piezo_burst_sw_22mhz(uint8_t cycles); /**< 2.2 MHz */ +void dr_piezo_burst_sw_17mhz(uint8_t cycles); /**< 1.7 MHz */ +void dr_piezo_burst_sw_19mhz(uint8_t cycles); /**< 1.9 MHz */ #endif /* DR_PIEZO_H */ - diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/temperature/tmp235_q1.c b/project/ble_peripheral/ble_app_bladder_patch/measurement/temperature/tmp235_q1.c index c4f85b1..79bcb06 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/temperature/tmp235_q1.c +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/temperature/tmp235_q1.c @@ -1,27 +1,14 @@ -/******************************************************************************* - * @file tmp235_q1.c - * @author CandyPops Co. - * @version V1.0.0 - * @date 2022-09-05 - * @brief - ******************************************************************************/ - -/******************************************************************************* - * [모듈 개요] TMP235-Q1 아날로그 온도센서 드라이버 +/*============================================================================== + * tmp235_q1.c - TMP235-Q1 analogue temperature sensor driver * - * TMP235-Q1은 온도에 비례하는 아날로그 전압(Vout)을 출력하는 센서 - * nRF52840 SAADC의 AIN3 채널로 Vout을 읽고, mV로 변환한 뒤 온도(°C)로 계산 + * Reads the TMP235-Q1 analogue output via SAADC AIN3 and converts to deg C. * - * 온도 계산 공식 (구간별 선형 보간): - * - Vout <= 1500mV (0~100°C): Ta = (Vout - 500) / 10.0 - * - Vout <= 1750mV (100~125°C): Ta = (Vout - 1500) / 10.1 + 100 - * - Vout <= 2000mV (125~150°C): Ta = (Vout - 1752.5) / 10.6 + 125 - * - Vout > 2000mV: 오류 (150°C 초과) - * - * info4 모드(전체 센서 수집) 동작 순서: - * 배터리(go_batt) → 온도(go_temp) → IMU(motion_raw_data_enabled) - * 온도 측정 완료 시 go_temp=false, motion_raw_data_enabled=true 로 전환 - ******************************************************************************/ + * Temperature conversion (piecewise linear, per datasheet): + * Vout <= 1500 mV (0..100 C) : Ta = (Vout - 500) / 10.0 + * Vout <= 1750 mV (100..125 C): Ta = (Vout - 1500) / 10.1 + 100 + * Vout <= 2000 mV (125..150 C): Ta = (Vout - 1752.5) / 10.6 + 125 + * Vout > 2000 mV : out of sensor range + *============================================================================*/ #include "sdk_common.h" @@ -36,181 +23,138 @@ #include "ble_nus.h" #include "tmp235_q1.h" #include "main.h" -/* 2026-03-17: cmd_parse.h 삭제 — main.h는 이미 포함됨 */ #include "main_timer.h" #include "debug_print.h" -/* SAADC 내부 기준전압 600mV (부동소수점) */ -#define TMP235_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */ -/* 1/3 프리스케일링 보상 계수 x6 (부동소수점) */ -#define TMP235_PRE_SCALING_COMPENSATION 6.0f /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/ -/* 12비트 ADC 최대값 4096 (부동소수점, 분해능 기준) */ -#define TMP235_ADC_RES_12BITS 4096.0f /**< Maximum digital value for 12-bit ADC conversion. */ -/**@brief Macro to convert the result of ADC conversion in millivolts. - * - * @param[in] ADC_VALUE ADC result. - * - * @retval Result converted to millivolts. - */ -/* ADC 원시값 → TMP235 출력전압(mV) 변환 매크로: ADC x (600/4096) x 6 */ +/* SAADC internal reference (mV) */ +#define TMP235_REF_VOLTAGE_IN_MILLIVOLTS 600.0f +/* 1/3 prescaling compensation (x6) */ +#define TMP235_PRE_SCALING_COMPENSATION 6.0f +/* 12-bit ADC full scale */ +#define TMP235_ADC_RES_12BITS 4096.0f + +/* Convert raw ADC value to TMP235 output voltage (mV) */ #define TMP235_VOUT_IN_MILLI_VOLTS(ADC_VALUE)\ ((((ADC_VALUE) * TMP235_REF_VOLTAGE_IN_MILLIVOLTS) / TMP235_ADC_RES_12BITS) * TMP235_PRE_SCALING_COMPENSATION) -/* SAADC 변환 결과 저장 버퍼 (1채널) */ static nrf_saadc_value_t adc_buf; -extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN]; -extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ; +extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN]; +extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN]; -/* 현재 명령 소스: CMD_UART 또는 CMD_BLE */ extern which_cmd_t cmd_type_t; +extern bool info4; +extern bool go_temp; -/* info4: 전체 센서 데이터 수집 모드 플래그 */ -extern bool info4; // main.c +/* info4 mode: cached temperature (deg C x 100, integer) */ +volatile uint16_t info_temp; +extern bool motion_raw_data_enabled; -/* 온도 측정 순서 제어 플래그 */ -extern bool go_temp; // main_timer.c - -/* info4 모드에서 온도값 임시 저장 (°C x 100, 정수 표현) */ -volatile uint16_t info_temp; //48_C -extern bool motion_raw_data_enabled; - -/* SAADC 완료 플래그 — all_sensors()에서 콜백 완료 대기용 */ +/* SAADC completion flag — used by all_sensors() to wait */ volatile bool tmp235_saadc_done = false; -/**@brief Function for handling the ADC interrupt. - * - * @details This function will fetch the conversion result from the ADC, convert the value into - * percentage and send it to peer. - */ -/** - * @brief TMP235 온도센서 ADC 완료 콜백 +/*============================================================================== + * tmp235_voltage_handler - SAADC conversion complete callback * - * SAADC 변환 완료 시 호출된다. - * ADC값 → Vout(mV) → 온도(°C) 순으로 변환하고, 동작 모드에 따라: - * - info4 모드: info_temp에 저장 (°C x 100 정수), 이후 IMU 측정으로 전환 - * - 일반 모드: BLE("rso:" 바이너리) 또는 UART로 온도값 전송 - */ -void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout reading */ + * ADC value -> Vout (mV) -> temperature (deg C), then: + * - info4 mode: store to info_temp (C x 100 integer) + * - Normal mode: send rso: response over BLE or UART + *============================================================================*/ +void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) { - float led_temp; /* 계산된 온도 (°C, 부동소수점) */ - float led_temp_16; /* BLE 전송용 온도 (°C x 100, 부동소수점) */ + float led_temp; + float led_temp_16; if (p_event->type == NRF_DRV_SAADC_EVT_DONE) { nrf_saadc_value_t adc_result; - float tmp235_voltage_in_milli_volts = 0; + float tmp235_voltage_in_milli_volts = 0; - /* ADC 변환 결과 읽기 */ adc_result = p_event->data.done.p_buffer[0]; - //DBG_PRINTF("[TMP] adc=%d\r\n", adc_result); - /* SAADC 해제 — 배터리/압력센서 측정과 하드웨어 공유 */ - nrf_drv_saadc_channel_uninit(0); // 채널 먼저 해제 - nrf_drv_saadc_uninit(); - //nrf_drv_saadc_uninit(); // 이전: 드라이버 먼저 해제 - //nrf_drv_saadc_channel_uninit(0); + /* Release SAADC — shared with battery / pressure ADC */ + nrf_drv_saadc_channel_uninit(0); + nrf_drv_saadc_uninit(); - /* ADC값 → TMP235 출력전압(mV) 변환 */ + /* ADC -> TMP235 output voltage (mV) */ tmp235_voltage_in_milli_volts = TMP235_VOUT_IN_MILLI_VOLTS(adc_result); - /* - * Vout → 온도(°C) 변환 (구간별 선형 보간) - * TMP235 데이터시트 기반: - * 0~100°C 구간: 기울기 10.0 mV/°C, 오프셋 500mV - * 100~125°C 구간: 기울기 10.1 mV/°C - * 125~150°C 구간: 기울기 10.6 mV/°C - */ - if(tmp235_voltage_in_milli_volts <= 1500) - { - /* 0~100°C: Ta = (Vout - 500mV) / 10.0 mV/°C */ - led_temp = (tmp235_voltage_in_milli_volts - 500.0f) / 10.0f + 0.0f; - } - else if(tmp235_voltage_in_milli_volts <= 1750) - { - /* 100~125°C: 기울기가 10.1로 약간 증가 */ - led_temp = (tmp235_voltage_in_milli_volts - 1500.0f) / 10.1f + 100.0f; - } - else if(tmp235_voltage_in_milli_volts <= 2000) - { - /* 125~150°C: 기울기가 10.6으로 더 증가 */ - led_temp = (tmp235_voltage_in_milli_volts - 1752.5f) / 10.6f + 125.0f; - } - else - { - /* 150°C 초과 — 센서 측정 범위 벗어남 */ - DBG_PRINTF("ERR!!! Temprature is over 150c\r\n"); - } + /* Vout -> temperature (piecewise linear per datasheet) */ + if(tmp235_voltage_in_milli_volts <= 1500) + { + /* 0..100 C: slope 10.0 mV/C, offset 500 mV */ + led_temp = (tmp235_voltage_in_milli_volts - 500.0f) / 10.0f + 0.0f; + } + else if(tmp235_voltage_in_milli_volts <= 1750) + { + /* 100..125 C: slope 10.1 mV/C */ + led_temp = (tmp235_voltage_in_milli_volts - 1500.0f) / 10.1f + 100.0f; + } + else if(tmp235_voltage_in_milli_volts <= 2000) + { + /* 125..150 C: slope 10.6 mV/C */ + led_temp = (tmp235_voltage_in_milli_volts - 1752.5f) / 10.6f + 125.0f; + } + else + { + /* Out of sensor range (>150 C) */ + DBG_PRINTF("ERR!!! Temperature is over 150c\r\n"); + } - /* info4 모드: 온도값을 정수(°C x 100)로 저장 (예: 36.50°C → 3650) */ - if (info4 == true) - { - info_temp = (uint16_t)(led_temp * 100); - } - /* UART 모드: 소수점 2자리까지 텍스트로 출력 */ - else if(cmd_type_t == CMD_UART) - { - DBG_PRINTF("To%.2f\r\n\r\n",led_temp); - } - /* BLE 모드: °C x 100 정수를 "rso:" 헤더로 바이너리 전송 */ - else if(cmd_type_t == CMD_BLE) - { - led_temp_16 = led_temp * 100; - single_format_data(ble_bin_buffer, "rso:", (uint16_t)led_temp_16); + if (info4 == true) + { + /* Store as integer (e.g. 36.50 C -> 3650) */ + info_temp = (uint16_t)(led_temp * 100); + } + else if(cmd_type_t == CMD_UART) + { + DBG_PRINTF("To%.2f\r\n\r\n",led_temp); + } + else if(cmd_type_t == CMD_BLE) + { + led_temp_16 = led_temp * 100; + single_format_data(ble_bin_buffer, "rso:", (uint16_t)led_temp_16); + dr_binary_tx_safe(ble_bin_buffer,3); + } - dr_binary_tx_safe(ble_bin_buffer,3); - -// sprintf(ble_tx_buffer, "To%.2f\r\n",led_temp); -// data_tx_handler(ble_tx_buffer); - } - - tmp235_saadc_done = true; + tmp235_saadc_done = true; } - } -/** - * @brief TMP235 온도센서 SAADC 초기화 및 측정 시작 +/*============================================================================== + * tmp235_init - Initialise SAADC for TMP235 and start measurement * - * AIN3 채널을 싱글엔드 모드로 설정하고 즉시 샘플링을 트리거한다. - * 결과는 tmp235_voltage_handler 콜백에서 비동기로 처리된다. - */ + * AIN3, single-ended, 12-bit, 4x oversampling, burst enabled. + * Triggers sampling immediately; result arrives via tmp235_voltage_handler. + *============================================================================*/ void tmp235_init(void) { - /* SAADC 드라이버 초기화 (4x 오버샘플링으로 노이즈 저감) */ - nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG; - saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; - saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X; - ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, tmp235_voltage_handler); - APP_ERROR_CHECK(err_code); + nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG; + saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; + saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X; + ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, tmp235_voltage_handler); + APP_ERROR_CHECK(err_code); - /* AIN3 채널 설정: TMP235-Q1 Vout 핀 (싱글엔드 입력, burst 활성화) */ - nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3); - config.burst = NRF_SAADC_BURST_ENABLED; - err_code = nrf_drv_saadc_channel_init(0, &config); - APP_ERROR_CHECK(err_code); + nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3); + config.burst = NRF_SAADC_BURST_ENABLED; + err_code = nrf_drv_saadc_channel_init(0, &config); + APP_ERROR_CHECK(err_code); - /* ADC 버퍼 등록 (1채널, 1샘플) */ - err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1); - APP_ERROR_CHECK(err_code); + err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1); + APP_ERROR_CHECK(err_code); - /* 즉시 ADC 샘플링 시작 (비동기) */ err_code = nrf_drv_saadc_sample(); APP_ERROR_CHECK(err_code); } -/* Ta = (Vout – Voffs ) / Tc + Tinfl */ -/** - * @brief 온도 측정 외부 호출 함수 +/*============================================================================== + * tmp235_voltage_level_meas - External entry point for one-shot reading * - * tmp235_init()을 호출하여 SAADC 초기화 + 측정을 일괄 수행한다. - * 내부적으로 init 시 바로 샘플링이 시작되므로 별도 sample 호출 불필요. - */ + * Calls tmp235_init() which both initialises and triggers sampling. + *============================================================================*/ void tmp235_voltage_level_meas(void) { - tmp235_init(); // init 함수에 있는 걸 그냥 여기 넣어도 - //tmp235_uninit(); + tmp235_init(); } - diff --git a/project/ble_peripheral/ble_app_bladder_patch/measurement/temperature/tmp235_q1.h b/project/ble_peripheral/ble_app_bladder_patch/measurement/temperature/tmp235_q1.h index 7cbd238..751b236 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/measurement/temperature/tmp235_q1.h +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/temperature/tmp235_q1.h @@ -1,31 +1,22 @@ -/******************************************************************************* - * @file tmp235_q1.h - * @author CandyPops Co. - * @version V1.0.0 - * @date 2022-09-05 - * @brief - ******************************************************************************/ - -/******************************************************************************* - * [헤더 개요] TMP235-Q1 아날로그 온도센서 드라이버 인터페이스 +/*============================================================================== + * tmp235_q1.h - TMP235-Q1 analogue temperature sensor driver interface * - * TMP235-Q1의 아날로그 전압 출력을 SAADC(AIN3)로 읽어 - * 온도(°C)로 변환하는 기능의 외부 호출용 API를 선언한다. + * Reads the TMP235-Q1 analogue voltage output via SAADC (AIN3) and converts + * it to temperature (deg C). * - * 주요 API: - * - tmp235_init() : SAADC 초기화 + 즉시 측정 시작 (내부 사용) - * - tmp235_voltage_level_meas() : 온도 1회 측정 (외부 호출용 래퍼) + * Conversion: Ta(C) = (Vout_mV - 500) / 10.0 (valid 0..100 C) * - * 온도 변환: Vout(mV) → Ta(°C) = (Vout - 500) / 10.0 (0~100°C 구간) - ******************************************************************************/ + * API: + * tmp235_init() : initialise SAADC + start measurement (internal) + * tmp235_voltage_level_meas() : one-shot temperature reading (external wrapper) + *============================================================================*/ #ifndef _TMP235_Q1_H_ #define _TMP235_Q1_H_ -/** @brief TMP235 SAADC 초기화 및 측정 시작 (AIN3 채널) */ +/* Initialise SAADC for TMP235 and start measurement (AIN3). */ void tmp235_init(void); -/** @brief 온도 1회 측정 외부 호출 함수 (내부적으로 tmp235_init 호출) */ +/* External entry point for a single temperature reading. */ void tmp235_voltage_level_meas(void); #endif /* !_TMP235_Q1_H_ */ - diff --git a/project/ble_peripheral/ble_app_bladder_patch/system/debug_print.h b/project/ble_peripheral/ble_app_bladder_patch/system/debug_print.h index f2519fe..2dbc86b 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/system/debug_print.h +++ b/project/ble_peripheral/ble_app_bladder_patch/system/debug_print.h @@ -1,32 +1,32 @@ // file: debug_print.h /******************************************************************************* - * [한국어 설명] 디버그 출력 매크로 (조건부 컴파일) + * Debug print macros (conditional compilation) * - * SEGGER RTT(Real Time Transfer)를 이용한 디버그 출력 매크로. - * J-Link 디버거를 통해 실시간으로 로그를 PC에 전송한다. - * UART를 사용하지 않으므로 시스템 타이밍에 미치는 영향이 적다. + * Debug output macros using SEGGER RTT (Real Time Transfer). + * Sends logs to the PC in real time via J-Link debugger. + * Minimal impact on system timing since UART is not used. * - * === 조건부 컴파일 === - * ENABLE_PRINTF = 1: DBG_PRINTF가 SEGGER_RTT_printf(채널0)로 치환됨 - * -> 실제 로그 출력 (디버깅 시 사용) - * ENABLE_PRINTF = 0: DBG_PRINTF가 빈 매크로로 치환됨 - * -> 코드에서 완전히 제거 (릴리스 빌드 시 사용) + * === Conditional compilation === + * ENABLE_PRINTF = 1: DBG_PRINTF maps to SEGGER_RTT_printf (channel 0) + * -> Actual log output (for debugging) + * ENABLE_PRINTF = 0: DBG_PRINTF maps to an empty macro + * -> Completely removed from code (for release builds) * - * === 사용법 === - * DBG_PRINTF("값: %d\r\n", value); // printf와 동일한 포맷 문자열 - * 출력은 SEGGER RTT Viewer 또는 J-Link RTT Client에서 확인. + * === Usage === + * DBG_PRINTF("val: %d\r\n", value); // Same format string as printf + * View output in SEGGER RTT Viewer or J-Link RTT Client. ******************************************************************************/ #ifndef DEBUG_PRINT_H #define DEBUG_PRINT_H -#define ENABLE_PRINTF 1 /* 1=디버그 출력 활성화, 0=전역 비활성화 */ +#define ENABLE_PRINTF 1 /* 1=Enable debug output, 0=Disable globally */ #if ENABLE_PRINTF #include "SEGGER_RTT.h" - /* SEGGER RTT 채널 0으로 포맷 문자열 출력 */ + /* Print formatted string to SEGGER RTT channel 0 */ #define DBG_PRINTF(...) SEGGER_RTT_printf(0, __VA_ARGS__) #else - /* 빈 매크로: 컴파일러가 호출 코드를 완전히 제거 */ + /* Empty macro: compiler removes all call-site code */ #define DBG_PRINTF(...) // Do nothing #endif diff --git a/project/ble_peripheral/ble_app_bladder_patch/system/led/led_control.c b/project/ble_peripheral/ble_app_bladder_patch/system/led/led_control.c index a09c442..b68ebcd 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/system/led/led_control.c +++ b/project/ble_peripheral/ble_app_bladder_patch/system/led/led_control.c @@ -1,11 +1,11 @@ /******************************************************************************* * @file led_control.c - * @brief LED 직접 제어 드라이버 (BSP 미사용) + * @brief Direct LED control driver (no BSP) * @date 2026-03-30 * - * app_timer 1개로 2색 LED(녹색/주황)의 복합 blink 패턴 구현 - * 단순 on/off 상태는 타이머 없이 즉시 GPIO 제어 - * 복합 패턴(에러 등)은 phase 기반 state machine으로 처리 + * Implements dual-color LED (green/orange) blink patterns with a single app_timer. + * Simple on/off states use immediate GPIO control without a timer. + * Complex patterns (e.g. error) are handled by a phase-based state machine. ******************************************************************************/ #include "led_control.h" @@ -13,20 +13,20 @@ #include "app_timer.h" /*============================================================================== - * 내부 상수 + * Internal constants *============================================================================*/ #define MS_TO_TICKS(ms) APP_TIMER_TICKS(ms) /*============================================================================== - * 색상 + * Colors *============================================================================*/ #define COLOR_NONE 0 #define COLOR_GREEN 1 #define COLOR_ORANGE 2 /*============================================================================== - * 에러 패턴 상수 (No.7) - * 3Hz 깜빡 3회 = 166ms on + 166ms off × 3 = ~1초, 이후 꺼짐 1초 + * Error pattern constants (No.7) + * 3Hz blink x3 = 166ms on + 166ms off x 3 = ~1s, then off for 1s *============================================================================*/ #define ERROR_BLINK_ON_MS 166 #define ERROR_BLINK_OFF_MS 166 @@ -34,42 +34,42 @@ #define ERROR_PAUSE_MS 1000 /*============================================================================== - * 패턴 테이블 (단순 blink 용) + * Pattern table (for simple blink) *============================================================================*/ typedef struct { - uint32_t on_ms; /* LED 켜짐 시간 (ms) */ - uint32_t off_ms; /* LED 꺼짐 시간 (ms) */ - uint8_t color; /* COLOR_GREEN 또는 COLOR_ORANGE */ - bool repeat; /* true: 무한 반복, false: 1회 후 OFF */ + uint32_t on_ms; /* LED on duration (ms) */ + uint32_t off_ms; /* LED off duration (ms) */ + uint8_t color; /* COLOR_GREEN or COLOR_ORANGE */ + bool repeat; /* true: repeat forever, false: once then OFF */ } led_pattern_t; static const led_pattern_t m_patterns[LED_STATE_COUNT] = { [LED_STATE_OFF] = { 0, 0, COLOR_NONE, false }, - [LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → 유지 */ - [LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → OFF */ - [LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true }, /* 초록 점멸 1초 */ - [LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, /* 초록 1초 on / 3초 off */ - [LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, /* 주황 점멸 1초 */ - [LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true }, /* 초록 3초 on / 1초 off */ - [LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true } /* 별도 state machine */ + [LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false }, /* Green on 2s, stay on */ + [LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, /* Green on 2s, then OFF */ + [LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true }, /* Green blink 1s interval */ + [LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, /* Green 1s on / 3s off */ + [LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, /* Orange blink 1s interval */ + [LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true }, /* Green 3s on / 1s off */ + [LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true } /* Separate state machine */ }; /*============================================================================== - * 모듈 변수 + * Module variables *============================================================================*/ APP_TIMER_DEF(m_led_timer); static led_state_t m_current_state = LED_STATE_OFF; -static bool m_phase_on; /* true: LED 켜진 구간, false: 꺼진 구간 */ +static bool m_phase_on; /* true: LED on phase, false: off phase */ -/* 에러 패턴 전용 */ -static uint8_t m_error_blink_cnt; /* 현재까지 깜빡인 횟수 */ +/* Error pattern only */ +static uint8_t m_error_blink_cnt; /* Blink count so far */ static uint8_t m_error_phase; /* 0: blink-on, 1: blink-off, 2: pause */ /*============================================================================== - * GPIO 헬퍼 + * GPIO helpers *============================================================================*/ static inline void led_green_on(void) @@ -122,7 +122,7 @@ static void led_color_on(uint8_t color) } /*============================================================================== - * 타이머 시작 헬퍼 + * Timer start helper *============================================================================*/ static void timer_start_ms(uint32_t ms) { @@ -131,7 +131,7 @@ static void timer_start_ms(uint32_t ms) } /*============================================================================== - * 에러 패턴 state machine (No.7) + * Error pattern state machine (No.7) * phase 0: LED ON (166ms) → phase 1 * phase 1: LED OFF (166ms) → cnt++ → cnt<3 ? phase 0 : phase 2 * phase 2: PAUSE (1000ms) → cnt=0, phase 0 @@ -148,30 +148,30 @@ static void error_pattern_tick(void) { switch (m_error_phase) { - case 0: /* ON 구간 끝 → OFF */ + case 0: /* ON phase done -> OFF */ led_all_off(); m_error_phase = 1; timer_start_ms(ERROR_BLINK_OFF_MS); break; - case 1: /* OFF 구간 끝 */ + case 1: /* OFF phase done */ m_error_blink_cnt++; if (m_error_blink_cnt < ERROR_BLINK_COUNT) { - /* 다시 ON */ + /* Back to ON */ m_error_phase = 0; led_color_on(COLOR_ORANGE); timer_start_ms(ERROR_BLINK_ON_MS); } else { - /* 3회 완료 → pause */ + /* 3 blinks done -> pause */ m_error_phase = 2; timer_start_ms(ERROR_PAUSE_MS); } break; - case 2: /* pause 끝 → 처음부터 */ + case 2: /* Pause done -> restart */ m_error_blink_cnt = 0; m_error_phase = 0; led_color_on(COLOR_ORANGE); @@ -184,13 +184,13 @@ static void error_pattern_tick(void) } /*============================================================================== - * 타이머 콜백 + * Timer callback *============================================================================*/ static void led_timer_handler(void * p_context) { (void)p_context; - /* 에러 상태는 별도 처리 */ + /* Error state handled separately */ if (m_current_state == LED_STATE_ERROR) { error_pattern_tick(); @@ -201,7 +201,7 @@ static void led_timer_handler(void * p_context) if (m_phase_on) { - /* ON→OFF 전환 */ + /* ON -> OFF transition */ led_all_off(); m_phase_on = false; @@ -211,40 +211,40 @@ static void led_timer_handler(void * p_context) } else if (!p->repeat) { - /* 1회성: off_ms == 0 이면 그냥 유지 (POWER_ON) 또는 끄기 (POWER_OFF) */ + /* One-shot: if off_ms == 0, stay on (POWER_ON) or turn off (POWER_OFF) */ if (m_current_state == LED_STATE_POWER_OFF) { led_all_off(); m_current_state = LED_STATE_OFF; } - /* POWER_ON: 점등 유지 상태 → 타이머 x */ + /* POWER_ON: stay lit, no timer */ } } else { - /* OFF → ON 전환 */ + /* OFF -> ON transition */ if (p->repeat) { led_color_on(p->color); m_phase_on = true; timer_start_ms(p->on_ms); } - /* repeat == false && off_ms > 0 인 경우는 현재 없음 */ + /* No current case where repeat == false && off_ms > 0 */ } } /*============================================================================== - * 공개 함수 + * Public functions *============================================================================*/ void led_init(void) { - /* GPIO 출력 설정 */ + /* Configure GPIO outputs */ nrf_gpio_cfg_output(LED_PIN_GREEN); nrf_gpio_cfg_output(LED_PIN_ORANGE); led_all_off(); - /* 타이머 생성 (single-shot) */ + /* Create timer (single-shot) */ app_timer_create(&m_led_timer, APP_TIMER_MODE_SINGLE_SHOT, led_timer_handler); m_current_state = LED_STATE_OFF; @@ -254,7 +254,7 @@ void led_set_state(led_state_t state) { if (state >= LED_STATE_COUNT) return; - /* 이전 패턴 중단 */ + /* Stop previous pattern */ app_timer_stop(m_led_timer); led_all_off(); @@ -266,15 +266,15 @@ void led_set_state(led_state_t state) switch (state) { case LED_STATE_OFF: - /* 이미 all off */ + /* Already all off */ break; - /* 에러 패턴: 별도 state machine */ + /* Error pattern: separate state machine */ case LED_STATE_ERROR: error_pattern_start(); break; - /* 그 외: on 구간부터 시작 */ + /* All others: start from ON phase */ default: led_color_on(p->color); m_phase_on = true; diff --git a/project/ble_peripheral/ble_app_bladder_patch/system/led/led_control.h b/project/ble_peripheral/ble_app_bladder_patch/system/led/led_control.h index a844466..80170a3 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/system/led/led_control.h +++ b/project/ble_peripheral/ble_app_bladder_patch/system/led/led_control.h @@ -1,10 +1,10 @@ /******************************************************************************* * @file led_control.h - * @brief LED 직접 제어 드라이버 (BSP 미사용) + * @brief Direct LED control driver (no BSP) * @date 2026-03-30 * - * 녹색(P0.12) + 주황(P0.29) 2색 LED를 app_timer 기반으로 제어 - * 각 상태별 on/off 시간, 색상, 반복 패턴을 테이블로 관리 + * Controls dual-color LED green(P0.12) + orange(P0.29) via app_timer. + * On/off timing, color, and repeat pattern managed per state in a table. ******************************************************************************/ #ifndef LED_CONTROL_H__ @@ -14,39 +14,39 @@ #include /*============================================================================== - * LED 핀 정의 + * LED pin definitions *============================================================================*/ -#define LED_PIN_GREEN NRF_GPIO_PIN_MAP(0, 12) /* 녹색 LED */ -#define LED_PIN_ORANGE NRF_GPIO_PIN_MAP(0, 29) /* 주황 LED */ -#define LED_ACTIVE_LOW 1 /* 1 = Active Low (GPIO LOW에서 LED 켜짐) */ +#define LED_PIN_GREEN NRF_GPIO_PIN_MAP(0, 12) /* Green LED */ +#define LED_PIN_ORANGE NRF_GPIO_PIN_MAP(0, 29) /* Orange LED */ +#define LED_ACTIVE_LOW 1 /* 1 = Active Low (LED on when GPIO LOW) */ /*============================================================================== - * LED 상태 열거형 + * LED state enumeration *============================================================================*/ typedef enum { - LED_STATE_OFF = 0, /* 모든 LED 소등 */ - LED_STATE_POWER_ON, /* 1: 전원 ON - 초록 소등 → 녹색 점등 2초 */ - LED_STATE_POWER_OFF, /* 2: 전원 OFF - 초록 점등 2초 → 소등 */ - LED_STATE_ADVERTISING, /* 3: 블루투스 스캐닝 - 초록 점멸 1초 간격(반복) */ - LED_STATE_DETACH_WARNING, /* 4: 정상 작동중(미부착 시) - 초록 점등 1초 / 소등 3초 (반복) */ - LED_STATE_ALIGN_SEARCHING, /* 5: 정렬모드(탐지중) - 주황 점멸 1초 간격 (반복) */ - LED_STATE_ALIGN_COMPLETE, /* 6: 정렬모드(탐지 완료) - 초록 점등 3초 / 소등 1초 */ - LED_STATE_ERROR, /* 7: 오류 발생 - 주황 3Hz 깜빡 3회 / 꺼짐 1초 (반복) */ - LED_STATE_COUNT /* 배열 크기 */ + LED_STATE_OFF = 0, /* All LEDs off */ + LED_STATE_POWER_ON, /* 1: Power ON - green on 2s */ + LED_STATE_POWER_OFF, /* 2: Power OFF - green on 2s then off */ + LED_STATE_ADVERTISING, /* 3: BLE advertising - green blink 1s interval (repeat) */ + LED_STATE_DETACH_WARNING, /* 4: Normal operation (detached) - green 1s on / 3s off (repeat) */ + LED_STATE_ALIGN_SEARCHING, /* 5: Alignment mode (searching) - orange blink 1s interval (repeat) */ + LED_STATE_ALIGN_COMPLETE, /* 6: Alignment mode (complete) - green 3s on / 1s off */ + LED_STATE_ERROR, /* 7: Error - orange 3Hz blink x3 / off 1s (repeat) */ + LED_STATE_COUNT /* Array size */ } led_state_t; /*============================================================================== - * 공개 함수 + * Public functions *============================================================================*/ -/** @brief LED GPIO 초기화 + 타이머 생성 - main()에서 1회 호출 */ +/** @brief Initialize LED GPIO + create timer - call once from main() */ void led_init(void); -/** @brief LED 상태 변경 - 이전 패턴을 즉시 중단하고 새 패턴 시작 */ +/** @brief Change LED state - immediately stops previous pattern and starts new one */ void led_set_state(led_state_t state); -/** @brief 현재 LED 상태 조회 */ +/** @brief Get current LED state */ led_state_t led_get_state(void); #endif /* LED_CONTROL_H__ */ diff --git a/project/ble_peripheral/ble_app_bladder_patch/system/main_timer.c b/project/ble_peripheral/ble_app_bladder_patch/system/main_timer.c index 1859128..e8ec55e 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/system/main_timer.c +++ b/project/ble_peripheral/ble_app_bladder_patch/system/main_timer.c @@ -2,30 +2,30 @@ TEST medi50 Dec 23 ******************************************************************************* * - * [모듈 개요] - * 메인 이벤트 루프 타이머 모듈 (10ms 간격, 싱글샷 모드). + * [Module overview] + * Main event loop timer module (10ms interval, single-shot mode). * - * 센서 데이터 수집 및 시스템 제어 이벤트를 플래그 기반으로 디스패치한다. - * app_timer 싱글샷 모드를 사용하므로, 이벤트 처리 완료 후 - * 필요 시 main_timer_start()로 수동 재시작해야 한다. + * Dispatches sensor data collection and system control events via flags. + * Uses app_timer single-shot mode; after processing, call main_timer_start() + * manually to restart if needed. * - * [이벤트 플래그 및 처리 순서] - * motion_raw_data_enabled → IMU 데이터 읽기 (icm42670_main 호출) - * - motion_data_once == true: 단발성 읽기 (HW I2C 초기화 후 1회) - * - motion_data_once == false: 연속 읽기 (BLE 전송 대기 중이 아닐 때) - * go_batt → 배터리 전압 측정 (battery_level_meas) - * go_temp → 온도 측정 (tmp235_voltage_level_meas) - * go_device_power_off → 디바이스 전원 OFF (device_power_off) - * go_sleep_mode_enter → 슬립 모드 진입 (sleep_mode_enter) - * go_NVIC_SystemReset → NVIC 시스템 리셋 + * [Event flags and processing order] + * motion_raw_data_enabled -> IMU data read (calls icm42670_main) + * - motion_data_once == true: one-shot read (after HW I2C init) + * - motion_data_once == false: continuous read (when not waiting for BLE TX) + * go_batt -> Battery voltage measurement (battery_level_meas) + * go_temp -> Temperature measurement (tmp235_voltage_level_meas) + * go_device_power_off -> Device power OFF (device_power_off) + * go_sleep_mode_enter -> Enter sleep mode (sleep_mode_enter) + * go_NVIC_SystemReset -> NVIC system reset * - * [info4 모드 측정 순서] - * IMU 연속 읽기 → go_batt(배터리) → go_temp(온도) → motion_data_once(IMU 단발) - * 온도 측정 완료 시 motion_data_once=true로 설정하여 다시 IMU로 돌아간다. + * [info4 mode measurement order] + * IMU continuous read -> go_batt (battery) -> go_temp (temp) -> motion_data_once (IMU one-shot) + * After temperature measurement, motion_data_once=true to return to IMU. * - * [타이머 설정] - * - 일반 모드: 10ms 간격 (MAIN_LOOP_INTERVAL) - * - FEATURE_DETAIL_VALUE_FULL 모드: 80ms 간격 (디테일 프린트아웃용) + * [Timer settings] + * - Normal mode: 10ms interval (MAIN_LOOP_INTERVAL) + * - FEATURE_DETAIL_VALUE_FULL mode: 80ms interval (for detail printout) * ******************************************************************************/ @@ -56,16 +56,16 @@ #include "tmp235_q1.h" //#include "fstorage.h" #include "power_control.h" -#include "main.h" /* 2026-03-17: cmd_parse.h 삭제 → main.h */ +#include "main.h" /* 2026-03-17: cmd_parse.h removed, use main.h */ #include "debug_print.h" #include "i2c_manager.h" //add cj -/* 메인 루프 싱글샷 타이머 인스턴스 */ +/* Main loop single-shot timer instance */ APP_TIMER_DEF(m_main_loop_timer_id); #if FEATURE_DETAIL_VALUE_FULL -/* 디테일 프린트아웃 모드: 80ms 간격으로 메인 루프 실행 */ -#define MAIN_LOOP_INTERVAL 80 /* 디테일 프린트아웃이 있을경우 Full_Mode Main Prosessing 수행하는 타이머 */ +/* Detail printout mode: run main loop at 80ms interval */ +#define MAIN_LOOP_INTERVAL 80 /* Timer for Full_Mode main processing when detail printout is enabled */ //extern bool pd_adc_full_a_start; //extern bool pd_adc_full_b_start; @@ -75,36 +75,36 @@ APP_TIMER_DEF(m_main_loop_timer_id); extern which_cmd_t cmd_type_t; #else -/* 일반 모드: 10ms 간격으로 메인 루프 실행 */ +/* Normal mode: run main loop at 10ms interval */ #define MAIN_LOOP_INTERVAL 10 #endif /* ========================================================================== */ -/* 이벤트 플래그 (외부 모듈에서 설정, main_loop에서 처리) */ +/* Event flags (set by external modules, processed in main_loop) */ /* ========================================================================== */ -bool go_batt= false; /* 배터리 측정 요청 플래그 */ -bool go_temp= false; /* 온도 측정 요청 플래그 */ -bool go_device_power_off = false; /* 디바이스 전원 OFF 요청 플래그 */ -bool go_sleep_mode_enter = false; /* 슬립 모드 진입 요청 플래그 */ -bool go_NVIC_SystemReset = false; /* 시스템 리셋 요청 플래그 */ -bool motion_raw_data_enabled = false; /* IMU 모션 데이터 읽기 활성화 플래그 */ -bool ble_got_new_data = false; /* BLE로 새 데이터 전송 완료 여부 */ -bool motion_data_once = false; /* IMU 단발성 읽기 모드 (true: 1회만 읽기) */ +bool go_batt= false; /* Battery measurement request flag */ +bool go_temp= false; /* Temperature measurement request flag */ +bool go_device_power_off = false; /* Device power OFF request flag */ +bool go_sleep_mode_enter = false; /* Sleep mode entry request flag */ +bool go_NVIC_SystemReset = false; /* System reset request flag */ +bool motion_raw_data_enabled = false; /* IMU motion data read enable flag */ +bool ble_got_new_data = false; /* BLE new data transmission complete */ +bool motion_data_once = false; /* IMU one-shot read mode (true: read once) */ /** - * @brief 메인 이벤트 루프 (싱글샷 타이머 콜백) + * @brief Main event loop (single-shot timer callback) * - * 플래그 기반 이벤트 디스패처로, 설정된 플래그에 따라 해당 처리를 수행한다. - * 싱글샷 타이머이므로 연속 실행이 필요한 경우 처리 내부에서 main_timer_start()를 - * 다시 호출하여 타이머를 재시작해야 한다. + * Flag-based event dispatcher; processes actions based on set flags. + * Since the timer is single-shot, call main_timer_start() again from + * within the handler to continue execution. * - * [처리 우선순위] (코드 순서대로 검사) - * 1. IMU 모션 데이터 (motion_raw_data_enabled) - * 2. 배터리 측정 (go_batt) - * 3. 온도 측정 (go_temp) - * 4. 전원 OFF (go_device_power_off) - * 5. 슬립 모드 (go_sleep_mode_enter) - * 6. 시스템 리셋 (go_NVIC_SystemReset) + * [Processing priority] (checked in code order) + * 1. IMU motion data (motion_raw_data_enabled) + * 2. Battery measurement (go_batt) + * 3. Temperature measurement (go_temp) + * 4. Power OFF (go_device_power_off) + * 5. Sleep mode (go_sleep_mode_enter) + * 6. System reset (go_NVIC_SystemReset) */ void main_loop(void * p_context) /* For x ms */ { @@ -140,36 +140,36 @@ void main_loop(void * p_context) /* For x ms */ // For Motion Data Sampling - /* ---- IMU 모션 데이터 읽기 ---- */ + /* ---- IMU motion data read ---- */ /* - * motion_raw_data_enabled가 true이면 IMU(ICM42670P) 데이터를 읽는다. + * If motion_raw_data_enabled is true, read IMU (ICM42670P) data. * * motion_data_once == true: - * 단발성 읽기 모드. HW I2C를 초기화한 후 icm42670_main()을 1회 호출. - * info4 모드에서 배터리/온도 측정 후 다시 IMU로 돌아올 때 사용. + * One-shot read mode. Initialize HW I2C then call icm42670_main() once. + * Used in info4 mode to return to IMU after battery/temp measurement. * * motion_data_once == false: - * 연속 읽기 모드. BLE 전송 대기 중(ble_got_new_data==false)이면 - * icm42670_main()을 호출하고 10ms 후 타이머를 재시작하여 반복 실행. + * Continuous read mode. If not waiting for BLE TX (ble_got_new_data==false), + * call icm42670_main() and restart timer after 10ms for repeated execution. */ if(motion_raw_data_enabled == true) { - main_timer_stop(); /* 타이머 정지 (재진입 방지) */ + main_timer_stop(); /* Stop timer (prevent re-entry) */ if(motion_data_once == true) { - /* 단발성 모드: HW I2C 초기화 후 IMU 데이터 1회 읽기 */ - hw_i2c_init_once(); /* HW TWI 모드로 전환 (400kHz) */ - icm42670_main(); /* IMU 데이터 읽기 및 처리 */ + /* One-shot mode: init HW I2C then read IMU data once */ + hw_i2c_init_once(); /* Switch to HW TWI mode (400kHz) */ + icm42670_main(); /* Read and process IMU data */ } else{ - /* 연속 모드: BLE 전송 대기 중이 아니면 반복 읽기 */ + /* Continuous mode: repeat read if not waiting for BLE TX */ if(ble_got_new_data==false){ //for(uint16_t i=0 ; i<60 ;i++) //{ DBG_PRINTF("IMU \r\n"); - icm42670_main(); /* IMU 데이터 읽기 */ - motion_raw_data_enabled = true; /* 플래그 유지 (연속 읽기) */ - main_timer_start_ms(1000); /* 1초 후 다음 IMU 읽기 */ + icm42670_main(); /* Read IMU data */ + motion_raw_data_enabled = true; /* Keep flag set (continuous read) */ + main_timer_start_ms(1000); /* Next IMU read after 1s */ } // else if(ble_got_new_data==true){ // motion_data_once = true; @@ -178,71 +178,71 @@ void main_loop(void * p_context) /* For x ms */ } - /* ---- 배터리 전압 측정 ---- */ + /* ---- Battery voltage measurement ---- */ /* - * go_batt 플래그가 true이면 배터리 레벨을 측정한다. - * info4 모드에서 IMU 연속 읽기 이후 호출되는 단계. - * 측정 완료 후 타이머가 정지된 상태로 유지된다. + * If go_batt is true, measure battery level. + * Called after IMU continuous read in info4 mode. + * Timer remains stopped after measurement. */ if(go_batt == true) { DBG_PRINTF("IMU BATT\r\n"); - main_timer_stop(); /* 타이머 정지 */ - go_batt = false; /* 플래그 소비 (1회 실행) */ + main_timer_stop(); /* Stop timer */ + go_batt = false; /* Consume flag (one-shot) */ // go_temp = true; - battery_level_meas(); /* SAADC를 이용한 배터리 전압 측정 */ + battery_level_meas(); /* Measure battery voltage via SAADC */ // nrf_delay_ms(20); // m48_adc_start_init(); // main_timer_start(); } - /* ---- 온도 측정 ---- */ + /* ---- Temperature measurement ---- */ /* - * go_temp 플래그가 true이면 TMP235-Q1 센서로 온도를 측정한다. - * info4 모드에서 배터리 측정 이후 호출되는 단계. - * 측정 완료 후 motion_data_once=true로 설정하여 - * 다음 IMU 읽기는 단발성 모드(HW I2C 재초기화)로 전환된다. + * If go_temp is true, measure temperature via TMP235-Q1 sensor. + * Called after battery measurement in info4 mode. + * After completion, sets motion_data_once=true so the next IMU read + * uses one-shot mode (with HW I2C re-init). */ if(go_temp == true) { DBG_PRINTF("IMU Temp\r\n"); - main_timer_stop(); /* 타이머 정지 */ + main_timer_stop(); /* Stop timer */ // go_batt = false; - go_temp = false; /* 플래그 소비 (1회 실행) */ - motion_data_once = true; /* 다음 IMU 읽기를 단발성 모드로 전환 */ - tmp235_voltage_level_meas(); /* TMP235-Q1 온도 센서 전압 측정 */ + go_temp = false; /* Consume flag (one-shot) */ + motion_data_once = true; /* Switch next IMU read to one-shot mode */ + tmp235_voltage_level_meas(); /* Measure TMP235-Q1 temperature sensor voltage */ // motion_raw_data_enabled = true; // main_timer_start(); } - /* ---- 시스템 제어 이벤트 처리 ---- */ + /* ---- System control event handling ---- */ - /* 디바이스 전원 OFF 처리 */ + /* Device power OFF handling */ if(go_device_power_off == true){ - main_timer_stop(); /* 타이머 정지 */ + main_timer_stop(); /* Stop timer */ DBG_PRINTF("Off main_timer\r\n"); - device_power_off(); /* 디바이스 전원 OFF 실행 */ + device_power_off(); /* Execute device power OFF */ } - /* 슬립 모드 진입 처리 */ + /* Sleep mode entry handling */ if(go_sleep_mode_enter == true){ - main_timer_stop(); /* 타이머 정지 */ + main_timer_stop(); /* Stop timer */ DBG_PRINTF("sleep main timer\r\n"); - sleep_mode_enter(); /* 슬립 모드 진입 실행 */ + sleep_mode_enter(); /* Execute sleep mode entry */ } - /* NVIC 시스템 리셋 처리 */ + /* NVIC system reset handling */ if(go_NVIC_SystemReset == true) { - main_timer_stop(); /* 타이머 정지 */ - NVIC_SystemReset(); /* ARM Cortex-M4 시스템 리셋 */ + main_timer_stop(); /* Stop timer */ + NVIC_SystemReset(); /* ARM Cortex-M4 system reset */ } } /** - * @brief 메인 루프 타이머 시작 + * @brief Start main loop timer * - * 싱글샷 모드로 MAIN_LOOP_INTERVAL(10ms 또는 80ms) 후 main_loop()를 호출 + * Single-shot mode; calls main_loop() after MAIN_LOOP_INTERVAL (10ms or 80ms) */ void main_timer_start(void) { @@ -250,9 +250,9 @@ void main_timer_start(void) } /** - * @brief 지정된 간격(ms)으로 메인 루프 타이머 시작 + * @brief Start main loop timer with specified interval (ms) * - * IMU 연속 스트리밍 등 기본 간격과 다른 주기가 필요할 때 사용 + * Used when a different period than the default is needed (e.g. IMU streaming) */ void main_timer_start_ms(uint32_t interval_ms) { @@ -260,7 +260,7 @@ void main_timer_start_ms(uint32_t interval_ms) } /** - * @brief 메인 루프 타이머 정지 + * @brief Stop main loop timer */ void main_timer_stop(void) { @@ -268,10 +268,10 @@ void main_timer_stop(void) } /** - * @brief 메인 루프 타이머 초기화 (앱 시작 시 1회 호출) + * @brief Initialize main loop timer (call once at app startup) * - * 싱글샷 모드 타이머를 생성하고, 콜백으로 main_loop()를 등록 - * 싱글샷이므로 매 호출마다 main_timer_start()로 수동 재시작해야 함 + * Creates a single-shot timer with main_loop() as callback. + * Must manually restart via main_timer_start() after each firing. */ void main_timer_init(void) { diff --git a/project/ble_peripheral/ble_app_bladder_patch/system/main_timer.h b/project/ble_peripheral/ble_app_bladder_patch/system/main_timer.h index 4192dfc..0cbaa67 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/system/main_timer.h +++ b/project/ble_peripheral/ble_app_bladder_patch/system/main_timer.h @@ -6,32 +6,32 @@ * @brief ******************************************************************************* * - * [헤더 개요] - * 메인 이벤트 루프 타이머의 공용 인터페이스 헤더. + * [Header overview] + * Public interface header for the main event loop timer. * - * 10ms(일반) 또는 80ms(디테일) 간격의 싱글샷 타이머를 사용하여 - * main_loop() 콜백에서 센서 데이터 수집 및 시스템 제어를 수행한다. + * Uses a single-shot timer at 10ms (normal) or 80ms (detail) interval. + * The main_loop() callback handles sensor data collection and system control. * - * [주요 함수] - * main_timer_start() : 타이머 시작 (싱글샷, 수동 재시작 필요) - * main_timer_stop() : 타이머 정지 - * main_timer_init() : 타이머 초기화 (앱 시작 시 1회 호출) + * [Key functions] + * main_timer_start() : Start timer (single-shot, manual restart required) + * main_timer_stop() : Stop timer + * main_timer_init() : Initialize timer (call once at app startup) * ******************************************************************************/ #ifndef TIMER_ROUTINE_H__ #define TIMER_ROUTINE_H__ -/** @brief 메인 루프 타이머 시작 (싱글샷, MAIN_LOOP_INTERVAL 후 main_loop 호출) */ +/** @brief Start main loop timer (single-shot, calls main_loop after MAIN_LOOP_INTERVAL) */ void main_timer_start(void); -/** @brief 지정된 간격(ms)으로 메인 루프 타이머 시작 */ +/** @brief Start main loop timer with specified interval (ms) */ void main_timer_start_ms(uint32_t interval_ms); -/** @brief 메인 루프 타이머 정지 */ +/** @brief Stop main loop timer */ void main_timer_stop(void); -/** @brief 메인 루프 타이머 초기화 (앱 시작 시 1회, 싱글샷 모드로 생성) */ +/** @brief Initialize main loop timer (call once at startup, creates single-shot timer) */ void main_timer_init(void); #endif //TIMER_ROUTINE_H__ diff --git a/project/ble_peripheral/ble_app_bladder_patch/system/power/power_control.c b/project/ble_peripheral/ble_app_bladder_patch/system/power/power_control.c index 7a52cd3..b357b22 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/system/power/power_control.c +++ b/project/ble_peripheral/ble_app_bladder_patch/system/power/power_control.c @@ -3,24 +3,24 @@ //=========power_control.c==================== ******************************************************************************* * - * [모듈 개요] - * 디바이스 전원 시퀀스를 관리하는 모듈 (전원 켜기 / 끄기 / 슬립). + * [Module overview] + * Device power sequence manager (power on / off / sleep). * - * [전원 켜기 흐름] + * [Power-on flow] * device_activated() - * → power_loop 타이머 시작 → 즉시 완료 (센서 초기화 불필요) - * → 센서(IMU)는 측정 명령 시 imu_read_direct()가 자체 처리 + * -> Start power_loop timer -> completes immediately (no sensor init needed) + * -> Sensor (IMU) is handled by imu_read_direct() at measurement time * - * [슬립 모드] + * [Sleep mode] * device_sleep_mode() - * → processing 플래그 해제 + * -> Clear processing flag * - * [재활성화] + * [Reactivation] * device_reactivated() - * → I2C 재초기화 후 전원 시퀀스를 처음부터 다시 시작 + * -> Re-init I2C then restart power sequence from the beginning * - * [타이머] - * 싱글샷 모드 app_timer, 20ms 간격으로 power_loop 호출 + * [Timer] + * Single-shot app_timer, calls power_loop at 20ms interval * ******************************************************************************/ #include @@ -38,53 +38,53 @@ #include "i2c_manager.h" -/* 전원 시퀀스용 싱글샷 타이머 인스턴스 */ +/* Single-shot timer instance for power sequence */ APP_TIMER_DEF(m_power_timer_id); -/* 전원 시퀀스 상태머신의 타이머 간격 (20ms) */ +/* Power sequence state machine timer interval (20ms) */ // 2025-12-08 change to #define POWER_LOOP_INTERVAL 30 -> 20 #define POWER_LOOP_INTERVAL 20 -/* 전원 시퀀스 현재 단계 (0: I2C 초기화, 1: 예약, 2: 완료) */ +/* Power sequence current step (0: I2C init, 1: reserved, 2: complete) */ static uint8_t p_order; -/* 데이터 처리 중 플래그 (외부 모듈에서 선언) */ +/* Data processing flag (declared in external module) */ extern volatile bool processing; -/* 전원 시퀀스 잠금 플래그 (true = 전원 시퀀스 진행 중) */ +/* Power sequence lock flag (true = sequence in progress) */ bool lock_check = false; /** - * @brief 디바이스 슬립 모드 진입 + * @brief Enter device sleep mode * - * 데이터 처리 플래그(processing)를 해제하여 - * 메인 루프가 더 이상 센서 데이터를 처리하지 않도록 한다. + * Clears the processing flag so the main loop stops + * processing sensor data. * - * @return 0 (항상 성공) + * @return 0 (always succeeds) */ int device_sleep_mode(void){ int rc = 0; nrf_delay_ms(2); DBG_PRINTF("Device_Sleep_Mode OK!\r\n"); nrf_delay_ms(10); - processing = false; /* 데이터 처리 플래그 해제 → 센서 처리 중단 */ + processing = false; /* Clear processing flag -> stop sensor handling */ return rc; } /** - * @brief 디바이스 전원 켜기 (전원 시퀀스 시작) + * @brief Power on device (start power sequence) * - * 전원 시퀀스 단계를 0으로 초기화하고, 잠금 플래그를 설정한 뒤 - * 타이머를 시작하여 power_loop() 상태머신을 구동한다. + * Resets the sequence step to 0, sets the lock flag, and starts + * the timer to drive the power_loop() state machine. * - * @return 0 (항상 성공) + * @return 0 (always succeeds) */ int device_activated(void){ int rc = 0; - p_order = 0; /* 상태머신 시작 단계 (Step 0: I2C 초기화) */ - lock_check =true; /* 전원 시퀀스 진행 중 잠금 */ - power_timer_start(); /* 20ms 후 power_loop() 첫 호출 */ + p_order = 0; /* State machine start step (Step 0: I2C init) */ + lock_check =true; /* Lock: power sequence in progress */ + power_timer_start(); /* First power_loop() call after 20ms */ return rc; } @@ -101,60 +101,60 @@ int device_activated(void){ * 2: Complete */ /* - * 전원 시퀀스 상태머신 (20ms 싱글샷 타이머 콜백). + * Power sequence state machine (20ms single-shot timer callback). * - * [실행 흐름] - * 1) 타이머 콜백 진입 → 타이머 정지 (싱글샷이므로) - * 2) 현재 p_order에 해당하는 초기화 단계 실행 - * 3) p_order < 2이면 다음 단계로 진행하고 타이머 재시작 - * 4) p_order == 2이면 전원 시퀀스 완료 + * [Execution flow] + * 1) Timer callback entry -> stop timer (single-shot) + * 2) Execute init step for current p_order + * 3) If p_order < 2, advance to next step and restart timer + * 4) If p_order == 2, power sequence complete * - * 센서 초기화 불필요 — imu_read_direct()가 매 측정 시 자체 처리 + * No sensor init needed -- imu_read_direct() handles it per measurement. */ void power_loop(void *p_context) { UNUSED_PARAMETER(p_context); - power_timer_stop(); /* 현재 타이머 정지 (싱글샷 → 수동 재시작 필요) */ + power_timer_stop(); /* Stop current timer (single-shot, manual restart needed) */ - /* 센서 초기화 불필요 — imu_read_direct()가 매 측정 시 자체 처리 */ - /* 추후 전원 시퀀스 단계가 필요하면 여기에 switch(p_order) 추가 */ + /* No sensor init needed -- imu_read_direct() handles it per measurement */ + /* Add switch(p_order) here if future power sequence steps are needed */ p_order = 2; /* Advance to next step or finish */ - /* 다음 단계로 진행하거나, 시퀀스 완료 처리 */ + /* Advance to next step or finish sequence */ if (p_order < 2) { - p_order++; /* 다음 단계로 이동 */ - power_timer_start(); /* 20ms 후 다음 단계 실행 */ + p_order++; /* Move to next step */ + power_timer_start(); /* Execute next step after 20ms */ } else { - /* 전원 시퀀스 전체 완료 */ + /* Power sequence fully complete */ DBG_PRINTF("[PWR] Device Activated OK!\r\n"); } } /** - * @brief 디바이스 재활성화 (슬립 복귀 시) + * @brief Reactivate device (on wake from sleep) * - * I2C를 재초기화하고, 전원 시퀀스를 처음부터 다시 시작한다. - * 슬립 모드에서 깨어날 때 호출된다. + * Re-initializes I2C and restarts the power sequence from the beginning. + * Called when waking from sleep mode. * - * @return 0 (항상 성공) + * @return 0 (always succeeds) */ int device_reactivated(void){ int rc = 0; - sw_i2c_init_once(); /* I2C 버스 재초기화 */ - nrf_delay_ms(10); /* 안정화 대기 */ - lock_check = true; /* 전원 시퀀스 진행 중 잠금 */ - p_order = 0; /* 상태머신을 Step 0부터 재시작 */ - power_timer_start(); /* 20ms 후 power_loop() 시작 */ + sw_i2c_init_once(); /* Re-initialize I2C bus */ + nrf_delay_ms(10); /* Stabilization delay */ + lock_check = true; /* Lock: power sequence in progress */ + p_order = 0; /* Restart state machine from Step 0 */ + power_timer_start(); /* Start power_loop() after 20ms */ return rc; } /** - * @brief 전원 시퀀스 타이머 시작 + * @brief Start power sequence timer * - * 싱글샷 모드로 POWER_LOOP_INTERVAL(20ms) 후 power_loop()를 호출한다. + * Single-shot mode; calls power_loop() after POWER_LOOP_INTERVAL (20ms). */ void power_timer_start(void) { @@ -163,7 +163,7 @@ void power_timer_start(void) /** - * @brief 전원 시퀀스 타이머 정지 + * @brief Stop power sequence timer */ void power_timer_stop(void) { @@ -173,10 +173,10 @@ void power_timer_stop(void) /** - * @brief 전원 시퀀스 타이머 초기화 (앱 시작 시 1회 호출) + * @brief Initialize power sequence timer (call once at app startup) * - * 싱글샷 모드 타이머를 생성하고, 콜백으로 power_loop()를 등록한다. - * 싱글샷이므로 매 단계마다 power_timer_start()로 수동 재시작해야 한다. + * Creates a single-shot timer with power_loop() as callback. + * Must manually restart via power_timer_start() after each step. */ void power_timer_init(void) //active start { diff --git a/project/ble_peripheral/ble_app_bladder_patch/system/power/power_control.h b/project/ble_peripheral/ble_app_bladder_patch/system/power/power_control.h index eb47ac6..5afc44a 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/system/power/power_control.h +++ b/project/ble_peripheral/ble_app_bladder_patch/system/power/power_control.h @@ -6,16 +6,16 @@ * @brief ******************************************************************************* * - * [헤더 개요] - * 디바이스 전원 시퀀스 관리 모듈의 공용 인터페이스 헤더. + * [Header overview] + * Public interface header for the device power sequence manager. * - * [주요 함수 요약] - * device_activated() : 전원 켜기 → power_loop 상태머신 시작 - * device_sleep_mode() : 슬립 모드 진입 (처리 중단) - * device_reactivated() : 슬립 복귀 → 전원 시퀀스 재시작 - * power_loop() : 20ms 간격 전원 시퀀스 상태머신 콜백 - * power_timer_start/stop : 전원 시퀀스 타이머 제어 - * power_timer_init() : 전원 시퀀스 타이머 초기화 (앱 시작 시 1회) + * [Key functions] + * device_activated() : Power on -> start power_loop state machine + * device_sleep_mode() : Enter sleep mode (stop processing) + * device_reactivated() : Wake from sleep -> restart power sequence + * power_loop() : 20ms power sequence state machine callback + * power_timer_start/stop : Power sequence timer control + * power_timer_init() : Initialize power sequence timer (call once at startup) * ******************************************************************************/ @@ -24,25 +24,25 @@ #include "main.h" -/** @brief 디바이스 슬립 모드 진입 (processing 해제) */ +/** @brief Enter device sleep mode (clears processing flag) */ int device_sleep_mode(void); -/** @brief 디바이스 전원 켜기 (전원 시퀀스 상태머신 시작) */ +/** @brief Power on device (start power sequence state machine) */ int device_activated(void); -/** @brief 디바이스 재활성화 (슬립 복귀 시 I2C 재초기화 후 시퀀스 재시작) */ +/** @brief Reactivate device (re-init I2C and restart sequence on wake from sleep) */ int device_reactivated(void); -/** @brief 전원 시퀀스 상태머신 (20ms 타이머 콜백, Step 0→1→2) */ +/** @brief Power sequence state machine (20ms timer callback, Step 0->1->2) */ void power_loop(void * p_context); /* For x ms */ -/** @brief 전원 시퀀스 타이머 시작 (20ms 싱글샷) */ +/** @brief Start power sequence timer (20ms single-shot) */ void power_timer_start(void); -/** @brief 전원 시퀀스 타이머 정지 */ +/** @brief Stop power sequence timer */ void power_timer_stop(void);; -/** @brief 전원 시퀀스 타이머 초기화 (앱 시작 시 1회 호출) */ +/** @brief Initialize power sequence timer (call once at app startup) */ void power_timer_init(void); #endif //_POWER_CONTROL_H_ diff --git a/project/ble_peripheral/ble_app_bladder_patch/system/security/ble_quick_security.c b/project/ble_peripheral/ble_app_bladder_patch/system/security/ble_quick_security.c index 41929b9..07ed623 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/system/security/ble_quick_security.c +++ b/project/ble_peripheral/ble_app_bladder_patch/system/security/ble_quick_security.c @@ -100,7 +100,7 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt) { ret_code_t err_code; - // DEV 모드: 보안 실패 이벤트는 SDK 핸들러에 전달하지 않음 (disconnect 방지) + // DEV mode: do not forward security failure events to SDK handler (prevent disconnect) if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) { DBG_PRINTF("Security failed: error=%d\r\n", p_evt->params.conn_sec_failed.error); @@ -133,13 +133,13 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt) p_evt->params.conn_sec_failed.error); if (m_state.dev_mode) { - // DEV 모드: 보안 실패 무시 — 연결 유지 + // DEV mode: ignore security failure, keep connection DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n"); break; } if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) { - // Key missing: 재페어링 시도, 실패 시 disconnect로 폴백 + // Key missing: attempt re-pairing, fall back to disconnect on failure err_code = pm_conn_secure(p_evt->conn_handle, true); if (err_code != NRF_ERROR_INVALID_STATE && err_code != NRF_ERROR_BUSY && @@ -147,11 +147,11 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt) APP_ERROR_CHECK(err_code); } if (err_code != NRF_SUCCESS) { - // 재페어링 불가 → disconnect + // Re-pairing not possible -> disconnect pm_handler_disconnect_on_sec_failure(p_evt); } } else { - // 기타 보안 실패 → bond 삭제 후 재페어링 시도 + // Other security failure -> delete bond then attempt re-pairing pm_peer_id_t peer_id; if (pm_peer_id_get(p_evt->conn_handle, &peer_id) == NRF_SUCCESS && peer_id != PM_PEER_ID_INVALID) {