Compare commits
76 Commits
master
..
b47e11dc08
| Author | SHA1 | Date | |
|---|---|---|---|
| b47e11dc08 | |||
| fae5957c0c | |||
| 37aff21af8 | |||
| 555a5df241 | |||
| 7db24d762f | |||
| a0eaa01d27 | |||
| fc00d41293 | |||
| 9ed0e55b10 | |||
| 06b985e178 | |||
| 5d17bb7c6e | |||
| 59fe590ce4 | |||
| 9e4eed4ae6 | |||
| 750e1116cd | |||
| 2feef0589d | |||
| af439862a0 | |||
| 3ec6afec36 | |||
| 6cb927e831 | |||
| 47a31c1eb4 | |||
| 5ee39f834e | |||
| 5128c44912 | |||
| 8fdc153ed5 | |||
| d8ef9bcb41 | |||
| 007e0b5683 | |||
| d63a9cfa3c | |||
| 217d741d31 | |||
| f464838ed4 | |||
| a150d1da96 | |||
| 11a0b3f11a | |||
| 5e27eb762d | |||
| 689ad29aa6 | |||
| aaf22d942a | |||
| 91e294da5a | |||
| 856cf084b2 | |||
| 9e48c084b4 | |||
| a9fa050bb0 | |||
| de9774a7ce | |||
| 3455bc2b38 | |||
| 153c4a027f | |||
| 7459fffc20 | |||
| 4c2948eb43 | |||
| 89cef54f86 | |||
| 923f29db82 | |||
| 92ebe8ebb4 | |||
| 5db067f483 | |||
| f666892987 | |||
| 379bf9c294 | |||
| 6fbd30244f | |||
| b820589883 | |||
| af7afff117 | |||
| dd3bce4f94 | |||
| 9580bffa65 | |||
| e461f59a5f | |||
| ed3a8d7acd | |||
| 7b8103a530 | |||
| a805c0ab78 | |||
| 0c44f32724 | |||
| 5d507a05ac | |||
| e1b4793d57 | |||
| e29196c770 | |||
| f3d6dd290d | |||
| 1aa6585725 | |||
| 1f97bee322 | |||
| 35e09eee33 | |||
| be3ebc9a63 | |||
| 35d6956de2 | |||
| 75ee2187d6 | |||
| aa3c040ae0 | |||
| 3ecd81c252 | |||
| 96a46a26dd | |||
| 39b4ffe66a | |||
| e0a504b969 | |||
| 831dbc2844 | |||
| a77919b4d3 | |||
| 590922638c | |||
| b3adfd42e6 | |||
| a8ba31871e |
@@ -216,9 +216,9 @@ uint32_t ble_dfu_buttonless_char_add(ble_dfu_buttonless_t * p_dfu)
|
|||||||
add_char_params.is_var_len = true;
|
add_char_params.is_var_len = true;
|
||||||
add_char_params.max_len = BLE_GATT_ATT_MTU_DEFAULT;
|
add_char_params.max_len = BLE_GATT_ATT_MTU_DEFAULT;
|
||||||
|
|
||||||
add_char_params.cccd_write_access = SEC_MITM;
|
add_char_params.cccd_write_access = SEC_OPEN;
|
||||||
add_char_params.write_access = SEC_MITM;
|
add_char_params.write_access = SEC_OPEN;
|
||||||
add_char_params.read_access = SEC_MITM;
|
add_char_params.read_access = SEC_OPEN;
|
||||||
|
|
||||||
return characteristic_add(p_dfu->service_handle, &add_char_params, &p_dfu->control_point_char);
|
return characteristic_add(p_dfu->service_handle, &add_char_params, &p_dfu->control_point_char);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,8 +283,8 @@ uint32_t ble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
|
|||||||
add_char_params.char_props.write = 1;
|
add_char_params.char_props.write = 1;
|
||||||
add_char_params.char_props.write_wo_resp = 1;
|
add_char_params.char_props.write_wo_resp = 1;
|
||||||
|
|
||||||
add_char_params.read_access = SEC_MITM;
|
add_char_params.read_access = SEC_OPEN;
|
||||||
add_char_params.write_access = SEC_MITM;
|
add_char_params.write_access = SEC_OPEN;
|
||||||
|
|
||||||
err_code = characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->rx_handles);
|
err_code = characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->rx_handles);
|
||||||
if (err_code != NRF_SUCCESS)
|
if (err_code != NRF_SUCCESS)
|
||||||
@@ -302,9 +302,9 @@ uint32_t ble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
|
|||||||
add_char_params.is_var_len = true;
|
add_char_params.is_var_len = true;
|
||||||
add_char_params.char_props.notify = 1;
|
add_char_params.char_props.notify = 1;
|
||||||
|
|
||||||
add_char_params.read_access = SEC_MITM;
|
add_char_params.read_access = SEC_OPEN;
|
||||||
add_char_params.write_access = SEC_MITM;
|
add_char_params.write_access = SEC_OPEN;
|
||||||
add_char_params.cccd_write_access = SEC_MITM;
|
add_char_params.cccd_write_access = SEC_OPEN;
|
||||||
|
|
||||||
return characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->tx_handles);
|
return characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->tx_handles);
|
||||||
/**@snippet [Adding proprietary characteristic to the SoftDevice] */
|
/**@snippet [Adding proprietary characteristic to the SoftDevice] */
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Compiled binaries
|
||||||
|
*.o
|
||||||
|
*.hex
|
||||||
|
*.bin
|
||||||
|
*.elf
|
||||||
|
*.map
|
||||||
|
*.lst
|
||||||
|
|
||||||
|
# Build directories
|
||||||
|
_build/
|
||||||
|
build/
|
||||||
|
output/
|
||||||
|
|
||||||
|
# IDE/Editor
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Segger/J-Link
|
||||||
|
*.jlink
|
||||||
|
|
||||||
|
# nRF specific
|
||||||
|
*.zip
|
||||||
|
sdk_config.h.bak
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
Thumbs.db
|
||||||
|
desktop.ini
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
+11
-54
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include "ble_quick_security.h"
|
#include "ble_quick_security.h"
|
||||||
#include "peer_manager_handler.h"
|
#include "peer_manager_handler.h"
|
||||||
#include "nrf_ble_lesc.h"
|
|
||||||
#include "app_error.h"
|
#include "app_error.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -20,22 +19,6 @@ static struct {
|
|||||||
bool bonds_delete_pending;
|
bool bonds_delete_pending;
|
||||||
} m_state = {0};
|
} m_state = {0};
|
||||||
|
|
||||||
static const char * sec_error_name(pm_sec_error_code_t error)
|
|
||||||
{
|
|
||||||
switch (error) {
|
|
||||||
case PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING:
|
|
||||||
return "PIN_OR_KEY_MISSING";
|
|
||||||
case PM_CONN_SEC_ERROR_MIC_FAILURE:
|
|
||||||
return "MIC_FAILURE";
|
|
||||||
case PM_CONN_SEC_ERROR_DISCONNECT:
|
|
||||||
return "DISCONNECT";
|
|
||||||
case PM_CONN_SEC_ERROR_SMP_TIMEOUT:
|
|
||||||
return "SMP_TIMEOUT";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize BLE security
|
* @brief Initialize BLE security
|
||||||
*/
|
*/
|
||||||
@@ -52,10 +35,6 @@ void ble_security_quick_init(bool development_mode)
|
|||||||
err_code = pm_init();
|
err_code = pm_init();
|
||||||
APP_ERROR_CHECK(err_code);
|
APP_ERROR_CHECK(err_code);
|
||||||
|
|
||||||
// Initialize LESC module (ECDH P-256 key pair generation)
|
|
||||||
err_code = nrf_ble_lesc_init();
|
|
||||||
APP_ERROR_CHECK(err_code);
|
|
||||||
|
|
||||||
// Configure security parameters
|
// Configure security parameters
|
||||||
memset(&sec_params, 0, sizeof(ble_gap_sec_params_t));
|
memset(&sec_params, 0, sizeof(ble_gap_sec_params_t));
|
||||||
|
|
||||||
@@ -82,7 +61,7 @@ void ble_security_quick_init(bool development_mode)
|
|||||||
// ===== PRODUCTION MODE: Full security =====
|
// ===== PRODUCTION MODE: Full security =====
|
||||||
sec_params.bond = 1; // Enable bonding
|
sec_params.bond = 1; // Enable bonding
|
||||||
sec_params.mitm = 1; // Enable MITM
|
sec_params.mitm = 1; // Enable MITM
|
||||||
sec_params.lesc = 1; // LE Secure Connections (ECDH P-256)
|
sec_params.lesc = 0; // Standard pairing
|
||||||
sec_params.keypress = 0;
|
sec_params.keypress = 0;
|
||||||
sec_params.io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; // Show passkey
|
sec_params.io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; // Show passkey
|
||||||
sec_params.oob = 0;
|
sec_params.oob = 0;
|
||||||
@@ -116,11 +95,10 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
|
|||||||
{
|
{
|
||||||
ret_code_t err_code;
|
ret_code_t err_code;
|
||||||
|
|
||||||
// DEV mode: do not forward security failure events to SDK handler (prevent disconnect)
|
// DEV 모드: 보안 실패 이벤트는 SDK 핸들러에 전달하지 않음 (disconnect 방지)
|
||||||
if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) {
|
if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) {
|
||||||
DBG_PRINTF("Security failed: error=%d (%s)\r\n",
|
DBG_PRINTF("Security failed: error=%d\r\n",
|
||||||
p_evt->params.conn_sec_failed.error,
|
p_evt->params.conn_sec_failed.error);
|
||||||
sec_error_name(p_evt->params.conn_sec_failed.error));
|
|
||||||
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
|
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -135,43 +113,22 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
|
|||||||
if (m_state.dev_mode) {
|
if (m_state.dev_mode) {
|
||||||
DBG_PRINTF("DEV: Connected (no security)\r\n");
|
DBG_PRINTF("DEV: Connected (no security)\r\n");
|
||||||
} else {
|
} else {
|
||||||
pm_conn_sec_status_t status;
|
DBG_PRINTF("PROD: Link secured (bonded)\r\n");
|
||||||
if (pm_conn_sec_status_get(p_evt->conn_handle, &status) == NRF_SUCCESS) {
|
|
||||||
DBG_PRINTF("PROD: Link secured - LESC=%d MITM=%d bonded=%d\r\n",
|
|
||||||
status.lesc, status.mitm_protected, status.bonded);
|
|
||||||
} else {
|
|
||||||
DBG_PRINTF("PROD: Link secured (bonded)\r\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PM_EVT_CONN_SEC_FAILED:
|
case PM_EVT_CONN_SEC_FAILED:
|
||||||
DBG_PRINTF("Security failed: error=%d (%s)\r\n",
|
DBG_PRINTF("Security failed: error=%d\r\n",
|
||||||
p_evt->params.conn_sec_failed.error,
|
p_evt->params.conn_sec_failed.error);
|
||||||
sec_error_name(p_evt->params.conn_sec_failed.error));
|
|
||||||
|
|
||||||
if (m_state.dev_mode) {
|
if (m_state.dev_mode) {
|
||||||
// DEV mode: ignore security failure, keep connection
|
// DEV 모드: 보안 실패 무시 — 연결 유지
|
||||||
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
|
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_DISCONNECT) {
|
|
||||||
// The peer/link already disconnected before security finished.
|
|
||||||
// There is no live connection to repair; BLE_GAP_EVT_DISCONNECTED
|
|
||||||
// will restart advertising.
|
|
||||||
DBG_PRINTF("Security ended by disconnect; waiting for reconnect\r\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_SMP_TIMEOUT) {
|
|
||||||
// The SDK cannot start another SMP procedure on this link.
|
|
||||||
pm_handler_disconnect_on_sec_failure(p_evt);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) {
|
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) {
|
||||||
// Key missing: attempt re-pairing, fall back to disconnect on failure
|
// Key missing: 재페어링 시도, 실패 시 disconnect로 폴백
|
||||||
err_code = pm_conn_secure(p_evt->conn_handle, true);
|
err_code = pm_conn_secure(p_evt->conn_handle, true);
|
||||||
if (err_code != NRF_ERROR_INVALID_STATE &&
|
if (err_code != NRF_ERROR_INVALID_STATE &&
|
||||||
err_code != NRF_ERROR_BUSY &&
|
err_code != NRF_ERROR_BUSY &&
|
||||||
@@ -179,11 +136,11 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
|
|||||||
APP_ERROR_CHECK(err_code);
|
APP_ERROR_CHECK(err_code);
|
||||||
}
|
}
|
||||||
if (err_code != NRF_SUCCESS) {
|
if (err_code != NRF_SUCCESS) {
|
||||||
// Re-pairing not possible -> disconnect
|
// 재페어링 불가 → disconnect
|
||||||
pm_handler_disconnect_on_sec_failure(p_evt);
|
pm_handler_disconnect_on_sec_failure(p_evt);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Other security failure -> delete bond then attempt re-pairing
|
// 기타 보안 실패 → bond 삭제 후 재페어링 시도
|
||||||
pm_peer_id_t peer_id;
|
pm_peer_id_t peer_id;
|
||||||
if (pm_peer_id_get(p_evt->conn_handle, &peer_id) == NRF_SUCCESS
|
if (pm_peer_id_get(p_evt->conn_handle, &peer_id) == NRF_SUCCESS
|
||||||
&& peer_id != PM_PEER_ID_INVALID) {
|
&& peer_id != PM_PEER_ID_INVALID) {
|
||||||
+199
-330
File diff suppressed because it is too large
Load Diff
+19
-5
@@ -263,6 +263,19 @@ uint32_t dr_adc_get_vref(void);
|
|||||||
* DEBUG FUNCTIONS
|
* DEBUG FUNCTIONS
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test ADC communication
|
||||||
|
* @return true if OK
|
||||||
|
*/
|
||||||
|
bool dr_adc_test(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print echo buffer to debug output
|
||||||
|
* @param buffer Sample buffer
|
||||||
|
* @param num_samples Number of samples
|
||||||
|
*/
|
||||||
|
void dr_adc_print_buffer(const uint16_t *buffer, uint16_t num_samples);
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* POWER CONTROL
|
* POWER CONTROL
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
@@ -437,8 +450,8 @@ typedef struct {
|
|||||||
uint16_t averaging; /**< Averaging count */
|
uint16_t averaging; /**< Averaging count */
|
||||||
uint8_t *ble_buffer; /**< Working buffer for BLE packets */
|
uint8_t *ble_buffer; /**< Working buffer for BLE packets */
|
||||||
dr_maa_channel_t channels[MAA_NUM_CHANNELS]; /**< Captured data for each channel */
|
dr_maa_channel_t channels[MAA_NUM_CHANNELS]; /**< Captured data for each channel */
|
||||||
bool pre_capture_all; /**< true: capture all channels before transmitting (mbb) */
|
bool pre_capture_all; /**< true: 전채널 캡처 완료 후 일괄 전송 (mbb용) */
|
||||||
void (*on_complete_cb)(void); /**< callback after async capture completes (NULL = none) */
|
void (*on_complete_cb)(void); /**< 비동기 캡처 완료 후 호출될 콜백 (NULL이면 미사용) */
|
||||||
} maa_async_ctx_t;
|
} maa_async_ctx_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -487,14 +500,15 @@ maa_async_state_t maa_async_get_state(void);
|
|||||||
void maa_async_abort(void);
|
void maa_async_abort(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set auto power-off flag (power off after completion)
|
* @brief 자동 전원 플래그 설정 (완료 후 자동 power off)
|
||||||
*/
|
*/
|
||||||
void maa_async_set_auto_power(bool on);
|
void maa_async_set_auto_power(bool on);
|
||||||
void maa_async_set_pre_capture_all(bool on);
|
void maa_async_set_pre_capture_all(bool on);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set async capture completion callback
|
* @brief 비동기 캡처 완료 콜백 설정
|
||||||
* Called after raa: is transmitted and power-off. NULL = no callback.
|
* raa: 전송 + 전원 OFF 이후 호출된다. NULL이면 콜백 없음.
|
||||||
*/
|
*/
|
||||||
void maa_async_set_on_complete(void (*cb)(void));
|
void maa_async_set_on_complete(void (*cb)(void));
|
||||||
|
|
||||||
+2
-10
@@ -1,11 +1,3 @@
|
|||||||
/*==============================================================================
|
|
||||||
* dr_util.c - BLE response formatting helpers
|
|
||||||
*
|
|
||||||
* Convenience wrappers used by command handlers to assemble and transmit
|
|
||||||
* short BLE responses. Each helper prepends a 4-char TAG and writes the
|
|
||||||
* supplied uint16 values in Big-Endian order.
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
#include "dr_util.h"
|
#include "dr_util.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
@@ -55,7 +47,7 @@ void dr_ble_return_3(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3)
|
|||||||
void dr_ble_debug(uint16_t point_id, uint16_t value)
|
void dr_ble_debug(uint16_t point_id, uint16_t value)
|
||||||
{
|
{
|
||||||
/* Use dedicated buffer to avoid conflicts with ble_bin_buffer */
|
/* Use dedicated buffer to avoid conflicts with ble_bin_buffer */
|
||||||
static uint8_t dbg_buffer[8] = {0};
|
static uint8_t dbg_buffer[8];
|
||||||
|
|
||||||
dbg_buffer[0] = 'd';
|
dbg_buffer[0] = 'd';
|
||||||
dbg_buffer[1] = 'b';
|
dbg_buffer[1] = 'b';
|
||||||
@@ -73,7 +65,7 @@ void dr_ble_debug(uint16_t point_id, uint16_t value)
|
|||||||
void dr_ble_return_piezo_1(const char *tag, uint16_t value)
|
void dr_ble_return_piezo_1(const char *tag, uint16_t value)
|
||||||
{
|
{
|
||||||
/* Use dedicated buffer for piezo responses to avoid conflicts with ble_bin_buffer */
|
/* Use dedicated buffer for piezo responses to avoid conflicts with ble_bin_buffer */
|
||||||
static uint8_t piezo_buffer[8] = {0};
|
static uint8_t piezo_buffer[8];
|
||||||
|
|
||||||
piezo_buffer[0] = tag[0];
|
piezo_buffer[0] = tag[0];
|
||||||
piezo_buffer[1] = tag[1];
|
piezo_buffer[1] = tag[1];
|
||||||
+3
-5
@@ -1,6 +1,3 @@
|
|||||||
/*==============================================================================
|
|
||||||
* dr_util.h - BLE response helper API
|
|
||||||
*============================================================================*/
|
|
||||||
#ifndef DR_UTIL_H
|
#ifndef DR_UTIL_H
|
||||||
#define DR_UTIL_H
|
#define DR_UTIL_H
|
||||||
|
|
||||||
@@ -11,10 +8,11 @@ void dr_ble_return_2(const char *tag, uint16_t v1, uint16_t v2);
|
|||||||
void dr_ble_return_3(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3);
|
void dr_ble_return_3(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3);
|
||||||
void dr_ble_return_3_be(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3);
|
void dr_ble_return_3_be(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3);
|
||||||
|
|
||||||
/* Piezo dedicated BLE return - uses a separate buffer to avoid conflicts. */
|
/* Piezo dedicated BLE return - uses separate buffer to avoid conflicts */
|
||||||
void dr_ble_return_piezo_1(const char *tag, uint16_t value);
|
void dr_ble_return_piezo_1(const char *tag, uint16_t value);
|
||||||
|
|
||||||
/* BLE debug output - sends "dbg:" + point_id + value. */
|
/* BLE debug output - sends "dbg:" + point_id + value */
|
||||||
void dr_ble_debug(uint16_t point_id, uint16_t value);
|
void dr_ble_debug(uint16_t point_id, uint16_t value);
|
||||||
|
|
||||||
#endif /* DR_UTIL_H */
|
#endif /* DR_UTIL_H */
|
||||||
|
|
||||||
+1370
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
|||||||
|
/* parser.h */
|
||||||
|
#ifndef PARSER_H
|
||||||
|
#define PARSER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* Platform-dependent function pointer set */
|
||||||
|
typedef struct {
|
||||||
|
void (*log)(const char *fmt, ...);
|
||||||
|
void (*tx_bin)(const uint8_t *buf, uint16_t len);
|
||||||
|
bool crc_check;
|
||||||
|
} dr_platform_if_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* Global interface & log flag */
|
||||||
|
extern dr_platform_if_t g_plat;
|
||||||
|
extern bool g_log_enable;
|
||||||
|
|
||||||
|
/* Main parser entry point */
|
||||||
|
int dr_cmd_parser(const uint8_t *buf, uint8_t len);
|
||||||
|
|
||||||
|
#endif /* PARSER_H */
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "../../.."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../../../../pc_firm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,297 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file battery_saadc.c
|
||||||
|
* @author CandyPops Co.
|
||||||
|
* @version V1.0.0
|
||||||
|
* @date 2022-09-05
|
||||||
|
* @brief
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* [모듈 개요] 배터리 전압 ADC 측정 모듈
|
||||||
|
*
|
||||||
|
* nRF52840의 SAADC(Successive Approximation ADC)를 사용하여 배터리 전압을 측정:
|
||||||
|
* - AIN2 채널, 10bit 해상도
|
||||||
|
* - 5초 주기 타이머(battery_loop)로 반복 측정
|
||||||
|
* - 저전압(3500mV 이하) 10회 연속 감지 시 자동 전원 OFF
|
||||||
|
* - info4 모드(전체 센서 수집)에서는 info_batt에 저장 후 온도 측정으로 전환
|
||||||
|
*
|
||||||
|
* 배터리 전압 변환 공식:
|
||||||
|
* 전압(mV) = ADC값 x (600mV / 1023) x 6 x 1.42 (분압 저항 보정 계수)
|
||||||
|
*
|
||||||
|
* info4 모드 순서: 배터리 -> 온도(go_temp) -> IMU(motion_raw_data_enabled)
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "sdk_common.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "nrf.h"
|
||||||
|
#include "boards.h"
|
||||||
|
#include "app_error.h"
|
||||||
|
#include "nrf_drv_saadc.h"
|
||||||
|
#include "nrf_drv_timer.h"
|
||||||
|
#include "ble_nus.h"
|
||||||
|
#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. */
|
||||||
|
|
||||||
|
/* 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.*/
|
||||||
|
|
||||||
|
/* 12비트 ADC 최대 디지털 값 (부동소수점) */
|
||||||
|
#define BATTERY_ADC_RES_12BITS 4095.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.
|
||||||
|
*/
|
||||||
|
#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하므로 더블 버퍼 불필요) */
|
||||||
|
static nrf_saadc_value_t adc_buf;
|
||||||
|
//static nrf_saadc_value_t adc_bufs[2]; // 이전: 더블 버퍼 (연속 측정용)
|
||||||
|
|
||||||
|
|
||||||
|
/* 배터리 모니터링 반복 타이머 정의 */
|
||||||
|
APP_TIMER_DEF(m_battery_loop_timer_id);
|
||||||
|
|
||||||
|
/* 배터리 측정 주기: 5초 (밀리초 단위) */
|
||||||
|
#define BATTERY_LOOP_INTERVAL 60000
|
||||||
|
|
||||||
|
/* 저전압 체크 플래그 — battery_loop에서 true로 설정, 핸들러에서 소비 */
|
||||||
|
bool low_battery_check = false;
|
||||||
|
|
||||||
|
/* SAADC 콜백 완료 플래그 — all_sensors()에서 배터리 측정 완료 대기용 */
|
||||||
|
volatile bool battery_saadc_done = false;
|
||||||
|
|
||||||
|
/* info4: 전체 센서 데이터 수집 모드 플래그 */
|
||||||
|
extern bool info4; // main.c
|
||||||
|
|
||||||
|
|
||||||
|
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] ;
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
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 완료 콜백
|
||||||
|
*
|
||||||
|
* SAADC 변환 완료 시 호출된다.
|
||||||
|
* ADC 값을 실제 배터리 전압(mV)으로 변환하고, 동작 모드에 따라:
|
||||||
|
* - 저전압 체크 모드: 3500mV 이하 10회 연속이면 자동 전원 OFF
|
||||||
|
* - info4 모드: info_batt에 저장 후 온도 측정(go_temp)으로 전환
|
||||||
|
* - 일반 모드: BLE 또는 UART로 즉시 전송
|
||||||
|
*
|
||||||
|
* 전압 변환: ADC값 x (600/1023) x 6 = 기본 전압, x 1.42 = 분압 보정 후 실제 전압
|
||||||
|
*/
|
||||||
|
void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
|
||||||
|
{
|
||||||
|
/* 저전압 연속 감지 카운터 (static으로 호출 간 유지) */
|
||||||
|
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; /* 분압 보정 후 최종 전압 (부동소수점) */
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* SAADC 해제 — 다른 ADC 측정(온도, 압력)과 하드웨어 공유 */
|
||||||
|
/* 1회 측정 후 해제이므로 buffer_convert(다음 버퍼 등록) 불필요 */
|
||||||
|
nrf_drv_saadc_channel_uninit(0);
|
||||||
|
nrf_drv_saadc_uninit();
|
||||||
|
|
||||||
|
/* 콜백 완료 알림 (all_sensors 대기 해제용) */
|
||||||
|
if (info4) DBG_PRINTF("2");
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* === 저전압 체크 모드 (battery_loop 타이머에서 설정) === */
|
||||||
|
if(low_battery_check == true)
|
||||||
|
{
|
||||||
|
low_battery_check = false;
|
||||||
|
|
||||||
|
/* 배터리 전압이 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === info4 모드: 전체 센서 수집(mbb) 중 배터리 값 저장 === */
|
||||||
|
else if (info4 == true)
|
||||||
|
{
|
||||||
|
info_batt = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SAADC를 배터리 전압 측정용으로 설정
|
||||||
|
*
|
||||||
|
* AIN2 채널을 싱글엔드(SE) 모드, 1/3 프리스케일링으로 초기화한다.
|
||||||
|
* 더블 버퍼(adc_bufs[0], [1])를 등록하여 연속 측정이 가능하도록 한다.
|
||||||
|
* 콜백: battery_event_handler
|
||||||
|
*/
|
||||||
|
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 사용 중 → 이번 측정 스킵, 다음 주기에 재시도 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* 싱글 버퍼 등록 (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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 배터리 전압 1회 측정 시작
|
||||||
|
*
|
||||||
|
* SAADC를 배터리용으로 설정 후 샘플링을 트리거한다.
|
||||||
|
* 결과는 battery_event_handler 콜백에서 비동기로 처리된다.
|
||||||
|
*/
|
||||||
|
void battery_level_meas(void)
|
||||||
|
{
|
||||||
|
ret_code_t err_code;
|
||||||
|
|
||||||
|
battery_configure(); /* SAADC 배터리용 초기화 */
|
||||||
|
err_code = nrf_drv_saadc_sample(); /* ADC 샘플링 트리거 (비동기) */
|
||||||
|
APP_ERROR_CHECK(err_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 배터리 모니터링 타이머 콜백 (5초 주기) - 주기 확인 필요(너무 짧음)
|
||||||
|
*
|
||||||
|
* 저전압 체크 플래그를 설정하고 배터리 측정을 시작한다.
|
||||||
|
* 다른 작업(IMU 등) 처리 중이면 측정을 건너뛴다.
|
||||||
|
*/
|
||||||
|
void battery_loop(void * p_context) /* For 1sec */
|
||||||
|
{
|
||||||
|
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회 측정 시작 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief 배터리 모니터링 타이머 시작 (5초 반복) */
|
||||||
|
void battery_timer_start(void)
|
||||||
|
{
|
||||||
|
APP_ERROR_CHECK(app_timer_start(m_battery_loop_timer_id, APP_TIMER_TICKS(BATTERY_LOOP_INTERVAL), NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief 배터리 모니터링 타이머 정지 */
|
||||||
|
void battery_timer_stop(void)
|
||||||
|
{
|
||||||
|
APP_ERROR_CHECK(app_timer_stop(m_battery_loop_timer_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief 배터리 모니터링 타이머 초기화 (반복 모드, 콜백: battery_loop) */
|
||||||
|
void battery_timer_init(void)
|
||||||
|
{
|
||||||
|
APP_ERROR_CHECK(app_timer_create(&m_battery_loop_timer_id, APP_TIMER_MODE_REPEATED, battery_loop));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file battery_saadc.h
|
||||||
|
* @author CandyPops Co.
|
||||||
|
* @version V1.0.0
|
||||||
|
* @date 2022-09-05
|
||||||
|
* @brief
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* [헤더 개요] 배터리 전압 SAADC 측정 인터페이스
|
||||||
|
*
|
||||||
|
* nRF52840 SAADC를 이용한 배터리 전압 측정 API를 선언한다.
|
||||||
|
*
|
||||||
|
* 주요 API:
|
||||||
|
* - battery_level_meas() : 배터리 전압 1회 측정 (AIN2)
|
||||||
|
* - battery_timer_init/start/stop() : 5초 주기 배터리 모니터링 타이머 제어
|
||||||
|
*
|
||||||
|
* LOW_BATTERY_VOLTAGE(3500mV) 이하가 10회 연속 감지되면 자동 전원 OFF
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef _BATTERY_SAADC_H_
|
||||||
|
#define _BATTERY_SAADC_H_
|
||||||
|
|
||||||
|
/* 저전압 판정 임계값 (mV) — 이 값 이하가 10회 연속이면 자동 전원 OFF */
|
||||||
|
#define LOW_BATTERY_VOLTAGE 3500 /* Low Battery 임계값 */
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief 배터리 SAADC 콜백 완료 플래그 (all_sensors 대기용) */
|
||||||
|
extern volatile bool battery_saadc_done;
|
||||||
|
|
||||||
|
/** @brief 배터리 전압 1회 측정 시작 (비동기, 결과는 콜백에서 처리) */
|
||||||
|
void battery_level_meas(void);
|
||||||
|
/** @brief 배터리 모니터링 5초 반복 타이머 시작 */
|
||||||
|
void battery_timer_start(void);
|
||||||
|
/** @brief 배터리 모니터링 타이머 정지 */
|
||||||
|
void battery_timer_stop(void);
|
||||||
|
/** @brief 배터리 모니터링 타이머 초기화 (앱 시작 시 1회 호출) */
|
||||||
|
void battery_timer_init(void);
|
||||||
|
|
||||||
|
#endif //_BATTERY_SAADC_H_
|
||||||
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* cmd_common.h - Shared header for command handlers
|
|
||||||
*
|
|
||||||
* Bundles every include / extern declaration / macro used in common across
|
|
||||||
* the cmd_*.c modules. A handler module only needs this header plus its own
|
|
||||||
* area-specific driver headers.
|
|
||||||
*============================================================================*/
|
|
||||||
#ifndef CMD_COMMON_H
|
|
||||||
#define CMD_COMMON_H
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#include "nrf_gpio.h"
|
|
||||||
#include "nrf_delay.h"
|
|
||||||
#include "debug_print.h"
|
|
||||||
#include "dr_util.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "app_timer.h"
|
|
||||||
#include "fstorage.h" /* config_data_t */
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* BLE transmission / formatting (defined in main.c)
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
extern uint8_t ble_bin_buffer[];
|
|
||||||
extern void single_format_data(uint8_t *buffer, const char *tag, uint16_t value);
|
|
||||||
extern void ascii_format_data(uint8_t *buffer, const char *tag, const char *ascii, size_t length);
|
|
||||||
extern void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length);
|
|
||||||
extern void dr_binary_tx_safe(const uint8_t *buffer, uint16_t length); /* length: word count */
|
|
||||||
extern void dr_sd_delay_ms(uint32_t ms);
|
|
||||||
extern volatile bool data_tx_in_progress;
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Device state / flags
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
extern bool device_status;
|
|
||||||
extern bool con_single;
|
|
||||||
extern bool lock_check;
|
|
||||||
extern uint8_t resetCount;
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Measurement functions (defined elsewhere)
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
extern void battery_level_meas(void);
|
|
||||||
extern void pressure_all_level_meas(void);
|
|
||||||
extern void tmp235_voltage_level_meas(void);
|
|
||||||
extern int imu_read_direct(void);
|
|
||||||
extern void battery_timer_stop(void);
|
|
||||||
extern void main_timer_start(void);
|
|
||||||
extern void hw_i2c_init_once(void);
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* info4 mode sensor cache (used to assemble the rbb: response in mbb?)
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
extern bool info4;
|
|
||||||
extern bool ble_got_new_data;
|
|
||||||
extern bool go_batt;
|
|
||||||
extern bool motion_data_once;
|
|
||||||
extern bool motion_raw_data_enabled;
|
|
||||||
extern volatile uint16_t info_batt;
|
|
||||||
extern volatile uint16_t info_temp;
|
|
||||||
extern volatile uint16_t info_imu[6];
|
|
||||||
extern volatile bool tmp235_saadc_done;
|
|
||||||
extern volatile bool battery_saadc_done;
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Device identifiers / passkey (FDS-backed)
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
extern char SERIAL_NO[12];
|
|
||||||
extern char HW_NO[12];
|
|
||||||
extern char m_static_passkey[6];
|
|
||||||
extern uint32_t m_life_cycle;
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Power / reset / bonding control
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
extern bool go_device_power_off;
|
|
||||||
extern bool go_NVIC_SystemReset;
|
|
||||||
extern bool bond_data_delete;
|
|
||||||
extern uint8_t m_reset_status;
|
|
||||||
extern void config_save(void);
|
|
||||||
extern config_data_t m_config;
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* AGC gain switch (P0.20)
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
#define GAIN_SW_PIN NRF_GPIO_PIN_MAP(0, 20)
|
|
||||||
#define AGC_GAIN_SW(x) do { if(x) nrf_gpio_pin_set(GAIN_SW_PIN); else nrf_gpio_pin_clear(GAIN_SW_PIN); } while(0)
|
|
||||||
|
|
||||||
#endif /* CMD_COMMON_H */
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* 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().
|
|
||||||
*
|
|
||||||
* Adding a new command:
|
|
||||||
* 1) Implement the handler in the appropriate cmd_*.c
|
|
||||||
* 2) Add the prototype to the matching cmd_*.h
|
|
||||||
* 3) Append { "tag?", true, Cmd_xxx } to m_cmd_table[] below
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
#include "cmd_table.h"
|
|
||||||
#include "cmd_device.h"
|
|
||||||
#include "cmd_info.h"
|
|
||||||
#include "cmd_sensor.h"
|
|
||||||
#include "cmd_piezo.h"
|
|
||||||
|
|
||||||
static const CmdEntry m_cmd_table[] = {
|
|
||||||
/* A. Device state control */
|
|
||||||
{ "msq?", true, Cmd_msq },
|
|
||||||
{ "mss?", true, Cmd_mss },
|
|
||||||
#if FEATURE_SECURE_CONNECTION
|
|
||||||
{ "msr?", true, Cmd_msr },
|
|
||||||
#endif
|
|
||||||
{ "cmd?", true, Cmd_cmd },
|
|
||||||
|
|
||||||
/* B. Device information */
|
|
||||||
{ "mfv?", true, Cmd_mfv },
|
|
||||||
{ "mid?", true, Cmd_mid },
|
|
||||||
{ "mwh?", true, Cmd_mwh },
|
|
||||||
{ "mws?", true, Cmd_mws },
|
|
||||||
{ "mrh?", true, Cmd_mrh },
|
|
||||||
{ "mrs?", true, Cmd_mrs },
|
|
||||||
{ "mpz?", true, Cmd_mpz },
|
|
||||||
{ "mqz?", true, Cmd_mqz },
|
|
||||||
|
|
||||||
/* C. Sensor measurement */
|
|
||||||
{ "msn?", true, Cmd_msn },
|
|
||||||
{ "mst?", true, Cmd_mst },
|
|
||||||
{ "msp?", true, Cmd_msp },
|
|
||||||
|
|
||||||
/* D. Piezo ultrasound */
|
|
||||||
{ "mpa?", true, Cmd_mpa },
|
|
||||||
{ "mpb?", true, Cmd_mpb },
|
|
||||||
{ "mpc?", true, Cmd_mpc },
|
|
||||||
{ "mec?", true, Cmd_mec },
|
|
||||||
{ "maa?", true, Cmd_maa },
|
|
||||||
{ "mbb?", true, Cmd_mbb },
|
|
||||||
{ "mcf?", true, Cmd_mcf },
|
|
||||||
{ "mcs?", true, Cmd_mcs },
|
|
||||||
|
|
||||||
/* E. LED control (handler in cmd_device.c) */
|
|
||||||
{ "mls?", true, Cmd_mls },
|
|
||||||
};
|
|
||||||
|
|
||||||
void cmd_table_init(void)
|
|
||||||
{
|
|
||||||
dr_parser_init(m_cmd_table, sizeof(m_cmd_table) / sizeof(m_cmd_table[0]));
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* cmd_table.h - Command table registration entry point
|
|
||||||
*
|
|
||||||
* main.c calls cmd_table_init() at boot to inject the command table into
|
|
||||||
* the parser.
|
|
||||||
*============================================================================*/
|
|
||||||
#ifndef CMD_TABLE_H
|
|
||||||
#define CMD_TABLE_H
|
|
||||||
|
|
||||||
void cmd_table_init(void);
|
|
||||||
|
|
||||||
#endif /* CMD_TABLE_H */
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* cmd_device.c - Device state control handlers
|
|
||||||
*
|
|
||||||
* A. msq? -> rsq: device power OFF
|
|
||||||
* B. mss? -> rss: device soft reset
|
|
||||||
* C. msr? -> rsr: BLE bond delete + reset (FEATURE_SECURE_CONNECTION)
|
|
||||||
* D. cmd? -> rmd: direct GPIO pin control (debug / test only)
|
|
||||||
* E. mls? -> rls: set LED state
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
#include "cmd_common.h"
|
|
||||||
#include "cmd_device.h"
|
|
||||||
#include "fstorage.h"
|
|
||||||
#include "led_control.h"
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* msq? -> rsq: Device power OFF
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_msq(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
uint16_t val = 0;
|
|
||||||
dr_get_u16(cmd, 0, &val);
|
|
||||||
if (g_plat.log)
|
|
||||||
{
|
|
||||||
g_plat.log("[Cmd_msq] Power off val=%u\r\n", val);
|
|
||||||
}
|
|
||||||
single_format_data(ble_bin_buffer, "rsq:", val);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 2);
|
|
||||||
go_device_power_off = true;
|
|
||||||
main_timer_start();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mss? -> rss: Device soft reset
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mss(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
uint16_t val = 0;
|
|
||||||
dr_get_u16(cmd, 0, &val);
|
|
||||||
single_format_data(ble_bin_buffer, "rss:", val);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 2);
|
|
||||||
|
|
||||||
m_reset_status = 2;
|
|
||||||
m_config.reset_status = m_reset_status;
|
|
||||||
config_save();
|
|
||||||
nrf_delay_ms(5);
|
|
||||||
go_NVIC_SystemReset = true;
|
|
||||||
main_timer_start();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* msr? -> rsr: Delete BLE bonding info + system reset
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "msr?"] [val 2B BE] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rsr:"] [val 2B BE] [CRC 2B]
|
|
||||||
*
|
|
||||||
* 1. Send BLE response
|
|
||||||
* 2. Persist bond-delete flag and reset status to FDS
|
|
||||||
* 3. Wait 5 ms for FDS write completion
|
|
||||||
* 4. Set NVIC system reset flag
|
|
||||||
*============================================================================*/
|
|
||||||
#if FEATURE_SECURE_CONNECTION
|
|
||||||
int Cmd_msr(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
uint16_t val = 0;
|
|
||||||
dr_get_u16(cmd, 0, &val);
|
|
||||||
single_format_data(ble_bin_buffer, "rsr:", val);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 2);
|
|
||||||
|
|
||||||
bond_data_delete = true;
|
|
||||||
m_config.bond_data_delete = (uint8_t)bond_data_delete;
|
|
||||||
m_reset_status = 2;
|
|
||||||
m_config.reset_status = m_reset_status;
|
|
||||||
config_save();
|
|
||||||
nrf_delay_ms(5);
|
|
||||||
go_NVIC_SystemReset = true;
|
|
||||||
main_timer_start();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* cmd? -> rmd: Direct GPIO pin control (debug / test)
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "cmd?"] [port 2B BE] [pin 2B BE] [state 2B BE] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rmd:"] [port 2B BE] [pin 2B BE] [state 2B BE] [CRC 2B]
|
|
||||||
* Error: rmd: + 0 (insufficient data)
|
|
||||||
*
|
|
||||||
* port : GPIO port (0 or 1)
|
|
||||||
* pin : pin number (0..31)
|
|
||||||
* state: 1=HIGH, 0=LOW
|
|
||||||
*
|
|
||||||
* Misuse can damage hardware - intended for debug only.
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_cmd(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
uint16_t v1, v2, v3;
|
|
||||||
uint32_t pin_number;
|
|
||||||
|
|
||||||
if (cmd->data_len < 6)
|
|
||||||
{
|
|
||||||
dr_ble_return_1("rmd:", 0);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pin_number = NRF_GPIO_PIN_MAP(v1, v2);
|
|
||||||
nrf_gpio_cfg_output(pin_number);
|
|
||||||
|
|
||||||
if (v3 == 1)
|
|
||||||
{
|
|
||||||
nrf_gpio_pin_set(pin_number);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nrf_gpio_pin_clear(pin_number);
|
|
||||||
}
|
|
||||||
|
|
||||||
dr_ble_return_3("rmd:", v1, v2, v3);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* 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
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
dr_ble_return_1("rls:", 0xFFFF);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t state;
|
|
||||||
dr_get_u16(cmd, 0, &state);
|
|
||||||
|
|
||||||
if (state > LED_STATE_ERROR)
|
|
||||||
{
|
|
||||||
dr_ble_return_1("rls:", 0xFFFE);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
led_set_state((led_state_t)state);
|
|
||||||
dr_ble_return_1("rls:", state);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* cmd_device.h - Device state control handlers (power / reset / GPIO / LED)
|
|
||||||
*============================================================================*/
|
|
||||||
#ifndef CMD_DEVICE_H
|
|
||||||
#define CMD_DEVICE_H
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
int Cmd_msq(const ParsedCmd *cmd); /* msq? -> rsq: device power OFF */
|
|
||||||
int Cmd_mss(const ParsedCmd *cmd); /* mss? -> rss: device soft reset */
|
|
||||||
#if FEATURE_SECURE_CONNECTION
|
|
||||||
int Cmd_msr(const ParsedCmd *cmd); /* msr? -> rsr: bond delete + reset */
|
|
||||||
#endif
|
|
||||||
int Cmd_cmd(const ParsedCmd *cmd); /* cmd? -> rmd: direct GPIO control */
|
|
||||||
int Cmd_mls(const ParsedCmd *cmd); /* mls? -> rls: set LED state */
|
|
||||||
|
|
||||||
#endif /* CMD_DEVICE_H */
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* cmd_info.c - Device information read/write handlers
|
|
||||||
*
|
|
||||||
* Commands that read or write FDS-persisted data: firmware version, hardware version, serial number, BLE passkey.
|
|
||||||
* Typically written once at the factory and read back later for identification.
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
#include "cmd_common.h"
|
|
||||||
#include "cmd_info.h"
|
|
||||||
#include "fstorage.h"
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mfv? -> rfv: Read firmware version
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mfv?"] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rfv:"] [FW_VER 12B ASCII] [CRC 2B]
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mfv(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
(void)cmd;
|
|
||||||
ascii_format_data(ble_bin_buffer, "rfv:", FIRMWARE_VERSION, 12);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 8); /* 16 bytes = 8 words */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mid? -> rid: Read HW number + serial number + FW version together
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mid?"] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rid:"] [HW 12B] [SN 12B] [FW 12B] [CRC 2B]
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mid(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
uint8_t *buf = ble_bin_buffer;
|
|
||||||
(void)cmd;
|
|
||||||
|
|
||||||
memcpy(HW_NO, m_config.hw_no, 12);
|
|
||||||
memcpy(SERIAL_NO, m_config.serial_no, 12);
|
|
||||||
|
|
||||||
buf[0] = 'r'; buf[1] = 'i'; buf[2] = 'd'; buf[3] = ':';
|
|
||||||
memcpy(&buf[4], HW_NO, 12);
|
|
||||||
memcpy(&buf[16], SERIAL_NO, 12);
|
|
||||||
memcpy(&buf[28], FIRMWARE_VERSION, 12);
|
|
||||||
|
|
||||||
dr_binary_tx_safe(buf, 20); /* 40 bytes = 20 words */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mwh? -> rwh: Write HW number to FDS
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mwh?"] [HW 12B ASCII] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rwh:"] [HW 12B ASCII] [CRC 2B]
|
|
||||||
* Error: rwh: + 0xFFFF (insufficient data)
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mwh(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
char buf[13];
|
|
||||||
|
|
||||||
if (cmd->data_len < 12)
|
|
||||||
{
|
|
||||||
dr_ble_return_1("rwh:", 0xFFFF);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dr_get_ascii(cmd, 0, buf, 12);
|
|
||||||
memcpy(HW_NO, buf, 12);
|
|
||||||
memcpy(m_config.hw_no, buf, 12);
|
|
||||||
config_save();
|
|
||||||
|
|
||||||
ascii_format_data(ble_bin_buffer, "rwh:", buf, 12);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 8);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mws? -> rws: Write serial number to FDS
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mws?"] [SN 12B ASCII] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rws:"] [SN 12B ASCII] [CRC 2B]
|
|
||||||
* Error: rws: + 0xFFFF (insufficient data)
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mws(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
char buf[13];
|
|
||||||
|
|
||||||
if (cmd->data_len < 12)
|
|
||||||
{
|
|
||||||
dr_ble_return_1("rws:", 0xFFFF);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dr_get_ascii(cmd, 0, buf, 12);
|
|
||||||
memcpy(SERIAL_NO, buf, 12);
|
|
||||||
memcpy(m_config.serial_no, buf, 12);
|
|
||||||
config_save();
|
|
||||||
|
|
||||||
ascii_format_data(ble_bin_buffer, "rws:", buf, 12);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 8);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mrh? -> rrh: Read HW number from FDS
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mrh?"] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rrh:"] [HW 12B ASCII] [CRC 2B]
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mrh(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
(void)cmd;
|
|
||||||
memcpy(HW_NO, m_config.hw_no, 12);
|
|
||||||
ascii_format_data(ble_bin_buffer, "rrh:", HW_NO, 12);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 8);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mrs? -> rrs: Read serial number from FDS
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mrs?"] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rrs:"] [SN 12B ASCII] [CRC 2B]
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mrs(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
(void)cmd;
|
|
||||||
memcpy(SERIAL_NO, m_config.serial_no, 12);
|
|
||||||
ascii_format_data(ble_bin_buffer, "rrs:", SERIAL_NO, 12);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 8);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mpz? -> rpz: Write BLE passkey to FDS
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mpz?"] [passkey 6B ASCII] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rpz:"] [passkey 6B ASCII] [CRC 2B]
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mpz(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
if (m_config.factory_provisioned != 0)
|
|
||||||
{
|
|
||||||
dr_ble_return_1("rpz:", 0xFFFF);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char passkey[7] = {0};
|
|
||||||
dr_get_ascii(cmd, 0, passkey, 6);
|
|
||||||
|
|
||||||
memcpy(m_static_passkey, passkey, 6);
|
|
||||||
memcpy(m_config.static_passkey, m_static_passkey, 6);
|
|
||||||
m_config.factory_provisioned = 1;
|
|
||||||
config_save();
|
|
||||||
|
|
||||||
ascii_format_data(ble_bin_buffer, "rpz:", passkey, 6);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 5);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mqz? -> rqz: Read BLE passkey from FDS
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mqz?"] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rqz:"] [passkey 6B ASCII] [CRC 2B]
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mqz(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
(void)cmd;
|
|
||||||
memcpy(m_static_passkey, m_config.static_passkey, 6);
|
|
||||||
ascii_format_data(ble_bin_buffer, "rqz:", m_static_passkey, 6);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 5);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* cmd_info.h - Device information read/write handlers
|
|
||||||
*============================================================================*/
|
|
||||||
#ifndef CMD_INFO_H
|
|
||||||
#define CMD_INFO_H
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
int Cmd_mfv(const ParsedCmd *cmd); /* mfv? -> rfv: read firmware version */
|
|
||||||
int Cmd_mid(const ParsedCmd *cmd); /* mid? -> rid: read HW + SN + FW together */
|
|
||||||
int Cmd_mwh(const ParsedCmd *cmd); /* mwh? -> rwh: write HW number */
|
|
||||||
int Cmd_mws(const ParsedCmd *cmd); /* mws? -> rws: write serial number */
|
|
||||||
int Cmd_mrh(const ParsedCmd *cmd); /* mrh? -> rrh: read HW number */
|
|
||||||
int Cmd_mrs(const ParsedCmd *cmd); /* mrs? -> rrs: read serial number */
|
|
||||||
int Cmd_mpz(const ParsedCmd *cmd); /* mpz? -> rpz: write BLE passkey */
|
|
||||||
int Cmd_mqz(const ParsedCmd *cmd); /* mqz? -> rqz: read BLE passkey */
|
|
||||||
|
|
||||||
#endif /* CMD_INFO_H */
|
|
||||||
@@ -1,343 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* cmd_piezo.c - Piezo ultrasound measurement handlers
|
|
||||||
*
|
|
||||||
* mpa? -> rpa: TX/RX power ON
|
|
||||||
* mpb? -> rpb: TX/RX power OFF
|
|
||||||
* mpc? -> rpc: burst generation (test)
|
|
||||||
* mec? -> reb:+raa: single-channel burst + echo capture
|
|
||||||
* maa? -> reb:+raa: 6-channel asynchronous capture
|
|
||||||
* mbb? -> rbb:+reb:+raa: bulk sensor measurement + 6-channel capture
|
|
||||||
* mcf? -> rcf: read piezo parameters
|
|
||||||
* mcs? -> rcs: write piezo parameters
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
#include "cmd_common.h"
|
|
||||||
#include "cmd_piezo.h"
|
|
||||||
#include "cmd_sensor.h" /* all_sensors() */
|
|
||||||
#include "dr_piezo.h"
|
|
||||||
#include "dr_adc121s051.h"
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mpa? -> rpa: Enable piezo TX/RX circuit
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mpa?"] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rpa:"] [1 2B] [CRC 2B]
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mpa(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
(void)cmd;
|
|
||||||
dr_piezo_power_on();
|
|
||||||
|
|
||||||
if (g_plat.tx_bin)
|
|
||||||
{
|
|
||||||
single_format_data(ble_bin_buffer, "rpa:", 1);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 3);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mpb? -> rpb: Disable piezo TX/RX circuit
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mpb?"] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rpb:"] [1 2B] [CRC 2B]
|
|
||||||
*
|
|
||||||
* Power saving once measurement is complete.
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mpb(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
(void)cmd;
|
|
||||||
dr_piezo_power_off();
|
|
||||||
|
|
||||||
if (g_plat.tx_bin)
|
|
||||||
{
|
|
||||||
single_format_data(ble_bin_buffer, "rpb:", 1);
|
|
||||||
dr_binary_tx_safe(ble_bin_buffer, 3);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mpc? -> rpc: Piezo burst generation (test / debug)
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mpc?"] [cycles 2B] [freq_option 2B] [piezo_ch 2B] [CRC 2B]
|
|
||||||
* cycles : 3..7 (default 5)
|
|
||||||
* freq_option : 0=1.8MHz, 1=2.1MHz(default), 2=2.0MHz, 3=1.7MHz, 4=2.2MHz
|
|
||||||
* piezo_ch : 0..7
|
|
||||||
* Response: [TAG 4B "rpc:"] [cycles 2B] [CRC 2B]
|
|
||||||
* Error: rpc: + 2 (cycles out of range)
|
|
||||||
*
|
|
||||||
* Generates the burst only - no echo capture.
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mpc(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
uint16_t cycles = 5;
|
|
||||||
uint16_t freq_option = 1;
|
|
||||||
uint16_t piezo_ch = 0;
|
|
||||||
|
|
||||||
(void)dr_get_u16(cmd, 0, &cycles);
|
|
||||||
(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 (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;
|
|
||||||
default:
|
|
||||||
dr_piezo_burst_sw((uint8_t)cycles);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
dr_ble_return_1("rpc:", (uint8_t)cycles);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mec? -> reb:+raa: Piezo burst + echo capture (16-bit raw)
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mec?"] [freq 2B] [delay_us 2B] [num_samples 2B]
|
|
||||||
* [cycles 2B] [averaging 2B] [piezo_ch 2B] [CRC 2B]
|
|
||||||
* Response: reb: [num_samples 2B] [raw_data up to 238B] (red: follow-up if needed)
|
|
||||||
* raa: [status 2B]
|
|
||||||
* Error: rer: + (0xEE00|err) + num_samples
|
|
||||||
*
|
|
||||||
* Sends raw 16-bit ADC values directly. Suited for short-range (~25cm).
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mec(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
uint16_t freq_option = 0;
|
|
||||||
uint16_t delay_us = 20;
|
|
||||||
uint16_t num_samples = 140;
|
|
||||||
uint16_t cycles = 5;
|
|
||||||
uint16_t averaging = 1;
|
|
||||||
uint16_t piezo_ch = 0;
|
|
||||||
|
|
||||||
if (!dr_piezo_is_power_on())
|
|
||||||
{
|
|
||||||
dr_piezo_power_on();
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)dr_get_u16(cmd, 0, &freq_option);
|
|
||||||
(void)dr_get_u16(cmd, 1, &delay_us);
|
|
||||||
(void)dr_get_u16(cmd, 2, &num_samples);
|
|
||||||
(void)dr_get_u16(cmd, 3, &cycles);
|
|
||||||
(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
dr_ble_return_2("rer:", 0xEE00 | (uint16_t)err, num_samples);
|
|
||||||
}
|
|
||||||
|
|
||||||
dr_piezo_power_off();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* maa? -> reb:+raa: 6-channel asynchronous full capture
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "maa?"] [CRC 2B]
|
|
||||||
* Response: per channel reb: [num_samples 2B] [raw_data...]
|
|
||||||
* final raa: [status 2B]
|
|
||||||
* Error: raa: + 0xFFFE (previous capture in progress)
|
|
||||||
* raa: + (0xFF00|err) (start failed)
|
|
||||||
*
|
|
||||||
* Asynchronous state machine - the BLE_NUS_EVT_TX_RDY callback drives the
|
|
||||||
* follow-up packet transmissions. Parameters are loaded from m_config (FDS).
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_maa(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
dr_adc_err_t err;
|
|
||||||
(void)cmd;
|
|
||||||
|
|
||||||
if (maa_async_is_busy())
|
|
||||||
{
|
|
||||||
dr_ble_return_1("raa:", 0xFFFE);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dr_piezo_is_power_on())
|
|
||||||
{
|
|
||||||
dr_piezo_power_on();
|
|
||||||
}
|
|
||||||
|
|
||||||
err = maa_async_start(
|
|
||||||
m_config.piezo_freq_option,
|
|
||||||
m_config.piezo_delay_us,
|
|
||||||
m_config.piezo_num_samples,
|
|
||||||
m_config.piezo_cycles,
|
|
||||||
m_config.piezo_averaging,
|
|
||||||
ble_bin_buffer
|
|
||||||
);
|
|
||||||
|
|
||||||
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();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mbb? -> rbb:+reb:+raa: Bulk sensor measurement + 6-channel full capture
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mbb?"] [CRC 2B]
|
|
||||||
* Response: rbb: [batt 2B] [IMU 12B] [temp 2B]
|
|
||||||
* reb: [num_samples 2B] [raw_data...] (per channel)
|
|
||||||
* raa: [status 2B]
|
|
||||||
* Error: raa: + 0xFFFE (previous capture in progress)
|
|
||||||
* raa: + (0xFF00|err) (start failed)
|
|
||||||
*
|
|
||||||
* 1) all_sensors() measures battery / IMU / temperature and emits rbb:
|
|
||||||
* 2) Starts 6-channel capture in pre-capture-all mode
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mbb(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
dr_adc_err_t err;
|
|
||||||
(void)cmd;
|
|
||||||
|
|
||||||
all_sensors();
|
|
||||||
|
|
||||||
if (maa_async_is_busy())
|
|
||||||
{
|
|
||||||
dr_ble_return_1("raa:", 0xFFFE);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
maa_async_set_pre_capture_all(true);
|
|
||||||
|
|
||||||
err = maa_async_start(
|
|
||||||
m_config.piezo_freq_option,
|
|
||||||
m_config.piezo_delay_us,
|
|
||||||
m_config.piezo_num_samples,
|
|
||||||
m_config.piezo_cycles,
|
|
||||||
m_config.piezo_averaging,
|
|
||||||
ble_bin_buffer
|
|
||||||
);
|
|
||||||
|
|
||||||
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();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* 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]
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mcf(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
(void)cmd;
|
|
||||||
|
|
||||||
uint8_t *buf = ble_bin_buffer;
|
|
||||||
buf[0] = 'r'; buf[1] = 'c'; buf[2] = 'f'; buf[3] = ':';
|
|
||||||
buf[4] = 0; buf[5] = m_config.piezo_freq_option;
|
|
||||||
buf[6] = 0; buf[7] = m_config.piezo_cycles;
|
|
||||||
buf[8] = (uint8_t)(m_config.piezo_averaging >> 8); buf[9] = (uint8_t)(m_config.piezo_averaging & 0xFF);
|
|
||||||
buf[10] = (uint8_t)(m_config.piezo_delay_us >> 8); buf[11] = (uint8_t)(m_config.piezo_delay_us & 0xFF);
|
|
||||||
buf[12] = (uint8_t)(m_config.piezo_num_samples >> 8); buf[13] = (uint8_t)(m_config.piezo_num_samples & 0xFF);
|
|
||||||
dr_binary_tx_safe(buf, 7); /* 14 bytes = 7 words */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* 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]
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
dr_ble_return_1("rcs:", 0xFFFF);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t freq, cycles, averaging, delay_us, num_samples;
|
|
||||||
dr_get_u16(cmd, 0, &freq);
|
|
||||||
dr_get_u16(cmd, 1, &cycles);
|
|
||||||
dr_get_u16(cmd, 2, &averaging);
|
|
||||||
dr_get_u16(cmd, 3, &delay_us);
|
|
||||||
dr_get_u16(cmd, 4, &num_samples);
|
|
||||||
|
|
||||||
m_config.piezo_freq_option = (uint8_t)freq;
|
|
||||||
m_config.piezo_cycles = (uint8_t)cycles;
|
|
||||||
m_config.piezo_averaging = averaging;
|
|
||||||
m_config.piezo_delay_us = delay_us;
|
|
||||||
m_config.piezo_num_samples = num_samples;
|
|
||||||
config_save();
|
|
||||||
|
|
||||||
uint8_t *buf = ble_bin_buffer;
|
|
||||||
buf[0] = 'r'; buf[1] = 'c'; buf[2] = 's'; buf[3] = ':';
|
|
||||||
buf[4] = 0; buf[5] = m_config.piezo_freq_option;
|
|
||||||
buf[6] = 0; buf[7] = m_config.piezo_cycles;
|
|
||||||
buf[8] = (uint8_t)(m_config.piezo_averaging >> 8); buf[9] = (uint8_t)(m_config.piezo_averaging & 0xFF);
|
|
||||||
buf[10] = (uint8_t)(m_config.piezo_delay_us >> 8); buf[11] = (uint8_t)(m_config.piezo_delay_us & 0xFF);
|
|
||||||
buf[12] = (uint8_t)(m_config.piezo_num_samples >> 8); buf[13] = (uint8_t)(m_config.piezo_num_samples & 0xFF);
|
|
||||||
dr_binary_tx_safe(buf, 7);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* cmd_piezo.h - Piezo ultrasound measurement handlers
|
|
||||||
*============================================================================*/
|
|
||||||
#ifndef CMD_PIEZO_H
|
|
||||||
#define CMD_PIEZO_H
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
int Cmd_mpa(const ParsedCmd *cmd); /* mpa? -> rpa: TX/RX power ON */
|
|
||||||
int Cmd_mpb(const ParsedCmd *cmd); /* mpb? -> rpb: TX/RX power OFF */
|
|
||||||
int Cmd_mpc(const ParsedCmd *cmd); /* mpc? -> rpc: burst generation */
|
|
||||||
int Cmd_mec(const ParsedCmd *cmd); /* mec? -> reb:+raa: single-channel capture */
|
|
||||||
int Cmd_maa(const ParsedCmd *cmd); /* maa? -> reb:+raa: 6-channel async capture */
|
|
||||||
int Cmd_mbb(const ParsedCmd *cmd); /* mbb? -> rbb:+reb:+raa: sensors + capture */
|
|
||||||
int Cmd_mcf(const ParsedCmd *cmd); /* mcf? -> rcf: read piezo parameters */
|
|
||||||
int Cmd_mcs(const ParsedCmd *cmd); /* mcs? -> rcs: write piezo parameters */
|
|
||||||
|
|
||||||
#endif /* CMD_PIEZO_H */
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* cmd_sensor.c - Sensor measurement handlers
|
|
||||||
*
|
|
||||||
* msn? -> rsn: battery ADC measurement
|
|
||||||
* mso? -> rso: TMP235 temperature reading
|
|
||||||
* msp? -> rsp: IMU 6-axis single read
|
|
||||||
* all_sensors() bulk-measurement helper used by the mbb? handler
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
#include "cmd_common.h"
|
|
||||||
#include "cmd_sensor.h"
|
|
||||||
#include "dr_piezo.h"
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* msn? -> rsn: Battery level ADC measurement
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "msn?"] [CRC 2B]
|
|
||||||
* Response: rsn: + battery voltage in mV (sent from battery_saadc.c callback)
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_msn(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
(void)cmd;
|
|
||||||
battery_level_meas();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* mst? -> rso: Temperature with piezo power cycle
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "mst?"] [CRC 2B]
|
|
||||||
* Response: [TAG 4B "rso:"] [temp_x100 2B BE] [CRC 2B]
|
|
||||||
*
|
|
||||||
* TMP235 shares the piezo TX/RX power rail. This command handles the full
|
|
||||||
* sequence: power ON -> measure -> power OFF, so the caller doesn't need
|
|
||||||
* to send mpa?/mpb? separately.
|
|
||||||
* Response is sent from the TMP235 SAADC callback (tmp235_voltage_handler).
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_mst(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
uint32_t timeout_cnt;
|
|
||||||
(void)cmd;
|
|
||||||
|
|
||||||
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++)
|
|
||||||
{
|
|
||||||
dr_sd_delay_ms(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
dr_piezo_power_off();
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* msp? -> rsp: IMU 6-axis single read
|
|
||||||
*
|
|
||||||
* Request: [TAG 4B "msp?"] [CRC 2B]
|
|
||||||
* Response: rsp: + accel(xyz) + gyro(xyz) (transmitted inside imu_read_direct)
|
|
||||||
*
|
|
||||||
* Reads IMU registers directly over I2C and transmits over BLE. Synchronous.
|
|
||||||
*============================================================================*/
|
|
||||||
int Cmd_msp(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
(void)cmd;
|
|
||||||
hw_i2c_init_once();
|
|
||||||
imu_read_direct();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* all_sensors() - Bulk-measurement helper for the mbb? handler
|
|
||||||
*
|
|
||||||
* Stores sensor values into globals via info4 mode, then transmits them as a
|
|
||||||
* single rbb: packet. SAADC measurements run asynchronously (callback), so
|
|
||||||
* dr_sd_delay_ms() is used to wait for completion.
|
|
||||||
*
|
|
||||||
* Order: battery -> IMU -> (Piezo TX/RX ON) -> temperature
|
|
||||||
* Response: rbb: [batt 2B] [IMU 6x2B] [temp 2B] = 20 bytes = 10 words
|
|
||||||
*============================================================================*/
|
|
||||||
void all_sensors(void)
|
|
||||||
{
|
|
||||||
uint8_t *buf;
|
|
||||||
uint32_t timeout_cnt;
|
|
||||||
|
|
||||||
info4 = true;
|
|
||||||
|
|
||||||
/* 1. Battery voltage -> info_batt */
|
|
||||||
battery_saadc_done = false;
|
|
||||||
battery_level_meas();
|
|
||||||
for (timeout_cnt = 0; !battery_saadc_done && timeout_cnt < 100; timeout_cnt++)
|
|
||||||
{
|
|
||||||
dr_sd_delay_ms(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 2. IMU 6-axis single read -> info_imu[6] */
|
|
||||||
hw_i2c_init_once();
|
|
||||||
imu_read_direct();
|
|
||||||
|
|
||||||
/* 3. Temperature -> info_temp (TMP235 needs Piezo TX/RX power) */
|
|
||||||
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++)
|
|
||||||
{
|
|
||||||
dr_sd_delay_ms(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
info4 = false;
|
|
||||||
|
|
||||||
/* Assemble and transmit the rbb: packet (dedicated buffer to avoid
|
|
||||||
collision with ble_bin_buffer used by the async ADC state machine) */
|
|
||||||
static uint8_t rbb_buf[20];
|
|
||||||
buf = rbb_buf;
|
|
||||||
buf[0] = 'r'; buf[1] = 'b'; buf[2] = 'b'; buf[3] = ':';
|
|
||||||
buf[4] = (uint8_t)(info_batt >> 8);
|
|
||||||
buf[5] = (uint8_t)(info_batt & 0xFF);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[18] = (uint8_t)(info_temp >> 8);
|
|
||||||
buf[19] = (uint8_t)(info_temp & 0xFF);
|
|
||||||
|
|
||||||
dr_binary_tx_safe(buf, 10); /* 20 bytes = 10 words */
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* cmd_sensor.h - Sensor measurement handlers (battery / temperature / IMU)
|
|
||||||
*============================================================================*/
|
|
||||||
#ifndef CMD_SENSOR_H
|
|
||||||
#define CMD_SENSOR_H
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
int Cmd_msn(const ParsedCmd *cmd); /* msn? -> rsn: battery ADC measurement */
|
|
||||||
int Cmd_mst(const ParsedCmd *cmd); /* mst? -> rso: TMP235 with piezo power cycle */
|
|
||||||
int Cmd_msp(const ParsedCmd *cmd); /* msp? -> rsp: IMU 6-axis single read */
|
|
||||||
/* 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 */
|
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* parser.c - BLE command parser / dispatcher
|
|
||||||
*
|
|
||||||
* Common module that parses binary command packets received over BLE and
|
|
||||||
* dispatches them to the appropriate handler from the command table.
|
|
||||||
*
|
|
||||||
* Key point: this file does not know which Cmd_* functions exist.
|
|
||||||
* cmd_table.c injects the table pointer at boot via dr_parser_init().
|
|
||||||
*
|
|
||||||
* Packet layout: [TAG 4B] [data N] [CRC16 2B (optional)]
|
|
||||||
* - TAG : 4-char ASCII identifier (e.g. "mid?")
|
|
||||||
* - data : Big-Endian uint16 or ASCII
|
|
||||||
* - CRC16: present when g_plat.crc_check is true; trailing 2 bytes (LE)
|
|
||||||
*
|
|
||||||
* Flow:
|
|
||||||
* 1) dr_cmd_parser() - external entry point
|
|
||||||
* 2) dr_parse_cmd() - length / CRC verification + TAG/data split
|
|
||||||
* 3) dr_cmd_dispatch() - walks the command table -> calls handler
|
|
||||||
*
|
|
||||||
* Error responses (all formatted as [err_tag 4B] [echo cmd_tag 4B] = 8 bytes):
|
|
||||||
* - rxs: Too short (< 4B, or < 7B with CRC enabled)
|
|
||||||
* - rxc: CRC fail
|
|
||||||
* - rxd: Disabled command
|
|
||||||
* - rxn: NULL handler
|
|
||||||
* - rxx: Unknown command
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Globals (declared extern in header)
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
dr_platform_if_t g_plat = { 0, 0, 0 };
|
|
||||||
bool g_log_enable = false;
|
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Internal state - command table pointer injected via dr_parser_init()
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
static const CmdEntry *m_cmd_table = NULL;
|
|
||||||
static uint16_t m_cmd_count = 0;
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* Internal utilities (static)
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
/* Copy first 4 bytes of buffer into a null-terminated TAG string. */
|
|
||||||
static void dr_copy_tag(const uint8_t *buf, char *tag_out)
|
|
||||||
{
|
|
||||||
tag_out[0] = (char)buf[0];
|
|
||||||
tag_out[1] = (char)buf[1];
|
|
||||||
tag_out[2] = (char)buf[2];
|
|
||||||
tag_out[3] = (char)buf[3];
|
|
||||||
tag_out[4] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compare two 4-char TAGs (byte-by-byte, faster than memcmp here). */
|
|
||||||
static bool dr_tag_eq(const char *tag, const char *key4)
|
|
||||||
{
|
|
||||||
return (tag[0] == key4[0] &&
|
|
||||||
tag[1] == key4[1] &&
|
|
||||||
tag[2] == key4[2] &&
|
|
||||||
tag[3] == key4[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* Public utilities - used by handlers
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*out = (uint16_t)((uint16_t)cmd->data[pos] << 8) | (uint16_t)cmd->data[pos + 1];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dr_get_ascii(const ParsedCmd *cmd, uint8_t offset, char *out, uint8_t max_len)
|
|
||||||
{
|
|
||||||
uint8_t i;
|
|
||||||
uint8_t remain;
|
|
||||||
|
|
||||||
if (offset >= cmd->data_len)
|
|
||||||
{
|
|
||||||
out[0] = '\0';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
remain = (uint8_t)(cmd->data_len - offset);
|
|
||||||
if (remain > max_len)
|
|
||||||
{
|
|
||||||
remain = max_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < remain; i++)
|
|
||||||
{
|
|
||||||
out[i] = (char)cmd->data[offset + i];
|
|
||||||
}
|
|
||||||
out[remain] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* CRC16 (Nordic SDK CRC-CCITT variant)
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
uint16_t dr_crc16_compute(const uint8_t *p_data, uint32_t size, const uint16_t *p_crc)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc;
|
|
||||||
|
|
||||||
for (i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
crc = (uint8_t)(crc >> 8) | (crc << 8);
|
|
||||||
crc ^= p_data[i];
|
|
||||||
crc ^= (uint8_t)(crc & 0xFF) >> 4;
|
|
||||||
crc ^= (crc << 8) << 4;
|
|
||||||
crc ^= ((crc & 0xFF) << 4) << 1;
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool dr_crc16_check(const uint8_t *p_data, uint32_t data_len, uint16_t expected_crc)
|
|
||||||
{
|
|
||||||
return (dr_crc16_compute(p_data, data_len, NULL) == expected_crc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Extract trailing 2 bytes (Little-Endian) as expected CRC and verify. */
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
data_len = packet_len - 2;
|
|
||||||
expected_crc = (uint16_t)packet[packet_len - 2]
|
|
||||||
| ((uint16_t)packet[packet_len - 1] << 8);
|
|
||||||
|
|
||||||
return dr_crc16_check(packet, data_len, expected_crc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* Error response helper
|
|
||||||
*
|
|
||||||
* Packet: [err_tag 4B] [echo cmd_tag 4B] = 8 bytes = 4 words
|
|
||||||
* CRC16 is appended automatically by the tx_bin layer.
|
|
||||||
*============================================================================*/
|
|
||||||
static void dr_send_error(const char *err_tag, const char *cmd_tag)
|
|
||||||
{
|
|
||||||
if (g_plat.tx_bin)
|
|
||||||
{
|
|
||||||
uint8_t err_buf[8];
|
|
||||||
memcpy(&err_buf[0], err_tag, 4);
|
|
||||||
memcpy(&err_buf[4], cmd_tag, 4);
|
|
||||||
g_plat.tx_bin(err_buf, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* Packet parser - raw buffer -> ParsedCmd
|
|
||||||
*
|
|
||||||
* Error responses (rxs: / rxc:) are emitted directly from this function.
|
|
||||||
*============================================================================*/
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
dr_send_error("rxs:", "????");
|
|
||||||
if (g_plat.log && g_log_enable)
|
|
||||||
{
|
|
||||||
g_plat.log("[parser] too short (%u bytes) -> rxs:\r\n", length);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Extract TAG first (used in error echoes below) */
|
|
||||||
dr_copy_tag(buffer, out->tag);
|
|
||||||
|
|
||||||
/* CRC verification */
|
|
||||||
if (g_plat.crc_check)
|
|
||||||
{
|
|
||||||
if (length < 7)
|
|
||||||
{
|
|
||||||
dr_send_error("rxs:", out->tag);
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
dr_send_error("rxc:", out->tag);
|
|
||||||
if (g_plat.log && g_log_enable)
|
|
||||||
{
|
|
||||||
g_plat.log("[parser] CRC mismatch '%s' -> rxc:\r\n", out->tag);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
data_len = (uint8_t)(length - 4 - 2); /* strip TAG and CRC */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data_len = (uint8_t)(length - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data_len > DR_MAX_DATA)
|
|
||||||
{
|
|
||||||
data_len = DR_MAX_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data_len > 0)
|
|
||||||
{
|
|
||||||
memcpy(out->data, buffer + 4, data_len);
|
|
||||||
}
|
|
||||||
out->data_len = data_len;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* Command dispatcher
|
|
||||||
*
|
|
||||||
* Lower-cases the parsed TAG and walks the command table to find a matching
|
|
||||||
* handler. Emits the matching error response on miss / disabled / NULL handler.
|
|
||||||
*============================================================================*/
|
|
||||||
static int dr_cmd_dispatch(const ParsedCmd *cmd)
|
|
||||||
{
|
|
||||||
uint16_t i;
|
|
||||||
char tag_lower[5];
|
|
||||||
|
|
||||||
if (m_cmd_table == NULL)
|
|
||||||
{
|
|
||||||
dr_send_error("rxn:", cmd->tag);
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
tag_lower[i] = '\0';
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
dr_send_error("rxd:", cmd->tag);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
dr_send_error("rxn:", cmd->tag);
|
|
||||||
if (g_plat.log)
|
|
||||||
{
|
|
||||||
g_plat.log("[parser] NULL handler for '%s' -> rxn:\n", cmd->tag);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_cmd_table[i].handler(cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TAG not found in table */
|
|
||||||
dr_send_error("rxx:", cmd->tag);
|
|
||||||
if (g_plat.log && g_log_enable)
|
|
||||||
{
|
|
||||||
g_plat.log("Unknown TAG '%s' -> rxx:\n", cmd->tag);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* Public API
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
/* Called by cmd_table.c at boot to inject the command table. */
|
|
||||||
void dr_parser_init(const CmdEntry *table, uint16_t count)
|
|
||||||
{
|
|
||||||
m_cmd_table = table;
|
|
||||||
m_cmd_count = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Entry point - called from BLE NUS / UART receive callbacks.
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* 1 = command processed successfully
|
|
||||||
* 0 = unknown TAG / disabled command (error response already sent)
|
|
||||||
* -1 = parse failure (rxs: / rxc: error response already sent)
|
|
||||||
*/
|
|
||||||
int dr_cmd_parser(const uint8_t *buf, uint8_t len)
|
|
||||||
{
|
|
||||||
ParsedCmd cmd;
|
|
||||||
|
|
||||||
if (!dr_parse_cmd(buf, len, &cmd))
|
|
||||||
{
|
|
||||||
if (g_plat.log) g_plat.log("[PARSER] PARSE FAIL\r\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dr_cmd_dispatch(&cmd);
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* parser.h - BLE command parser public API
|
|
||||||
*
|
|
||||||
* Parser infrastructure (parsing/CRC/dispatch) and command handlers are
|
|
||||||
* decoupled:
|
|
||||||
* - Parser: parser.c (defines the functions here)
|
|
||||||
* - Handlers: handlers/cmd_device.c / cmd_info.c / ...
|
|
||||||
* - Table: cmd_table.c (injected via cmd_table_init)
|
|
||||||
*
|
|
||||||
* Boot sequence: main.c calls cmd_table_init() -> dr_parser_init() to inject
|
|
||||||
* the command table pointer into the parser.
|
|
||||||
*============================================================================*/
|
|
||||||
#ifndef PARSER_H
|
|
||||||
#define PARSER_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
/* Maximum payload length (excludes TAG) */
|
|
||||||
#define DR_MAX_DATA 128
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Platform interface
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
typedef struct {
|
|
||||||
void (*log)(const char *fmt, ...);
|
|
||||||
void (*tx_bin)(const uint8_t *buf, uint16_t len); /* len: word (uint16) count */
|
|
||||||
bool crc_check;
|
|
||||||
} dr_platform_if_t;
|
|
||||||
|
|
||||||
extern dr_platform_if_t g_plat;
|
|
||||||
extern bool g_log_enable;
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Parsed command - input passed to handlers
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
typedef struct {
|
|
||||||
char tag[5]; /* Command TAG (e.g. "mid?") + '\0' */
|
|
||||||
uint8_t data[DR_MAX_DATA]; /* Payload bytes following the TAG */
|
|
||||||
uint8_t data_len; /* Valid length of data[] */
|
|
||||||
} ParsedCmd;
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Command table entry - defined in cmd_table.c
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
typedef struct {
|
|
||||||
char tag[5]; /* TAG: 4 chars + '\0' */
|
|
||||||
bool enabled; /* false -> rxd: response */
|
|
||||||
int (*handler)(const ParsedCmd *cmd); /* 1=success, 0=failure */
|
|
||||||
} CmdEntry;
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Parser API
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/* Inject command table (called once at boot). */
|
|
||||||
void dr_parser_init(const CmdEntry *table, uint16_t count);
|
|
||||||
|
|
||||||
/* Entry point: invoked from BLE NUS / UART receive callbacks. */
|
|
||||||
int dr_cmd_parser(const uint8_t *buf, uint8_t len);
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* Public utilities for handlers
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/* Extract Big-Endian uint16. Returns false if not enough data. */
|
|
||||||
bool dr_get_u16(const ParsedCmd *cmd, uint8_t word_index, uint16_t *out);
|
|
||||||
|
|
||||||
/* Extract null-terminated ASCII string. out must hold at least max_len+1. */
|
|
||||||
void dr_get_ascii(const ParsedCmd *cmd, uint8_t offset, char *out, uint8_t max_len);
|
|
||||||
|
|
||||||
/* CRC16 (Nordic SDK CRC-CCITT variant). p_crc=NULL -> initial 0xFFFF. */
|
|
||||||
uint16_t dr_crc16_compute(const uint8_t *p_data, uint32_t size, const uint16_t *p_crc);
|
|
||||||
|
|
||||||
#endif /* PARSER_H */
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// file: debug_print.h
|
||||||
|
/*******************************************************************************
|
||||||
|
* [한국어 설명] 디버그 출력 매크로 (조건부 컴파일)
|
||||||
|
*
|
||||||
|
* SEGGER RTT(Real Time Transfer)를 이용한 디버그 출력 매크로.
|
||||||
|
* J-Link 디버거를 통해 실시간으로 로그를 PC에 전송한다.
|
||||||
|
* UART를 사용하지 않으므로 시스템 타이밍에 미치는 영향이 적다.
|
||||||
|
*
|
||||||
|
* === 조건부 컴파일 ===
|
||||||
|
* ENABLE_PRINTF = 1: DBG_PRINTF가 SEGGER_RTT_printf(채널0)로 치환됨
|
||||||
|
* -> 실제 로그 출력 (디버깅 시 사용)
|
||||||
|
* ENABLE_PRINTF = 0: DBG_PRINTF가 빈 매크로로 치환됨
|
||||||
|
* -> 코드에서 완전히 제거 (릴리스 빌드 시 사용)
|
||||||
|
*
|
||||||
|
* === 사용법 ===
|
||||||
|
* DBG_PRINTF("값: %d\r\n", value); // printf와 동일한 포맷 문자열
|
||||||
|
* 출력은 SEGGER RTT Viewer 또는 J-Link RTT Client에서 확인.
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef DEBUG_PRINT_H
|
||||||
|
#define DEBUG_PRINT_H
|
||||||
|
|
||||||
|
#define ENABLE_PRINTF 1 /* 1=디버그 출력 활성화, 0=전역 비활성화 */
|
||||||
|
|
||||||
|
#if ENABLE_PRINTF
|
||||||
|
#include "SEGGER_RTT.h"
|
||||||
|
/* SEGGER RTT 채널 0으로 포맷 문자열 출력 */
|
||||||
|
#define DBG_PRINTF(...) SEGGER_RTT_printf(0, __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
/* 빈 매크로: 컴파일러가 호출 코드를 완전히 제거 */
|
||||||
|
#define DBG_PRINTF(...) // Do nothing
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // DEBUG_PRINT_H
|
||||||
@@ -0,0 +1,496 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
TEST medi50 Dec 23
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file fstorage.c
|
||||||
|
* @brief FDS(Flash Data Storage) 기반 설정 저장 모듈
|
||||||
|
*
|
||||||
|
* 외부 EEPROM을 대체하여 nRF52840 내장 플래시에 장치 설정을 저장/로드한다.
|
||||||
|
*
|
||||||
|
* [레코드 관리]
|
||||||
|
* - CONFIG_FILE = 0x8010, CONFIG_REC_KEY = 0x7010 으로 단일 레코드를 관리한다.
|
||||||
|
*
|
||||||
|
* [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 값이 0x20231226과 일치하는지 확인하여
|
||||||
|
* 유효한 설정인지 판별한다. 불일치 시 기본값으로 초기화한다.
|
||||||
|
*
|
||||||
|
* [FDS 이벤트 후처리]
|
||||||
|
* - FDS 쓰기/업데이트 완료 이벤트 수신 후, 대기 중인 후처리를 수행한다:
|
||||||
|
* 전원 OFF (go_device_power_off), 슬립 진입 (go_sleep_mode_enter),
|
||||||
|
* 시스템 리셋 (go_NVIC_SystemReset)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sdk_config.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "app_error.h"
|
||||||
|
#include "boards.h"
|
||||||
|
#include "nrf_fstorage.h"
|
||||||
|
#include "nrf_soc.h"
|
||||||
|
#include "nrf_strerror.h"
|
||||||
|
#include "sdk_config.h"
|
||||||
|
#include "nrf_fstorage_sd.h"
|
||||||
|
#include "nrf_delay.h"
|
||||||
|
#include "ble_gap.h"
|
||||||
|
#include "fds.h"
|
||||||
|
|
||||||
|
#include "nrf_log.h"
|
||||||
|
#include "nrf_log_ctrl.h"
|
||||||
|
#include "nrf_log_default_backends.h"
|
||||||
|
|
||||||
|
#include "fstorage.h"
|
||||||
|
#include "nrf_pwr_mgmt.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "debug_print.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* FDS 레코드 식별자: 파일 ID와 레코드 키로 단일 설정 레코드를 관리 */
|
||||||
|
|
||||||
|
#define CONFIG_FILE (0x8010)
|
||||||
|
#define CONFIG_REC_KEY (0x7010)
|
||||||
|
|
||||||
|
/* 매직 넘버: 플래시에 저장된 데이터가 유효한 설정인지 판별하는 데 사용 */
|
||||||
|
#define CONFIG_MAGIC_NUMBER_VALUE (0x20260319)
|
||||||
|
|
||||||
|
/* 전역 설정 데이터 구조체 인스턴스 */
|
||||||
|
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; /* 시스템 리셋 요청 */
|
||||||
|
|
||||||
|
/* FDS 초기화 완료 여부 플래그 (fds_evt_handler에서 true로 설정) */
|
||||||
|
static bool volatile m_fds_initialized;
|
||||||
|
|
||||||
|
/* FDS 쓰기 진행 중 플래그: true이면 쓰기 완료 대기 중 */
|
||||||
|
bool fds_flag_write = false;
|
||||||
|
|
||||||
|
/* FDS에 기록할 레코드 템플릿 (m_config 데이터를 가리킴) */
|
||||||
|
static fds_record_t const m_dummy_record =
|
||||||
|
{
|
||||||
|
.file_id = CONFIG_FILE,
|
||||||
|
.key = CONFIG_REC_KEY,
|
||||||
|
.data.p_data = (void const *)&m_config,
|
||||||
|
/* The length of a record is always expressed in 4-byte units (words). */
|
||||||
|
.data.length_words = (sizeof(m_config) + 3) / sizeof(uint32_t),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* 기본 설정값 상수 */
|
||||||
|
int8_t reset_status_dflt = 99; /* 리셋 상태 기본값 */
|
||||||
|
uint8_t static_passkey_dflt[6] = DEFAULT_PASSKEY; /* BLE 패스키 기본값 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 기본 설정값 초기화
|
||||||
|
*
|
||||||
|
* m_config 구조체의 각 필드를 공장 초기값으로 설정한다.
|
||||||
|
* 플래시에 유효한 설정이 없거나 매직 넘버가 불일치할 때 호출된다.VB0HW0000
|
||||||
|
*/
|
||||||
|
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));
|
||||||
|
|
||||||
|
/* Serial Number - default from 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);
|
||||||
|
|
||||||
|
/* Bond data delete */
|
||||||
|
m_config.bond_data_delete = 1;
|
||||||
|
|
||||||
|
/* Reset status */
|
||||||
|
m_config.reset_status = reset_status_dflt;
|
||||||
|
|
||||||
|
/* 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회 평균화 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 마지막 FDS 이벤트 ID 저장 (디버깅용) */
|
||||||
|
static volatile uint8_t fds_last_evt = 0xFF;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief FDS 이벤트 콜백 핸들러
|
||||||
|
*
|
||||||
|
* 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 : 현재 미사용
|
||||||
|
*/
|
||||||
|
static void fds_evt_handler( fds_evt_t const *p_evt )
|
||||||
|
{
|
||||||
|
fds_last_evt = p_evt->id;
|
||||||
|
|
||||||
|
switch( p_evt->id )
|
||||||
|
{
|
||||||
|
case FDS_EVT_INIT:
|
||||||
|
if( p_evt->result == NRF_SUCCESS )
|
||||||
|
{
|
||||||
|
m_fds_initialized = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FDS_EVT_WRITE:
|
||||||
|
{
|
||||||
|
fds_flag_write = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FDS_EVT_UPDATE:
|
||||||
|
{
|
||||||
|
fds_flag_write = false;
|
||||||
|
|
||||||
|
if(go_device_power_off == true) {
|
||||||
|
/* After flash writing completed, System Power Off */
|
||||||
|
device_power_off();
|
||||||
|
}
|
||||||
|
if(go_sleep_mode_enter == true) {
|
||||||
|
/* After flash writing completed, System go to Sleep Mode */
|
||||||
|
sleep_mode_enter();
|
||||||
|
}
|
||||||
|
if(go_NVIC_SystemReset == true) {
|
||||||
|
/* After flash writing completed, System Reset */
|
||||||
|
DBG_PRINTF("Off FDS_EVENT\r\n");
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FDS_EVT_DEL_RECORD:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FDS_EVT_DEL_FILE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FDS_EVT_GC:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief FDS 초기화 완료 대기
|
||||||
|
*
|
||||||
|
* m_fds_initialized 플래그가 true가 될 때까지 대기한다.
|
||||||
|
* 최대 3초(3000ms) 타임아웃이 설정되어 있으며,
|
||||||
|
* 타임아웃 시 에러 로그를 출력하고 반환한다.
|
||||||
|
*/
|
||||||
|
static void wait_for_fds_ready( void )
|
||||||
|
{
|
||||||
|
uint32_t timeout = 0;
|
||||||
|
while( !m_fds_initialized )
|
||||||
|
{
|
||||||
|
nrf_pwr_mgmt_run();
|
||||||
|
nrf_delay_ms(1);
|
||||||
|
timeout++;
|
||||||
|
if (timeout > 3000) /* 3 second timeout */
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[FDS] TIMEOUT!\r\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief FDS에서 설정 로드
|
||||||
|
*
|
||||||
|
* 플래시에서 CONFIG_FILE/CONFIG_REC_KEY 레코드를 검색하여 m_config에 로드한다.
|
||||||
|
*
|
||||||
|
* 동작 흐름:
|
||||||
|
* 1. fds_record_find()로 레코드 검색 (실패 시 최대 10회 재시도, 100ms 간격)
|
||||||
|
* 2. 레코드 발견 시:
|
||||||
|
* - fds_record_open()으로 열기 (CRC 에러 시 삭제 후 기본값으로 재생성)
|
||||||
|
* - 데이터를 m_config로 복사
|
||||||
|
* - 매직 넘버 불일치 시 기존 레코드 삭제 → 기본값 설정 → 재기록
|
||||||
|
* 3. 레코드 미발견 시:
|
||||||
|
* - 기본값으로 새 레코드 생성 후 다시 로드
|
||||||
|
*/
|
||||||
|
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 대기 카운터
|
||||||
|
|
||||||
|
cfg_load_start:
|
||||||
|
memset((char *)&desc, 0, sizeof(desc));
|
||||||
|
memset((char *)&tok, 0, sizeof(tok));
|
||||||
|
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
|
||||||
|
DBG_PRINTF("[FDS] find rc=%u\r\n", rc);
|
||||||
|
|
||||||
|
/* FDS may not be fully ready yet - retry before writing defaults */
|
||||||
|
if (rc != NRF_SUCCESS && cfg_retry < 10)
|
||||||
|
{
|
||||||
|
cfg_retry++;
|
||||||
|
DBG_PRINTF("[FDS] retry %u/10\r\n", cfg_retry);
|
||||||
|
nrf_delay_ms(100);
|
||||||
|
goto cfg_load_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
/* CRC error or corrupt record - delete and use defaults */
|
||||||
|
DBG_PRINTF("[FDS] open ERR=%u, deleting\r\n", rc);
|
||||||
|
(void)fds_record_delete(&desc);
|
||||||
|
fds_gc();
|
||||||
|
fds_default_value_set();
|
||||||
|
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();
|
||||||
|
|
||||||
|
/* 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) )
|
||||||
|
{
|
||||||
|
rc = fds_gc();
|
||||||
|
APP_ERROR_CHECK(rc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
APP_ERROR_CHECK(rc);
|
||||||
|
}
|
||||||
|
goto cfg_load_start;
|
||||||
|
}
|
||||||
|
DBG_PRINTF("[FDS] Loaded OK\r\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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_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; //
|
||||||
|
|
||||||
|
while(fds_flag_write && fds_wait_cnt < 3000) // FDS write 최대 3초 타임아웃
|
||||||
|
{
|
||||||
|
nrf_pwr_mgmt_run();
|
||||||
|
nrf_delay_ms(1);
|
||||||
|
fds_wait_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fds_flag_write) // FDS 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();
|
||||||
|
APP_ERROR_CHECK(rc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
APP_ERROR_CHECK(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
NRF_LOG_FLUSH();
|
||||||
|
goto cfg_load_start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 현재 설정을 FDS에 저장
|
||||||
|
*
|
||||||
|
* m_config의 내용을 플래시에 기록한다.
|
||||||
|
*
|
||||||
|
* 동작 흐름:
|
||||||
|
* 1. 이전 FDS 쓰기 작업이 진행 중이면 최대 3초 대기
|
||||||
|
* 2. 매직 넘버가 올바르지 않으면 보정
|
||||||
|
* 3. 기존 레코드가 있으면 fds_record_update()로 갱신
|
||||||
|
* - 플래시 공간 부족 시 GC(가비지 컬렉션) 수행 후 재시도
|
||||||
|
* 4. 기존 레코드가 없으면 fds_record_write()로 새로 생성
|
||||||
|
*
|
||||||
|
* 참고: 쓰기 완료는 fds_evt_handler()에서 비동기로 처리되며,
|
||||||
|
* 완료 후 전원 OFF/슬립/리셋 등의 후처리가 수행될 수 있다.
|
||||||
|
*/
|
||||||
|
void config_save( void )
|
||||||
|
{
|
||||||
|
ret_code_t rc;
|
||||||
|
fds_record_desc_t desc = { 0 };
|
||||||
|
fds_find_token_t tok = { 0 };
|
||||||
|
|
||||||
|
DBG_PRINTF("[CFG_SAVE] start\r\n");
|
||||||
|
|
||||||
|
/* Wait for any previous FDS operation to complete */
|
||||||
|
if (fds_flag_write)
|
||||||
|
{
|
||||||
|
uint32_t wait_cnt = 0;
|
||||||
|
|
||||||
|
DBG_PRINTF("[CFG_SAVE] waiting for prev FDS op...\r\n");
|
||||||
|
while (fds_flag_write && wait_cnt < 3000)
|
||||||
|
{
|
||||||
|
nrf_pwr_mgmt_run();
|
||||||
|
nrf_delay_ms(1);
|
||||||
|
wait_cnt++;
|
||||||
|
}
|
||||||
|
if (fds_flag_write)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[CFG_SAVE] TIMEOUT! forcing flag clear\r\n");
|
||||||
|
fds_flag_write = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE )
|
||||||
|
{
|
||||||
|
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset((char *)&desc, 0, sizeof(desc));
|
||||||
|
memset((char *)&tok, 0, sizeof(tok));
|
||||||
|
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
|
||||||
|
DBG_PRINTF("[CFG_SAVE] find rc=%u\r\n", rc);
|
||||||
|
|
||||||
|
if( rc == NRF_SUCCESS )
|
||||||
|
{
|
||||||
|
fds_flag_write = true;
|
||||||
|
rc = fds_record_update(&desc, &m_dummy_record);
|
||||||
|
DBG_PRINTF("[CFG_SAVE] update rc=%u\r\n", rc);
|
||||||
|
|
||||||
|
if( rc == FDS_ERR_NO_SPACE_IN_FLASH )
|
||||||
|
{
|
||||||
|
fds_flag_write = false;
|
||||||
|
rc = fds_gc();
|
||||||
|
DBG_PRINTF("[CFG_SAVE] gc rc=%u, retry\r\n", rc);
|
||||||
|
fds_flag_write = true;
|
||||||
|
rc = fds_record_update(&desc, &m_dummy_record);
|
||||||
|
DBG_PRINTF("[CFG_SAVE] retry rc=%u\r\n", rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( rc != NRF_SUCCESS )
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);
|
||||||
|
fds_flag_write = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[CFG_SAVE] not found, writing new\r\n");
|
||||||
|
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);
|
||||||
|
fds_flag_write = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_PRINTF("[CFG_SAVE] done\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief config_load()의 래퍼 함수
|
||||||
|
*
|
||||||
|
* 외부 모듈에서 설정 로드를 요청할 때 사용한다.
|
||||||
|
*/
|
||||||
|
void fs_set_value(void)
|
||||||
|
{
|
||||||
|
config_load();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief FDS 초기화
|
||||||
|
*
|
||||||
|
* 부팅 시 호출되어 FDS 모듈을 초기화한다.
|
||||||
|
* 1. fds_register()로 이벤트 핸들러 등록
|
||||||
|
* 2. fds_init()로 FDS 초기화 시작
|
||||||
|
* 3. wait_for_fds_ready()로 초기화 완료 대기 (최대 3초)
|
||||||
|
* 4. fds_stat()로 플래시 상태 확인
|
||||||
|
*/
|
||||||
|
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 };
|
||||||
|
rc = fds_stat(&stat);
|
||||||
|
APP_ERROR_CHECK(rc);
|
||||||
|
|
||||||
|
DBG_PRINTF("[FDS] OK\r\n");
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file fstorage.h
|
||||||
|
* @author CandyPops Co.
|
||||||
|
* @version V1.0.0
|
||||||
|
* @date 2022-09-05
|
||||||
|
* @brief FDS(Flash Data Storage) 기반 설정 저장 모듈 인터페이스
|
||||||
|
*******************************************************************************
|
||||||
|
*
|
||||||
|
* [헤더 개요]
|
||||||
|
* nRF52840 내장 플래시에 디바이스 설정을 저장/로드하는 FDS 모듈의 공용 API.
|
||||||
|
* 외부 EEPROM을 대체하며, 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): 디바이스 사용 횟수
|
||||||
|
*
|
||||||
|
* [주요 API]
|
||||||
|
* fs_storage_init(): FDS 초기화 (부트 시 1회)
|
||||||
|
* config_load(): FDS에서 설정 로드 (없으면 기본값 생성)
|
||||||
|
* config_save(): 현재 설정을 FDS에 저장
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef IHP_FSTORAGE_H_
|
||||||
|
#define IHP_FSTORAGE_H_
|
||||||
|
|
||||||
|
#include "sdk_config.h"
|
||||||
|
|
||||||
|
#include "nordic_common.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* 기본 버전 정보 (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번
|
||||||
|
------------------------------------------------------------------------- */
|
||||||
|
#define HARDWARE_VERSION "VBTHW0100"
|
||||||
|
#define SERIAL_NUMBER "VBT26030001"
|
||||||
|
#define DEFAULT_PASSKEY "123456"
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
|
||||||
|
/* 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에 저장하는 디바이스 설정 */
|
||||||
|
|
||||||
|
extern config_data_t m_config;
|
||||||
|
|
||||||
|
void fds_default_value_set(void);
|
||||||
|
void config_load( void );
|
||||||
|
void config_save( void );
|
||||||
|
|
||||||
|
void fs_set_value(void);
|
||||||
|
void fs_storage_init(void);
|
||||||
|
|
||||||
|
#endif /* IHP_FSTORAGE_H_ */
|
||||||
|
|
||||||
@@ -1,456 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* fstorage.c - FDS (Flash Data Storage) configuration module
|
|
||||||
*
|
|
||||||
* Stores and loads device configuration to/from nRF52840 internal flash,
|
|
||||||
* replacing external EEPROM. Coexists safely with the SoftDevice.
|
|
||||||
*
|
|
||||||
* Record management:
|
|
||||||
* CONFIG_FILE = 0x8010, CONFIG_REC_KEY = 0x7010 (single record)
|
|
||||||
*
|
|
||||||
* Magic number validation:
|
|
||||||
* Data loaded from flash is checked against CONFIG_MAGIC_NUMBER_VALUE.
|
|
||||||
* Mismatch -> reinitialise with factory defaults.
|
|
||||||
*
|
|
||||||
* 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"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "app_error.h"
|
|
||||||
#include "boards.h"
|
|
||||||
#include "nrf_fstorage.h"
|
|
||||||
#include "nrf_soc.h"
|
|
||||||
#include "nrf_strerror.h"
|
|
||||||
#include "sdk_config.h"
|
|
||||||
#include "nrf_fstorage_sd.h"
|
|
||||||
#include "nrf_delay.h"
|
|
||||||
#include "ble_gap.h"
|
|
||||||
#include "fds.h"
|
|
||||||
|
|
||||||
#include "nrf_log.h"
|
|
||||||
#include "nrf_log_ctrl.h"
|
|
||||||
#include "nrf_log_default_backends.h"
|
|
||||||
|
|
||||||
#include "fstorage.h"
|
|
||||||
#include "nrf_pwr_mgmt.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "debug_print.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
/* 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 initialisation complete flag (set in fds_evt_handler) */
|
|
||||||
static bool volatile m_fds_initialized;
|
|
||||||
|
|
||||||
/* FDS write-in-progress flag */
|
|
||||||
bool fds_flag_write = false;
|
|
||||||
|
|
||||||
/* FDS record template pointing to m_config */
|
|
||||||
static fds_record_t const m_dummy_record =
|
|
||||||
{
|
|
||||||
.file_id = CONFIG_FILE,
|
|
||||||
.key = CONFIG_REC_KEY,
|
|
||||||
.data.p_data = (void const *)&m_config,
|
|
||||||
/* The length of a record is always expressed in 4-byte units (words). */
|
|
||||||
.data.length_words = (sizeof(m_config) + 3) / sizeof(uint32_t),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Default values */
|
|
||||||
int8_t reset_status_dflt = 99;
|
|
||||||
uint8_t static_passkey_dflt[6] = DEFAULT_PASSKEY;
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* fds_default_value_set - Initialise m_config with factory defaults
|
|
||||||
*
|
|
||||||
* Called when flash contains no valid configuration or the magic number
|
|
||||||
* does not match.
|
|
||||||
*============================================================================*/
|
|
||||||
void fds_default_value_set(void)
|
|
||||||
{
|
|
||||||
/* HW number */
|
|
||||||
memset(m_config.hw_no, 0, 12);
|
|
||||||
memcpy(m_config.hw_no, HARDWARE_VERSION, strlen(HARDWARE_VERSION));
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Bond delete — default: no pending delete */
|
|
||||||
m_config.bond_data_delete = 0;
|
|
||||||
|
|
||||||
/* Reset status */
|
|
||||||
m_config.reset_status = reset_status_dflt;
|
|
||||||
|
|
||||||
/* Device usage count */
|
|
||||||
m_config.life_cycle = 0;
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
|
|
||||||
/* Factory provisioning — default: not provisioned */
|
|
||||||
m_config.factory_provisioned = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Last FDS event ID (for debugging) */
|
|
||||||
static volatile uint8_t fds_last_evt = 0xFF;
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* fds_evt_handler - FDS event callback
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
switch (p_evt->id)
|
|
||||||
{
|
|
||||||
case FDS_EVT_INIT:
|
|
||||||
if (p_evt->result == NRF_SUCCESS)
|
|
||||||
{
|
|
||||||
m_fds_initialized = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FDS_EVT_WRITE:
|
|
||||||
fds_flag_write = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FDS_EVT_UPDATE:
|
|
||||||
fds_flag_write = false;
|
|
||||||
|
|
||||||
if (go_device_power_off == true)
|
|
||||||
{
|
|
||||||
device_power_off();
|
|
||||||
}
|
|
||||||
if (go_sleep_mode_enter == true)
|
|
||||||
{
|
|
||||||
sleep_mode_enter();
|
|
||||||
}
|
|
||||||
if (go_NVIC_SystemReset == true)
|
|
||||||
{
|
|
||||||
DBG_PRINTF("Off FDS_EVENT\r\n");
|
|
||||||
NVIC_SystemReset();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FDS_EVT_DEL_RECORD:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FDS_EVT_DEL_FILE:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FDS_EVT_GC:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* wait_for_fds_ready - Block until FDS initialisation completes
|
|
||||||
*
|
|
||||||
* Times out after 3 seconds with an error log.
|
|
||||||
*============================================================================*/
|
|
||||||
static void wait_for_fds_ready( void )
|
|
||||||
{
|
|
||||||
uint32_t timeout = 0;
|
|
||||||
while(!m_fds_initialized)
|
|
||||||
{
|
|
||||||
nrf_pwr_mgmt_run();
|
|
||||||
nrf_delay_ms(1);
|
|
||||||
timeout++;
|
|
||||||
if (timeout > 3000)
|
|
||||||
{
|
|
||||||
DBG_PRINTF("[FDS] TIMEOUT!\r\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* config_load - Load configuration from FDS
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
cfg_load_start:
|
|
||||||
memset((char *)&desc, 0, sizeof(desc));
|
|
||||||
memset((char *)&tok, 0, sizeof(tok));
|
|
||||||
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
|
|
||||||
DBG_PRINTF("[FDS] find rc=%u\r\n", rc);
|
|
||||||
|
|
||||||
/* FDS may not be fully ready yet - retry before writing defaults */
|
|
||||||
if (rc != NRF_SUCCESS && cfg_retry < 10)
|
|
||||||
{
|
|
||||||
cfg_retry++;
|
|
||||||
DBG_PRINTF("[FDS] retry %u/10\r\n", cfg_retry);
|
|
||||||
nrf_delay_ms(100);
|
|
||||||
goto cfg_load_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc == NRF_SUCCESS)
|
|
||||||
{
|
|
||||||
fds_flash_record_t config = { 0 };
|
|
||||||
|
|
||||||
rc = fds_record_open(&desc, &config);
|
|
||||||
if (rc != NRF_SUCCESS)
|
|
||||||
{
|
|
||||||
/* CRC error or corrupt record - delete and use defaults */
|
|
||||||
DBG_PRINTF("[FDS] open ERR=%u, deleting\r\n", rc);
|
|
||||||
(void)fds_record_delete(&desc);
|
|
||||||
fds_gc();
|
|
||||||
fds_default_value_set();
|
|
||||||
goto cfg_load_write_new;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&m_config, config.p_data, sizeof(config_data_t));
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
fds_default_value_set();
|
|
||||||
|
|
||||||
rc = fds_record_update(&desc, &m_dummy_record);
|
|
||||||
if ((rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH))
|
|
||||||
{
|
|
||||||
rc = fds_gc();
|
|
||||||
APP_ERROR_CHECK(rc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
APP_ERROR_CHECK(rc);
|
|
||||||
}
|
|
||||||
goto cfg_load_start;
|
|
||||||
}
|
|
||||||
DBG_PRINTF("[FDS] Loaded OK\r\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cfg_load_write_new:
|
|
||||||
DBG_PRINTF("[FDS] New - writing defaults\r\n");
|
|
||||||
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
APP_ERROR_CHECK(rc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
APP_ERROR_CHECK(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
NRF_LOG_FLUSH();
|
|
||||||
goto cfg_load_start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* config_save - Persist current configuration to FDS
|
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
*
|
|
||||||
* Write completion is asynchronous (fds_evt_handler). Post-processing
|
|
||||||
* (power-off / sleep / reset) may follow.
|
|
||||||
*============================================================================*/
|
|
||||||
void config_save( void )
|
|
||||||
{
|
|
||||||
ret_code_t rc;
|
|
||||||
fds_record_desc_t desc = { 0 };
|
|
||||||
fds_find_token_t tok = { 0 };
|
|
||||||
|
|
||||||
DBG_PRINTF("[CFG_SAVE] start\r\n");
|
|
||||||
|
|
||||||
/* Wait for any previous FDS operation to complete */
|
|
||||||
if (fds_flag_write)
|
|
||||||
{
|
|
||||||
uint32_t wait_cnt = 0;
|
|
||||||
|
|
||||||
DBG_PRINTF("[CFG_SAVE] waiting for prev FDS op...\r\n");
|
|
||||||
while (fds_flag_write && wait_cnt < 3000)
|
|
||||||
{
|
|
||||||
nrf_pwr_mgmt_run();
|
|
||||||
nrf_delay_ms(1);
|
|
||||||
wait_cnt++;
|
|
||||||
}
|
|
||||||
if (fds_flag_write)
|
|
||||||
{
|
|
||||||
DBG_PRINTF("[CFG_SAVE] TIMEOUT! forcing flag clear\r\n");
|
|
||||||
fds_flag_write = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE)
|
|
||||||
{
|
|
||||||
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset((char *)&desc, 0, sizeof(desc));
|
|
||||||
memset((char *)&tok, 0, sizeof(tok));
|
|
||||||
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
|
|
||||||
DBG_PRINTF("[CFG_SAVE] find rc=%u\r\n", rc);
|
|
||||||
|
|
||||||
if (rc == NRF_SUCCESS)
|
|
||||||
{
|
|
||||||
fds_flag_write = true;
|
|
||||||
rc = fds_record_update(&desc, &m_dummy_record);
|
|
||||||
DBG_PRINTF("[CFG_SAVE] update rc=%u\r\n", rc);
|
|
||||||
|
|
||||||
if (rc == FDS_ERR_NO_SPACE_IN_FLASH)
|
|
||||||
{
|
|
||||||
fds_flag_write = false;
|
|
||||||
rc = fds_gc();
|
|
||||||
DBG_PRINTF("[CFG_SAVE] gc rc=%u, retry\r\n", rc);
|
|
||||||
fds_flag_write = true;
|
|
||||||
rc = fds_record_update(&desc, &m_dummy_record);
|
|
||||||
DBG_PRINTF("[CFG_SAVE] retry rc=%u\r\n", rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc != NRF_SUCCESS)
|
|
||||||
{
|
|
||||||
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);
|
|
||||||
fds_flag_write = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DBG_PRINTF("[CFG_SAVE] not found, writing new\r\n");
|
|
||||||
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);
|
|
||||||
fds_flag_write = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DBG_PRINTF("[CFG_SAVE] done\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* fs_set_value - Wrapper for config_load()
|
|
||||||
*============================================================================*/
|
|
||||||
void fs_set_value(void)
|
|
||||||
{
|
|
||||||
config_load();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* fs_storage_init - Initialise FDS
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
rc = fds_register(fds_evt_handler);
|
|
||||||
APP_ERROR_CHECK(rc);
|
|
||||||
|
|
||||||
rc = fds_init();
|
|
||||||
APP_ERROR_CHECK(rc);
|
|
||||||
|
|
||||||
wait_for_fds_ready();
|
|
||||||
|
|
||||||
fds_stat_t stat = { 0 };
|
|
||||||
rc = fds_stat(&stat);
|
|
||||||
APP_ERROR_CHECK(rc);
|
|
||||||
|
|
||||||
DBG_PRINTF("[FDS] OK\r\n");
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* fstorage.h - FDS (Flash Data Storage) configuration module interface
|
|
||||||
*
|
|
||||||
* 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 (49 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
|
|
||||||
* factory_provisioned(1B): passkey provisioning lock flag
|
|
||||||
*
|
|
||||||
* 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_
|
|
||||||
|
|
||||||
#include "sdk_config.h"
|
|
||||||
|
|
||||||
#include "nordic_common.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
|
||||||
* 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"
|
|
||||||
|
|
||||||
#pragma pack(1)
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
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 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) */
|
|
||||||
|
|
||||||
/* Factory provisioning lock */
|
|
||||||
uint8_t factory_provisioned; /* 1B - 0=passkey not set, 1=passkey set (locked) */
|
|
||||||
} config_data_t; /* Total: 49 bytes */
|
|
||||||
|
|
||||||
extern config_data_t m_config;
|
|
||||||
|
|
||||||
void fds_default_value_set(void);
|
|
||||||
void config_load( void );
|
|
||||||
void config_save( void );
|
|
||||||
|
|
||||||
void fs_set_value(void);
|
|
||||||
void fs_storage_init(void);
|
|
||||||
|
|
||||||
#endif /* IHP_FSTORAGE_H_ */
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* i2c_manager.c - HW / SW I2C mutex switching logic
|
|
||||||
*
|
|
||||||
* Manages mutually-exclusive HW (TWI peripheral) and SW (bit-bang) I2C modes.
|
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
*
|
|
||||||
* 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"
|
|
||||||
#include "nrf_delay.h"
|
|
||||||
|
|
||||||
#include "nrf_drv_twi.h"
|
|
||||||
#include "nrfx_twi.h"
|
|
||||||
#include "boards.h"
|
|
||||||
#include "system_interface.h"
|
|
||||||
|
|
||||||
/* Current I2C mode flags */
|
|
||||||
bool HW_I2C_FRQ = true;
|
|
||||||
bool SW_I2C_FRQ = false;
|
|
||||||
|
|
||||||
/* TWI instance (nRF52840 supports TWI0 and TWI1) */
|
|
||||||
#define TWI_INSTANCE 0
|
|
||||||
const nrfx_twi_t m_twi = NRFX_TWI_INSTANCE(TWI_INSTANCE);
|
|
||||||
|
|
||||||
/* Disable and uninitialise the TWI peripheral, releasing GPIO pins. */
|
|
||||||
static void twi_uninitialize(void)
|
|
||||||
{
|
|
||||||
nrfx_twi_disable(&m_twi);
|
|
||||||
nrfx_twi_uninit(&m_twi);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialise the TWI peripheral (SCL/SDA pins, 400 kHz, blocking mode). */
|
|
||||||
static void twi_initialize(void)
|
|
||||||
{
|
|
||||||
ret_code_t err_code;
|
|
||||||
|
|
||||||
const nrfx_twi_config_t twi_config =
|
|
||||||
{
|
|
||||||
.scl = ICM42670_I2C_SCL_PIN,
|
|
||||||
.sda = ICM42670_I2C_SDA_PIN,
|
|
||||||
.frequency = NRF_TWI_FREQ_400K,
|
|
||||||
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
|
|
||||||
};
|
|
||||||
|
|
||||||
err_code = nrfx_twi_init(&m_twi, &twi_config, NULL, NULL);
|
|
||||||
APP_ERROR_CHECK(err_code);
|
|
||||||
|
|
||||||
nrfx_twi_enable(&m_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)
|
|
||||||
{
|
|
||||||
if (SW_I2C_FRQ)
|
|
||||||
{
|
|
||||||
SW_I2C_FRQ = false;
|
|
||||||
nrf_delay_ms(2); /* mode-switch settling */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HW_I2C_FRQ)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
twi_initialize();
|
|
||||||
nrf_delay_ms(2);
|
|
||||||
|
|
||||||
HW_I2C_FRQ = true;
|
|
||||||
SW_I2C_FRQ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
if (HW_I2C_FRQ)
|
|
||||||
{
|
|
||||||
nrfx_twi_disable(&m_twi);
|
|
||||||
nrfx_twi_uninit(&m_twi);
|
|
||||||
nrf_delay_ms(2);
|
|
||||||
HW_I2C_FRQ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SW_I2C_FRQ)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
twi_uninitialize();
|
|
||||||
nrf_delay_ms(1);
|
|
||||||
|
|
||||||
SW_I2C_FRQ = true;
|
|
||||||
HW_I2C_FRQ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* 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;
|
|
||||||
SW_I2C_FRQ = false;
|
|
||||||
DBG_PRINTF("Flags reset\r\n");
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* i2c_manager.h - HW / SW I2C mutex control
|
|
||||||
*
|
|
||||||
* Manages the mutually-exclusive HW TWI and SW bit-bang I2C modes.
|
|
||||||
* Only one mode may be active at a time.
|
|
||||||
*
|
|
||||||
* 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 <stdbool.h>
|
|
||||||
#include "app_error.h"
|
|
||||||
|
|
||||||
extern bool HW_I2C_FRQ;
|
|
||||||
extern bool SW_I2C_FRQ;
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Initialise SW I2C (bit-bang) mode (legacy, currently unused).
|
|
||||||
* If HW mode is active it is released first. */
|
|
||||||
void sw_i2c_init_once(void);
|
|
||||||
|
|
||||||
/* Reset both mode flags to false, forcing re-initialisation on next call. */
|
|
||||||
void i2c_reset_state(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
set CURDIR=%cd%
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
|
||||||
|
copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex
|
||||||
|
|
||||||
|
nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex
|
||||||
|
|
||||||
|
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex
|
||||||
|
mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex
|
||||||
|
mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex
|
||||||
|
|
||||||
|
nrfjprog --family NRF52 --recover
|
||||||
|
nrfjprog --family NRF52 --eraseall
|
||||||
|
nrfjprog --family NRF52 --program medithings_bladder_patch_dfu_merged_all.hex
|
||||||
|
nrfjprog --family NRF52 --reset
|
||||||
|
nrfjprog --family NRF52 --rbp ALL
|
||||||
|
|
||||||
|
|
||||||
|
pause
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
set CURDIR=%cd%
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
|
||||||
|
copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex
|
||||||
|
|
||||||
|
nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex
|
||||||
|
|
||||||
|
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex
|
||||||
|
mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex
|
||||||
|
mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex
|
||||||
|
|
||||||
|
nrfjprog --family NRF52 --recover
|
||||||
|
nrfjprog --family NRF52 --eraseall
|
||||||
|
nrfjprog --family NRF52 --program medithings_bladder_patch_dfu_merged_all.hex
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pause
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
set CURDIR=%cd%
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
|
||||||
|
copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex
|
||||||
|
|
||||||
|
nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex
|
||||||
|
|
||||||
|
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex
|
||||||
|
mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex
|
||||||
|
mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex
|
||||||
|
|
||||||
|
|
||||||
|
nrfjprog --family NRF52 --eraseall
|
||||||
|
nrfjprog --family NRF52 --program medithings_bladder_patch_dfu_merged_all.hex
|
||||||
|
nrfjprog --family NRF52 --reset
|
||||||
|
nrfjprog --family NRF52 --rbp ALL
|
||||||
|
|
||||||
|
|
||||||
|
pause
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
set CURDIR=%cd%
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
|
||||||
|
copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex
|
||||||
|
|
||||||
|
nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex
|
||||||
|
|
||||||
|
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex
|
||||||
|
mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex
|
||||||
|
mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex
|
||||||
|
|
||||||
|
nrfjprog --family NRF52 --program medithings_bladder_patch_0001.hex
|
||||||
|
nrfjprog --family NRF52 --reset
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pause
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
set path=d:\nrfutil\bin;%path%
|
||||||
|
d:
|
||||||
|
cd D:\mt_project\vesiscan\project\ble_peripheral\ble_app_bladder_patch\hex
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file i2c_manager.c
|
||||||
|
* @brief Reliable HW↔SW I2C Switching Logic (with Mode Set Logging)
|
||||||
|
*******************************************************************************
|
||||||
|
*
|
||||||
|
* [모듈 개요]
|
||||||
|
* I2C 버스의 HW(하드웨어 TWI) / SW(소프트웨어 비트뱅) 모드 전환을 관리하는 모듈.
|
||||||
|
*
|
||||||
|
* - HW I2C: nRF52840 내장 TWI 하드웨어 주변장치를 사용 (ICM42670P IMU 센서 통신용, 400kHz)
|
||||||
|
* - SW I2C: GPIO 비트뱅 방식의 소프트웨어 I2C (현재 사용하지 않는 레거시 코드)
|
||||||
|
*
|
||||||
|
* [핀 설정]
|
||||||
|
* 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) 방식으로 관리한다.
|
||||||
|
* 모드 전환 시 기존 모드의 리소스를 먼저 해제한 후 새 모드를 초기화한다.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "i2c_manager.h"
|
||||||
|
#include "debug_print.h"
|
||||||
|
#include "nrf_delay.h"
|
||||||
|
|
||||||
|
#include "nrf_drv_twi.h"
|
||||||
|
#include "nrfx_twi.h"
|
||||||
|
#include "boards.h"
|
||||||
|
#include "system_interface.h"
|
||||||
|
|
||||||
|
/* 현재 I2C 모드 상태 플래그 (true = 해당 모드 활성화) */
|
||||||
|
bool HW_I2C_FRQ = true; /* HW TWI 모드 활성 여부 (기본값: true, 초기 상태는 HW) */
|
||||||
|
bool SW_I2C_FRQ = false; /* SW 비트뱅 모드 활성 여부 */
|
||||||
|
|
||||||
|
/* TWI 인스턴스 번호 (nRF52840은 TWI0, 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 초기화 해제 (핀 리소스 반환) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TWI (I2C) 하드웨어 초기화 (SCL/SDA핀, 400kHz) - jhChun 26.03.16 */
|
||||||
|
/* SCL, SDA 핀을 설정하고 400kHz Fast Mode로 TWI를 초기화 및 활성화한다 */
|
||||||
|
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, /* 높은 인터럽트 우선순위 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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 통신 가능 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* HW (TWI) 초기화 */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/*
|
||||||
|
* HW I2C 모드로 전환하거나 초기화하는 함수.
|
||||||
|
* - SW 모드가 활성화되어 있으면 SW 플래그를 해제하고 HW로 전환
|
||||||
|
* - 이미 HW 모드이면 중복 초기화를 방지하여 바로 리턴
|
||||||
|
* - 최초 HW 초기화 시 twi_initialize()를 호출하여 TWI 하드웨어 설정
|
||||||
|
*/
|
||||||
|
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) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 이미 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) */
|
||||||
|
|
||||||
|
/* 모드 플래그 갱신: 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에서 호출됨
|
||||||
|
*/
|
||||||
|
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) */
|
||||||
|
|
||||||
|
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) */
|
||||||
|
|
||||||
|
/* 모드 플래그 갱신: SW 활성, HW 비활성 */
|
||||||
|
SW_I2C_FRQ = true;
|
||||||
|
HW_I2C_FRQ = false;
|
||||||
|
|
||||||
|
// DBG_PRINTF("[I2C] SW I2C Mode set!\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* 전체 리셋 */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/*
|
||||||
|
* 모든 I2C 모드 플래그를 초기화하는 함수.
|
||||||
|
* HW/SW 모두 비활성 상태로 만들어, 다음 init 호출 시 강제로 재초기화되도록 한다.
|
||||||
|
* 주로 시스템 리셋이나 에러 복구 시 사용.
|
||||||
|
*/
|
||||||
|
void i2c_reset_state(void)
|
||||||
|
{
|
||||||
|
HW_I2C_FRQ = false; /* HW 모드 플래그 초기화 */
|
||||||
|
SW_I2C_FRQ = false; /* SW 모드 플래그 초기화 */
|
||||||
|
DBG_PRINTF("Flags reset\r\n");
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file i2c_manager.h
|
||||||
|
* @brief Common header for HW/SW I2C mutex control
|
||||||
|
*******************************************************************************
|
||||||
|
*
|
||||||
|
* [헤더 개요]
|
||||||
|
* I2C 버스 HW/SW 모드 전환 관리자의 공용 인터페이스 헤더.
|
||||||
|
*
|
||||||
|
* - HW_I2C_FRQ: HW TWI 모드 활성 여부 (true = HW I2C 사용 중)
|
||||||
|
* - SW_I2C_FRQ: SW 비트뱅 모드 활성 여부 (true = SW I2C 사용 중)
|
||||||
|
*
|
||||||
|
* 두 플래그는 상호 배제적으로 동작하며, 동시에 true가 되지 않도록 관리된다.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef __I2C_MANAGER_H__
|
||||||
|
#define __I2C_MANAGER_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "app_error.h"
|
||||||
|
|
||||||
|
/* I2C 모드 상태 플래그 (외부 참조용) */
|
||||||
|
extern bool HW_I2C_FRQ; /* HW TWI 모드 활성 여부 */
|
||||||
|
extern bool SW_I2C_FRQ; /* SW 비트뱅 모드 활성 여부 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HW I2C(TWI) 모드 초기화 (중복 초기화 방지)
|
||||||
|
*
|
||||||
|
* SW 모드가 활성화되어 있으면 해제 후 HW로 전환한다.
|
||||||
|
* 이미 HW 모드이면 아무 동작 없이 리턴한다.
|
||||||
|
* ICM42670P IMU 센서 통신 전에 호출하여 HW I2C를 준비한다.
|
||||||
|
*/
|
||||||
|
void hw_i2c_init_once(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SW I2C(비트뱅) 모드 초기화 (레거시, 현재 미사용)
|
||||||
|
*
|
||||||
|
* HW 모드가 활성화되어 있으면 TWI를 해제한 후 SW로 전환한다.
|
||||||
|
* 이미 SW 모드이면 아무 동작 없이 리턴한다.
|
||||||
|
*/
|
||||||
|
void sw_i2c_init_once(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I2C 모드 플래그 전체 초기화
|
||||||
|
*
|
||||||
|
* HW_I2C_FRQ, SW_I2C_FRQ를 모두 false로 리셋한다.
|
||||||
|
* 다음 init 호출 시 강제로 재초기화가 수행된다.
|
||||||
|
*/
|
||||||
|
void i2c_reset_state(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
+136
-136
@@ -7,29 +7,28 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* [Module overview] ICM42670P IMU driver application layer
|
* [모듈 개요] ICM42670P IMU 드라이버 상위 레이어
|
||||||
*
|
*
|
||||||
* Application layer module responsible for initialization, configuration,
|
* ICM42670P IMU 센서의 초기화, 설정, 데이터 읽기를 담당하는 애플리케이션 레이어 모듈
|
||||||
* and data reading of the ICM42670P IMU sensor. Wraps InvenSense driver API.
|
* InvenSense 드라이버 API를 래핑하여 사용
|
||||||
*
|
*
|
||||||
* Key functions:
|
* 주요 기능:
|
||||||
* 1) setup_imu_device() - IMU init and WHOAMI verification (0x67 = ICM42670P)
|
* 1) setup_imu_device() - IMU 초기화 및 WHOAMI 확인 (0x67 = ICM42670P)
|
||||||
* 2) configure_imu_device() - Sensor parameter configuration
|
* 2) configure_imu_device() - 센서 파라미터 설정
|
||||||
* - Accelerometer: +/-4g FSR, 100Hz (low-power) or 800Hz (low-noise)
|
* - 가속도계: ±4g FSR, 100Hz(저전력) 또는 800Hz(저잡음)
|
||||||
* - Gyroscope: +/-2000dps FSR, 100Hz or 800Hz
|
* - 자이로: ±2000dps FSR, 100Hz 또는 800Hz
|
||||||
* - FIFO disabled (direct register read mode)
|
* - FIFO 비활성화 (레지스터 직접 읽기 모드)
|
||||||
* 3) get_imu_data() - Read sensor data from FIFO or registers
|
* 3) get_imu_data() - FIFO 또는 레지스터에서 센서 데이터 읽기
|
||||||
* 4) imu_callback() - Sensor data receive callback
|
* 4) imu_callback() - 센서 데이터 수신 콜백
|
||||||
* - Applies mounting matrix (board orientation correction)
|
* - 마운팅 매트릭스 적용 (보드 방향 보정)
|
||||||
* - info4 mode: stores data in info_imu[6]
|
* - info4 모드: info_imu[6]에 데이터 저장
|
||||||
* - BLE mode: sends 6-axis data via BLE with "rsp:" tag
|
* - BLE 모드: "rsp:" 태그로 6축 데이터 BLE 전송
|
||||||
* - UART mode: outputs text format to serial
|
* - UART 모드: 텍스트 형식으로 시리얼 출력
|
||||||
* 5) imu_read_direct() - Direct I2C register read bypassing driver API
|
* 5) imu_read_direct() - 드라이버 API를 우회한 직접 I2C 레지스터 읽기
|
||||||
* - Configure sensor -> power ON -> wait 80ms -> read 12 bytes -> sleep
|
* - 센서 설정 → 전원 ON → 80ms 대기 → 12바이트 읽기 → 슬립
|
||||||
*
|
*
|
||||||
* Mounting matrix:
|
* 마운팅 매트릭스:
|
||||||
* 3x3 rotation matrix in Q30 fixed-point format, correcting the sensor's
|
* Q30 고정소수점 형식의 3x3 회전 매트릭스로, 보드에 장착된 센서의물리적 방향을 소프트웨어 좌표계에 맞춰 보정
|
||||||
* physical mounting orientation to match the software coordinate system.
|
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#include "sdk_config.h"
|
#include "sdk_config.h"
|
||||||
@@ -48,9 +47,9 @@
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Data output format selection
|
* 데이터 출력 형식 선택
|
||||||
* 0 : Raw data output (raw accel, gyro, temp)
|
* 0 : 원시 데이터 (raw accel, gyro, temp) 출력
|
||||||
* 1 : Scaled data output (g, dps, Celsius)
|
* 1 : 스케일링된 데이터 (g, dps, 섭씨) 출력
|
||||||
*/
|
*/
|
||||||
#define SCALED_DATA_G_DPS 0
|
#define SCALED_DATA_G_DPS 0
|
||||||
|
|
||||||
@@ -59,20 +58,20 @@
|
|||||||
* Static and extern variables
|
* Static and extern variables
|
||||||
* -------------------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/* IMU driver object — always passed to driver API calls */
|
/* IMU 드라이버 객체 — 드라이버 API 호출 시 항상 이 구조체 전달 */
|
||||||
static struct inv_imu_device icm_driver;
|
static struct inv_imu_device icm_driver;
|
||||||
|
|
||||||
/* Binary buffer for BLE transmission */
|
/* BLE 전송용 바이너리 버퍼 */
|
||||||
uint8_t imu_bin_buffer[BLE_NUS_MAX_DATA_LEN] = {0};
|
uint8_t imu_bin_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ICM42670P mounting matrix (Q30 fixed-point)
|
* ICM42670P 마운팅 매트릭스 (Q30 고정소수점)
|
||||||
*
|
*
|
||||||
* Coordinate transform based on the sensor's physical mounting orientation.
|
* 센서가 보드에 장착된 물리적 방향에 따라 좌표 변환
|
||||||
* Q30 format: 1.0 = (1 << 30) = 0x40000000
|
* Q30 형식: 1.0 = (1 << 30) = 0x40000000
|
||||||
*
|
*
|
||||||
* SM_REVB_DB (dev board): X->-Y, Y->X transform (90-degree rotation)
|
* SM_REVB_DB (개발보드): X→-Y, Y→X 변환 (90도 회전)
|
||||||
* Default (SmartMotion): identity matrix (no transform)
|
* 기본 (SmartMotion): 단위 행렬 (변환 없음)
|
||||||
*/
|
*/
|
||||||
#if (SM_BOARD_REV == SM_REVB_DB) /* when DB or EVB are used */
|
#if (SM_BOARD_REV == SM_REVB_DB) /* when DB or EVB are used */
|
||||||
static int32_t icm_mounting_matrix[9] = { 0, -(1<<30), 0,
|
static int32_t icm_mounting_matrix[9] = { 0, -(1<<30), 0,
|
||||||
@@ -84,13 +83,13 @@ static int32_t icm_mounting_matrix[9] = {(1<<30), 0, 0,
|
|||||||
0, 0, (1<<30)};
|
0, 0, (1<<30)};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool custom_add_data; /* Custom data append flag (BLE transmission control) */
|
bool custom_add_data; /* 커스텀 데이터 추가 플래그 (BLE 전송 제어) */
|
||||||
extern bool motion_raw_data_enabled; /* Flag requesting raw data read from external module */
|
extern bool motion_raw_data_enabled; /* 외부에서 원시 데이터 읽기를 요청하는 플래그 */
|
||||||
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN]; /* BLE text transmit buffer */
|
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN]; /* BLE 텍스트 전송 버퍼 */
|
||||||
extern which_cmd_t cmd_type_t; /* Current command source (BLE or UART) */
|
extern which_cmd_t cmd_type_t; /* 현재 명령 소스 (BLE 또는 UART) */
|
||||||
uint16_t ssp_data[6]={0,}; /* 6-axis data array for BLE (accel XYZ + gyro XYZ) */
|
uint16_t ssp_data[6]={0,}; /* BLE 전송용 6축 데이터 배열 (accel XYZ + gyro XYZ) */
|
||||||
extern bool info4; /* info4 mode flag (set by cmd_parse) */
|
extern bool info4; /* info4 모드 플래그 (cmd_parse에서 설정) */
|
||||||
volatile uint16_t info_imu[6]; /* Global array storing IMU data in info4 mode */
|
volatile uint16_t info_imu[6]; /* info4 모드에서 IMU 데이터를 저장하는 전역 배열 */
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------
|
||||||
* static function declaration
|
* static function declaration
|
||||||
@@ -103,35 +102,35 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3]);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* setup_imu_device()
|
* setup_imu_device()
|
||||||
* IMU device initialization and identification verification.
|
* IMU 디바이스 초기화 및 식별 확인
|
||||||
*
|
*
|
||||||
* Flow:
|
* 처리 흐름:
|
||||||
* 1) Initialize driver via inv_imu_init() (serial interface + callback registration)
|
* 1) inv_imu_init()으로 드라이버 초기화 (시리얼 인터페이스 + 콜백 등록)
|
||||||
* 2) Read WHOAMI register for device identification
|
* 2) WHOAMI 레지스터 읽기로 디바이스 확인
|
||||||
* 3) Verify WHOAMI value matches ICM_WHOAMI (0x67)
|
* 3) WHOAMI 값이 ICM_WHOAMI(0x67)와 일치하는지 검증
|
||||||
*
|
*
|
||||||
* Returns: 0=success, negative=error
|
* 반환값: 0=성공, 음수=에러
|
||||||
*/
|
*/
|
||||||
int setup_imu_device(struct inv_imu_serif *icm_serif)
|
int setup_imu_device(struct inv_imu_serif *icm_serif)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
uint8_t who_am_i;
|
uint8_t who_am_i;
|
||||||
|
|
||||||
/* Initialize IMU driver — connect serial interface and register callback */
|
/* IMU 드라이버 초기화 — 시리얼 인터페이스 연결 및 콜백 함수 등록 */
|
||||||
rc = inv_imu_init(&icm_driver, icm_serif, imu_callback);
|
rc = inv_imu_init(&icm_driver, icm_serif, imu_callback);
|
||||||
if (rc != INV_ERROR_SUCCESS) {
|
if (rc != INV_ERROR_SUCCESS) {
|
||||||
DBG_PRINTF("!!! ERROR : Failed to initialize IMU!\r\n");
|
DBG_PRINTF("!!! ERROR : Failed to initialize IMU!\r\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read WHOAMI register — verify device presence and communication */
|
/* WHOAMI 레지스터 읽기 — 디바이스 존재 및 통신 확인 */
|
||||||
rc = inv_imu_get_who_am_i(&icm_driver, &who_am_i);
|
rc = inv_imu_get_who_am_i(&icm_driver, &who_am_i);
|
||||||
if (rc != INV_ERROR_SUCCESS) {
|
if (rc != INV_ERROR_SUCCESS) {
|
||||||
DBG_PRINTF("!!! ERROR : Failed to read whoami!\r\n");
|
DBG_PRINTF("!!! ERROR : Failed to read whoami!\r\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verify WHOAMI value — must be 0x67 for ICM42670P */
|
/* WHOAMI 값 검증 — ICM42670P의 경우 0x67이어야 함 */
|
||||||
if (who_am_i != ICM_WHOAMI) {
|
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);
|
DBG_PRINTF("!!! ERROR : Bad WHOAMI value! Read 0x%02x, expected 0x%02x\r\n", who_am_i, ICM_WHOAMI);
|
||||||
return INV_ERROR;
|
return INV_ERROR;
|
||||||
@@ -142,53 +141,53 @@ int setup_imu_device(struct inv_imu_serif *icm_serif)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* configure_imu_device()
|
* configure_imu_device()
|
||||||
* Configures IMU sensor operating parameters.
|
* IMU 센서 동작 파라미터 설정
|
||||||
*
|
*
|
||||||
* Settings:
|
* 설정 항목:
|
||||||
* - FIFO: disabled (when USE_FIFO=0, direct register read mode)
|
* - FIFO: 비활성화 (USE_FIFO=0일 때, 레지스터 직접 읽기 모드)
|
||||||
* - Accel FSR: +/-4g (when USE_HIGH_RES_MODE=0)
|
* - 가속도계 FSR: ±4g (USE_HIGH_RES_MODE=0일 때)
|
||||||
* - Gyro FSR: +/-2000dps
|
* - 자이로 FSR: ±2000dps
|
||||||
* - ODR (output data rate):
|
* - ODR(출력 데이터율):
|
||||||
* - Low-noise mode (USE_LOW_NOISE_MODE=1): 800Hz
|
* - 저잡음 모드(USE_LOW_NOISE_MODE=1): 800Hz
|
||||||
* - Low-power mode (USE_LOW_NOISE_MODE=0): 100Hz
|
* - 저전력 모드(USE_LOW_NOISE_MODE=0): 100Hz
|
||||||
* - Gyro always operates in low-noise mode
|
* - 자이로는 항상 저잡음 모드로 동작
|
||||||
* - Waits for gyro startup time when FIFO is not used
|
* - FIFO 미사용 시 자이로 스타트업 시간만큼 대기
|
||||||
*
|
*
|
||||||
* Returns: 0=success, negative=error
|
* 반환값: 0=성공, 음수=에러
|
||||||
*/
|
*/
|
||||||
int configure_imu_device(void)
|
int configure_imu_device(void)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
/* Disable FIFO — read data directly from registers */
|
/* FIFO 비활성화 — 레지스터에서 직접 데이터를 읽기 */
|
||||||
if (!USE_FIFO)
|
if (!USE_FIFO)
|
||||||
rc |= inv_imu_configure_fifo(&icm_driver, INV_IMU_FIFO_DISABLED);
|
rc |= inv_imu_configure_fifo(&icm_driver, INV_IMU_FIFO_DISABLED);
|
||||||
|
|
||||||
if (USE_HIGH_RES_MODE) {
|
if (USE_HIGH_RES_MODE) {
|
||||||
/* High-resolution FIFO mode: 20-bit data, FSR locked to 16g/2000dps */
|
/* 고해상도 FIFO 모드: 20비트 데이터, FSR은 16g/2000dps로 고정됨 */
|
||||||
rc |= inv_imu_enable_high_resolution_fifo(&icm_driver);
|
rc |= inv_imu_enable_high_resolution_fifo(&icm_driver);
|
||||||
} else {
|
} else {
|
||||||
/* Standard mode: accel +/-4g, gyro +/-2000dps FSR */
|
/* 표준 모드: 가속도계 ±4g, 자이로 ±2000dps FSR 설정 */
|
||||||
rc |= inv_imu_set_accel_fsr(&icm_driver, ACCEL_CONFIG0_FS_SEL_4g);
|
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);
|
rc |= inv_imu_set_gyro_fsr(&icm_driver, GYRO_CONFIG0_FS_SEL_2000dps);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (USE_LOW_NOISE_MODE) {
|
if (USE_LOW_NOISE_MODE) {
|
||||||
/* Low-noise mode: 800Hz ODR, enable accel low-noise mode */
|
/* 저잡음 모드: 800Hz ODR, 가속도계 저잡음 모드 활성화 */
|
||||||
rc |= inv_imu_set_accel_frequency(&icm_driver, ACCEL_CONFIG0_ODR_800_HZ);
|
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_set_gyro_frequency(&icm_driver, GYRO_CONFIG0_ODR_800_HZ);
|
||||||
rc |= inv_imu_enable_accel_low_noise_mode(&icm_driver);
|
rc |= inv_imu_enable_accel_low_noise_mode(&icm_driver);
|
||||||
} else {
|
} else {
|
||||||
/* Low-power mode: 100Hz ODR, enable accel low-power mode */
|
/* 저전력 모드: 100Hz ODR, 가속도계 저전력 모드 활성화 */
|
||||||
rc |= inv_imu_set_accel_frequency(&icm_driver, ACCEL_CONFIG0_ODR_100_HZ);
|
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_set_gyro_frequency(&icm_driver, GYRO_CONFIG0_ODR_100_HZ);
|
||||||
rc |= inv_imu_enable_accel_low_power_mode(&icm_driver);
|
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);
|
rc |= inv_imu_enable_gyro_low_noise_mode(&icm_driver);
|
||||||
|
|
||||||
/* When FIFO is not used, wait for gyro startup time (delay until first valid data) */
|
/* FIFO 미사용 시 자이로 스타트업 시간만큼 대기 (첫 유효 데이터까지의 지연) */
|
||||||
if (!USE_FIFO)
|
if (!USE_FIFO)
|
||||||
inv_imu_sleep_us(GYR_STARTUP_TIME_US);
|
inv_imu_sleep_us(GYR_STARTUP_TIME_US);
|
||||||
|
|
||||||
@@ -198,9 +197,9 @@ int configure_imu_device(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* get_imu_data()
|
* get_imu_data()
|
||||||
* Reads sensor data from the IMU.
|
* IMU에서 센서 데이터 읽기
|
||||||
* Fetches data from FIFO or registers depending on USE_FIFO setting.
|
* USE_FIFO 설정에 따라 FIFO 또는 레지스터에서 데이터를 가져옴
|
||||||
* Read data is processed via imu_callback().
|
* 읽은 데이터는 imu_callback()을 통해 처리
|
||||||
*/
|
*/
|
||||||
int get_imu_data(void)
|
int get_imu_data(void)
|
||||||
{
|
{
|
||||||
@@ -215,8 +214,8 @@ int get_imu_data(void)
|
|||||||
#if SCALED_DATA_G_DPS
|
#if SCALED_DATA_G_DPS
|
||||||
/*
|
/*
|
||||||
* get_accel_and_gyr_fsr()
|
* get_accel_and_gyr_fsr()
|
||||||
* Retrieves the currently configured FSR (Full Scale Range) for accel and gyro.
|
* 현재 설정된 가속도계와 자이로의 FSR(Full Scale Range) 값을 가져온다.
|
||||||
* Used for converting to scaled data (g, dps).
|
* 스케일링된 데이터(g, dps) 변환에 사용된다.
|
||||||
*/
|
*/
|
||||||
static void get_accel_and_gyr_fsr(int16_t * accel_fsr_g, int16_t * gyro_fsr_dps)
|
static void get_accel_and_gyr_fsr(int16_t * accel_fsr_g, int16_t * gyro_fsr_dps)
|
||||||
{
|
{
|
||||||
@@ -254,17 +253,17 @@ static void get_accel_and_gyr_fsr(int16_t * accel_fsr_g, int16_t * gyro_fsr_dps)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* imu_callback()
|
* imu_callback()
|
||||||
* Callback invoked each time the IMU driver reads new sensor data.
|
* IMU 드라이버가 새 센서 데이터를 읽을 때마다 호출되는 콜백 함수
|
||||||
*
|
*
|
||||||
* Flow:
|
* 처리 흐름:
|
||||||
* 1) Extract raw accel/gyro data from event
|
* 1) 이벤트에서 가속도/자이로 원시 데이터 추출
|
||||||
* - FIFO mode: handles timestamp rollover, supports high-res (20-bit)
|
* - FIFO 모드: 타임스탬프 롤오버 처리, 고해상도(20비트) 지원
|
||||||
* - Register mode: uses 16-bit data directly
|
* - 레지스터 모드: 16비트 데이터 직접 사용
|
||||||
* 2) Apply mounting matrix (board orientation correction)
|
* 2) 마운팅 매트릭스 적용 (보드 장착 방향 보정)
|
||||||
* 3) Output data (branches by mode):
|
* 3) 데이터 출력 (모드에 따라 분기):
|
||||||
* - info4 mode: stores in info_imu[6] global array (polled externally)
|
* - info4 모드: info_imu[6] 전역 배열에 저장 (외부에서 폴링)
|
||||||
* - UART mode: text output with "Tp" prefix for 6-axis data
|
* - UART 모드: "Tp" 접두사로 6축 데이터 텍스트 출력
|
||||||
* - BLE mode: binary packet with "rsp:" tag + simultaneous UART output
|
* - BLE 모드: "rsp:" 태그로 바이너리 패킷 전송 + UART 텍스트 동시 출력
|
||||||
*/
|
*/
|
||||||
void imu_callback(inv_imu_sensor_event_t *event)
|
void imu_callback(inv_imu_sensor_event_t *event)
|
||||||
{
|
{
|
||||||
@@ -281,18 +280,18 @@ void imu_callback(inv_imu_sensor_event_t *event)
|
|||||||
static uint64_t last_fifo_timestamp = 0;
|
static uint64_t last_fifo_timestamp = 0;
|
||||||
static uint32_t rollover_num = 0;
|
static uint32_t rollover_num = 0;
|
||||||
|
|
||||||
/* FIFO timestamp rollover handling (16-bit -> 64-bit extension) */
|
/* FIFO 타임스탬프 롤오버 처리 (16비트 → 64비트 확장) */
|
||||||
if (last_fifo_timestamp > event->timestamp_fsync)
|
if (last_fifo_timestamp > event->timestamp_fsync)
|
||||||
rollover_num++;
|
rollover_num++;
|
||||||
last_fifo_timestamp = event->timestamp_fsync;
|
last_fifo_timestamp = event->timestamp_fsync;
|
||||||
|
|
||||||
/* Convert timestamp to microseconds (apply Q24 resolution) */
|
/* 타임스탬프를 마이크로초 단위로 변환 (Q24 해상도 적용) */
|
||||||
timestamp = event->timestamp_fsync + rollover_num * UINT16_MAX;
|
timestamp = event->timestamp_fsync + rollover_num * UINT16_MAX;
|
||||||
timestamp *= inv_imu_get_fifo_timestamp_resolution_us_q24(&icm_driver);
|
timestamp *= inv_imu_get_fifo_timestamp_resolution_us_q24(&icm_driver);
|
||||||
timestamp /= (1UL << 24);
|
timestamp /= (1UL << 24);
|
||||||
|
|
||||||
if (icm_driver.fifo_highres_enabled) {
|
if (icm_driver.fifo_highres_enabled) {
|
||||||
/* High-res mode: left-shift 16-bit data by 4 + add lower 4 bits -> 20-bit */
|
/* 고해상도 모드: 16비트 데이터를 4비트 좌측 시프트 + 하위 4비트 추가 → 20비트 */
|
||||||
accel[0] = (((int32_t)event->accel[0] << 4)) | event->accel_high_res[0];
|
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[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];
|
accel[2] = (((int32_t)event->accel[2] << 4)) | event->accel_high_res[2];
|
||||||
@@ -302,7 +301,7 @@ void imu_callback(inv_imu_sensor_event_t *event)
|
|||||||
gyro[2] = (((int32_t)event->gyro[2] << 4)) | event->gyro_high_res[2];
|
gyro[2] = (((int32_t)event->gyro[2] << 4)) | event->gyro_high_res[2];
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* Standard resolution: use 16-bit data as-is */
|
/* 표준 해상도: 16비트 데이터 그대로 사용 */
|
||||||
accel[0] = event->accel[0];
|
accel[0] = event->accel[0];
|
||||||
accel[1] = event->accel[1];
|
accel[1] = event->accel[1];
|
||||||
accel[2] = event->accel[2];
|
accel[2] = event->accel[2];
|
||||||
@@ -313,7 +312,7 @@ void imu_callback(inv_imu_sensor_event_t *event)
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
||||||
/* Direct register read mode: extract 16-bit raw data */
|
/* 레지스터 직접 읽기 모드: 16비트 원시 데이터 추출 */
|
||||||
accel[0] = event->accel[0];
|
accel[0] = event->accel[0];
|
||||||
accel[1] = event->accel[1];
|
accel[1] = event->accel[1];
|
||||||
accel[2] = event->accel[2];
|
accel[2] = event->accel[2];
|
||||||
@@ -322,20 +321,20 @@ void imu_callback(inv_imu_sensor_event_t *event)
|
|||||||
gyro[1] = event->gyro[1];
|
gyro[1] = event->gyro[1];
|
||||||
gyro[2] = event->gyro[2];
|
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_TEMPERATURE);
|
||||||
event->sensor_mask |= (1 << INV_SENSOR_ACCEL);
|
event->sensor_mask |= (1 << INV_SENSOR_ACCEL);
|
||||||
event->sensor_mask |= (1 << INV_SENSOR_GYRO);
|
event->sensor_mask |= (1 << INV_SENSOR_GYRO);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Apply mounting matrix — correct sensor physical orientation to software coordinates */
|
/* 마운팅 매트릭스 적용 — 센서의 물리적 장착 방향을 소프트웨어 좌표계로 보정 */
|
||||||
apply_mounting_matrix(icm_mounting_matrix, accel);
|
apply_mounting_matrix(icm_mounting_matrix, accel);
|
||||||
apply_mounting_matrix(icm_mounting_matrix, gyro);
|
apply_mounting_matrix(icm_mounting_matrix, gyro);
|
||||||
|
|
||||||
#if SCALED_DATA_G_DPS
|
#if SCALED_DATA_G_DPS
|
||||||
/*
|
/*
|
||||||
* Convert raw data to physical units (g, dps)
|
* 원시 데이터를 물리 단위(g, dps)로 변환
|
||||||
* Formula: physical_value = raw_value * FSR / INT16_MAX
|
* 변환 공식: 물리값 = 원시값 * FSR / INT16_MAX
|
||||||
*/
|
*/
|
||||||
get_accel_and_gyr_fsr(&accel_fsr_g, &gyro_fsr_dps);
|
get_accel_and_gyr_fsr(&accel_fsr_g, &gyro_fsr_dps);
|
||||||
accel_g[0] = (float)(accel[0] * accel_fsr_g) / INT16_MAX;
|
accel_g[0] = (float)(accel[0] * accel_fsr_g) / INT16_MAX;
|
||||||
@@ -345,14 +344,14 @@ void imu_callback(inv_imu_sensor_event_t *event)
|
|||||||
gyro_dps[1] = (float)(gyro[1] * gyro_fsr_dps) / INT16_MAX;
|
gyro_dps[1] = (float)(gyro[1] * gyro_fsr_dps) / INT16_MAX;
|
||||||
gyro_dps[2] = (float)(gyro[2] * gyro_fsr_dps) / INT16_MAX;
|
gyro_dps[2] = (float)(gyro[2] * gyro_fsr_dps) / INT16_MAX;
|
||||||
|
|
||||||
/* Temperature conversion: high-res/register mode uses /128, FIFO standard mode uses /2 */
|
/* 온도 변환: 고해상도/레지스터 모드는 /128, FIFO 표준 모드는 /2 */
|
||||||
if (USE_HIGH_RES_MODE || !USE_FIFO)
|
if (USE_HIGH_RES_MODE || !USE_FIFO)
|
||||||
temp_degc = 25 + ((float)event->temperature / 128);
|
temp_degc = 25 + ((float)event->temperature / 128);
|
||||||
else
|
else
|
||||||
temp_degc = 25 + ((float)event->temperature / 2);
|
temp_degc = 25 + ((float)event->temperature / 2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Output scaled data via UART
|
* 스케일링된 데이터를 UART로 출력
|
||||||
*/
|
*/
|
||||||
if (event->sensor_mask & (1 << INV_SENSOR_ACCEL) && event->sensor_mask & (1 << INV_SENSOR_GYRO))
|
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",
|
DBG_PRINTF("%u: %.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f\r\n",
|
||||||
@@ -363,13 +362,13 @@ void imu_callback(inv_imu_sensor_event_t *event)
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Raw data output — branches by command source (info4/UART/BLE)
|
* 원시 데이터 출력 — 명령 소스(info4/UART/BLE)에 따라 분기
|
||||||
*/
|
*/
|
||||||
if (event->sensor_mask & (1 << INV_SENSOR_ACCEL) && event->sensor_mask & (1 << INV_SENSOR_GYRO) || motion_raw_data_enabled)
|
if (event->sensor_mask & (1 << INV_SENSOR_ACCEL) && event->sensor_mask & (1 << INV_SENSOR_GYRO) || motion_raw_data_enabled)
|
||||||
{
|
{
|
||||||
motion_raw_data_enabled = false;
|
motion_raw_data_enabled = false;
|
||||||
|
|
||||||
/* info4 mode: store data in global array info_imu[6], polled by external modules */
|
/* info4 모드: 전역 배열 info_imu[6]에 데이터 저장, 외부 모듈에서 이 배열을 폴링하여 데이터 사용 */
|
||||||
if (info4 == true)
|
if (info4 == true)
|
||||||
{
|
{
|
||||||
info_imu[0] = (uint16_t)accel[0];
|
info_imu[0] = (uint16_t)accel[0];
|
||||||
@@ -380,16 +379,16 @@ void imu_callback(inv_imu_sensor_event_t *event)
|
|||||||
info_imu[5] = (uint16_t)gyro[2];
|
info_imu[5] = (uint16_t)gyro[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* UART mode: output 6-axis data in text format with "Tp" prefix */
|
/* UART 모드: "Tp" 접두사로 6축 데이터를 텍스트 형식으로 출력 */
|
||||||
else if(cmd_type_t == CMD_UART) {
|
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]);
|
//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 mode: send 6-axis data as binary packet via BLE
|
* BLE 모드: 6축 데이터를 바이너리 패킷으로 BLE 전송
|
||||||
* ssp_data[0..2] = accel XYZ, ssp_data[3..5] = gyro XYZ
|
* ssp_data[0~2] = 가속도 XYZ, ssp_data[3~5] = 자이로 XYZ
|
||||||
* format_data() packs "rsp:" tag + 12-byte data
|
* format_data()로 "rsp:" 태그 + 12바이트 데이터를 패킷화
|
||||||
* dr_binary_tx_safe() sends 8 bytes via BLE
|
* dr_binary_tx_safe()로 8바이트 BLE 전송
|
||||||
*/
|
*/
|
||||||
else if(cmd_type_t == CMD_BLE) {
|
else if(cmd_type_t == CMD_BLE) {
|
||||||
ssp_data[0] = (uint16_t)accel[0];
|
ssp_data[0] = (uint16_t)accel[0];
|
||||||
@@ -420,13 +419,13 @@ void imu_callback(inv_imu_sensor_event_t *event)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* apply_mounting_matrix()
|
* apply_mounting_matrix()
|
||||||
* Applies a Q30 fixed-point rotation matrix to a 3-axis vector.
|
* Q30 고정소수점 회전 매트릭스를 3축 벡터에 적용한다.
|
||||||
*
|
*
|
||||||
* Calculation:
|
* 계산 방식:
|
||||||
* result[i] = matrix[i*3+0]*raw[0] + matrix[i*3+1]*raw[1] + matrix[i*3+2]*raw[2]
|
* result[i] = matrix[i*3+0]*raw[0] + matrix[i*3+1]*raw[1] + matrix[i*3+2]*raw[2]
|
||||||
* Right-shift result by 30 bits for Q30 -> integer conversion.
|
* 결과를 30비트 우측 시프트하여 Q30 → 정수 변환
|
||||||
*
|
*
|
||||||
* Ensures a consistent coordinate system regardless of physical sensor orientation.
|
* 이를 통해 센서의 물리적 장착 방향에 관계없이 일관된 좌표계를 사용할 수 있다.
|
||||||
*/
|
*/
|
||||||
static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3])
|
static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3])
|
||||||
{
|
{
|
||||||
@@ -438,7 +437,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+1] * raw[1]);
|
||||||
data_q30[i] += ((int64_t)matrix[3*i+2] * raw[2]);
|
data_q30[i] += ((int64_t)matrix[3*i+2] * raw[2]);
|
||||||
}
|
}
|
||||||
/* Q30 -> integer conversion: right-shift by 30 bits */
|
/* Q30 → 정수 변환: 30비트 우측 시프트 */
|
||||||
raw[0] = (int32_t)(data_q30[0]>>30);
|
raw[0] = (int32_t)(data_q30[0]>>30);
|
||||||
raw[1] = (int32_t)(data_q30[1]>>30);
|
raw[1] = (int32_t)(data_q30[1]>>30);
|
||||||
raw[2] = (int32_t)(data_q30[2]>>30);
|
raw[2] = (int32_t)(data_q30[2]>>30);
|
||||||
@@ -447,22 +446,22 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3])
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* imu_read_direct()
|
* imu_read_direct()
|
||||||
* Reads IMU registers directly via I2C, bypassing the driver API.
|
* 드라이버 API를 우회하여 직접 I2C 레지스터를 읽는 함수.
|
||||||
* Reads data immediately without waiting for DRDY interrupt.
|
* DRDY(데이터 준비) 인터럽트를 기다리지 않고 즉시 데이터를 읽는다.
|
||||||
*
|
*
|
||||||
* Flow:
|
* 처리 흐름:
|
||||||
* 1) Check TWI initialization (first call only)
|
* 1) TWI 초기화 확인 (최초 1회만)
|
||||||
* 2) Gyro config: +/-2000dps, 100Hz ODR (GYRO_CONFIG0 = 0x09)
|
* 2) 자이로 설정: ±2000dps, 100Hz ODR (GYRO_CONFIG0 = 0x09)
|
||||||
* 3) Accel config: +/-4g, 100Hz ODR (ACCEL_CONFIG0 = 0x29)
|
* 3) 가속도 설정: ±4g, 100Hz ODR (ACCEL_CONFIG0 = 0x29)
|
||||||
* 4) Power ON: accel+gyro low-noise mode (PWR_MGMT0 = 0x0F)
|
* 4) 전원 ON: 가속도+자이로 저잡음 모드 (PWR_MGMT0 = 0x0F)
|
||||||
* 5) Wait 80ms (gyro startup: min 45ms + margin)
|
* 5) 80ms 대기 (자이로 스타트업: 최소 45ms + 여유)
|
||||||
* 6) Read 12 consecutive bytes from ACCEL_DATA_X1 (0x0B) (accel 6 + gyro 6)
|
* 6) ACCEL_DATA_X1(0x0B)부터 12바이트 연속 읽기 (accel 6 + gyro 6)
|
||||||
* 7) Big-endian -> int16_t conversion
|
* 7) 빅엔디안 → int16_t 변환
|
||||||
* 8) Apply mounting matrix
|
* 8) 마운팅 매트릭스 적용
|
||||||
* 9) Send via BLE with "rsp:" tag
|
* 9) "rsp:" 태그로 BLE 전송
|
||||||
* 10) Switch IMU to sleep mode (power saving)
|
* 10) IMU 슬립 모드로 전환 (전력 절감)
|
||||||
*
|
*
|
||||||
* Returns: 0=success, -1=TX failure, -2=RX failure
|
* 반환값: 0=성공, -1=TX 실패, -2=RX 실패
|
||||||
*/
|
*/
|
||||||
/* Raw I2C read from ICM42670P — bypasses driver API entirely */
|
/* Raw I2C read from ICM42670P — bypasses driver API entirely */
|
||||||
#include "system_interface.h"
|
#include "system_interface.h"
|
||||||
@@ -471,51 +470,51 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3])
|
|||||||
extern const nrfx_twi_t m_twi_icm42670;
|
extern const nrfx_twi_t m_twi_icm42670;
|
||||||
|
|
||||||
#define IMU_I2C_ADDR 0x68
|
#define IMU_I2C_ADDR 0x68
|
||||||
#define REG_ACCEL_X1 0x0B /* ACCEL_DATA_X1 — accel X-axis upper byte register */
|
#define REG_ACCEL_X1 0x0B /* ACCEL_DATA_X1 — 가속도 X축 상위 바이트 레지스터 */
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------
|
||||||
* Direct IMU register read — raw I2C, no DRDY, sends rsp: via BLE
|
* Direct IMU register read — raw I2C, no DRDY, sends rsp: via BLE
|
||||||
* Direct I2C register read (no interrupt, no IMU driver API)
|
* 직접 I2C로 레지스터 읽는 방식 (인터럽트 X, IMU 드라이버 API X)
|
||||||
* -------------------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------------------- */
|
||||||
int imu_read_direct(void)
|
int imu_read_direct(void)
|
||||||
{
|
{
|
||||||
uint8_t raw[12]; /* accel 6 bytes + gyro 6 bytes */
|
uint8_t raw[12]; /* 가속도 6바이트 + 자이로 6바이트 */
|
||||||
int32_t accel[3], gyro[3];
|
int32_t accel[3], gyro[3];
|
||||||
uint8_t reg;
|
uint8_t reg;
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
|
|
||||||
static bool twi_ready = false;
|
static bool twi_ready = false;
|
||||||
|
|
||||||
/* TWI (I2C) init — performed only once (re-init ensures clean state) */
|
/* TWI(I2C) 초기화 — 최초 1회만 수행 (재초기화로 클린 상태 보장) */
|
||||||
if (!twi_ready) {
|
if (!twi_ready) {
|
||||||
inv_i2c_master_uninitialize();
|
inv_i2c_master_uninitialize();
|
||||||
inv_i2c_master_initialize();
|
inv_i2c_master_initialize();
|
||||||
twi_ready = true;
|
twi_ready = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Gyro config: GYRO_CONFIG0(0x20) = 0x09 -> +/-2000dps FSR, 100Hz ODR */
|
/* 자이로 설정: GYRO_CONFIG0(0x20) = 0x09 → ±2000dps FSR, 100Hz ODR */
|
||||||
{
|
{
|
||||||
uint8_t gyro_cfg[2] = { 0x20, 0x09 };
|
uint8_t gyro_cfg[2] = { 0x20, 0x09 };
|
||||||
icm42670_twi_tx(IMU_I2C_ADDR, gyro_cfg, 2, false);
|
icm42670_twi_tx(IMU_I2C_ADDR, gyro_cfg, 2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Accel config: ACCEL_CONFIG0(0x21) = 0x29 -> +/-4g FSR, 100Hz ODR */
|
/* 가속도 설정: ACCEL_CONFIG0(0x21) = 0x29 → ±4g FSR, 100Hz ODR */
|
||||||
{
|
{
|
||||||
uint8_t accel_cfg[2] = { 0x21, 0x29 };
|
uint8_t accel_cfg[2] = { 0x21, 0x29 };
|
||||||
icm42670_twi_tx(IMU_I2C_ADDR, accel_cfg, 2, false);
|
icm42670_twi_tx(IMU_I2C_ADDR, accel_cfg, 2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Power ON: PWR_MGMT0(0x1F) = 0x0F -> accel (low-noise) + gyro (low-noise) enabled */
|
/* 전원 ON: PWR_MGMT0(0x1F) = 0x0F → 가속도(저잡음) + 자이로(저잡음) 활성화 */
|
||||||
{
|
{
|
||||||
uint8_t pwr_cmd[2] = { 0x1F, 0x0F }; /* reg=0x1F, val=0x0F */
|
uint8_t pwr_cmd[2] = { 0x1F, 0x0F }; /* reg=0x1F, val=0x0F */
|
||||||
icm42670_twi_tx(IMU_I2C_ADDR, pwr_cmd, 2, false);
|
icm42670_twi_tx(IMU_I2C_ADDR, pwr_cmd, 2, false);
|
||||||
//nrf_delay_ms(80); /* Gyro startup: min 45ms + safety margin */
|
//nrf_delay_ms(80); /* 자이로 스타트업: 최소 45ms + 안전 마진 */
|
||||||
dr_sd_delay_ms(80);
|
dr_sd_delay_ms(80);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read 12 consecutive bytes from ACCEL_DATA_X1 (0x0B~0x16) */
|
/* ACCEL_DATA_X1(0x0B)부터 12바이트 연속 읽기 (0x0B~0x16) */
|
||||||
reg = REG_ACCEL_X1;
|
reg = REG_ACCEL_X1;
|
||||||
ret = icm42670_twi_tx(IMU_I2C_ADDR, ®, 1, true); /* Send register address (no STOP) */
|
ret = icm42670_twi_tx(IMU_I2C_ADDR, ®, 1, true); /* 레지스터 주소 전송 (STOP 없음) */
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
@@ -523,7 +522,7 @@ int imu_read_direct(void)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = icm42670_twi_rx(IMU_I2C_ADDR, raw, 12); /* Receive 12 bytes of data */
|
ret = icm42670_twi_rx(IMU_I2C_ADDR, raw, 12); /* 12바이트 데이터 수신 */
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
@@ -532,9 +531,9 @@ int imu_read_direct(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert big-endian register layout to int16_t
|
* 빅엔디안 레지스터 레이아웃을 int16_t로 변환
|
||||||
* raw[0..5] = accel X,Y,Z (2 bytes each, MSB first)
|
* raw[0..5] = 가속도 X,Y,Z (각 2바이트, MSB first)
|
||||||
* raw[6..11] = gyro X,Y,Z (2 bytes each, MSB first)
|
* raw[6..11] = 자이로 X,Y,Z (각 2바이트, MSB first)
|
||||||
*/
|
*/
|
||||||
accel[0] = (int16_t)((raw[0] << 8) | raw[1]);
|
accel[0] = (int16_t)((raw[0] << 8) | raw[1]);
|
||||||
accel[1] = (int16_t)((raw[2] << 8) | raw[3]);
|
accel[1] = (int16_t)((raw[2] << 8) | raw[3]);
|
||||||
@@ -543,11 +542,11 @@ int imu_read_direct(void)
|
|||||||
gyro[1] = (int16_t)((raw[8] << 8) | raw[9]);
|
gyro[1] = (int16_t)((raw[8] << 8) | raw[9]);
|
||||||
gyro[2] = (int16_t)((raw[10] << 8) | raw[11]);
|
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, accel);
|
||||||
apply_mounting_matrix(icm_mounting_matrix, gyro);
|
apply_mounting_matrix(icm_mounting_matrix, gyro);
|
||||||
|
|
||||||
/* Pack data */
|
/* 데이터 패킹 */
|
||||||
ssp_data[0] = (uint16_t)accel[0];
|
ssp_data[0] = (uint16_t)accel[0];
|
||||||
ssp_data[1] = (uint16_t)accel[1];
|
ssp_data[1] = (uint16_t)accel[1];
|
||||||
ssp_data[2] = (uint16_t)accel[2];
|
ssp_data[2] = (uint16_t)accel[2];
|
||||||
@@ -557,7 +556,7 @@ int imu_read_direct(void)
|
|||||||
|
|
||||||
if (info4 == true)
|
if (info4 == true)
|
||||||
{
|
{
|
||||||
/* info4 mode: store in global array (sent as rbb: packet by mbb?) */
|
/* info4 모드: 전역 배열에 저장 (mbb?에서 rbb: 패킷으로 일괄 전송) */
|
||||||
info_imu[0] = ssp_data[0];
|
info_imu[0] = ssp_data[0];
|
||||||
info_imu[1] = ssp_data[1];
|
info_imu[1] = ssp_data[1];
|
||||||
info_imu[2] = ssp_data[2];
|
info_imu[2] = ssp_data[2];
|
||||||
@@ -567,12 +566,13 @@ int imu_read_direct(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Normal mode: send immediately via BLE with "rsp:" tag */
|
/* 일반 모드: "rsp:" 태그로 BLE 즉시 전송 */
|
||||||
format_data(imu_bin_buffer, "rsp:", ssp_data, 12);
|
format_data(imu_bin_buffer, "rsp:", ssp_data, 12);
|
||||||
dr_binary_tx_safe(imu_bin_buffer, 8);
|
dr_binary_tx_safe(imu_bin_buffer, 8);
|
||||||
|
DBG_PRINTF("0");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* IMU sleep mode: PWR_MGMT0 = 0x00 -> accel/gyro both OFF (power saving) */
|
/* IMU 슬립 모드: PWR_MGMT0 = 0x00 → 가속도/자이로 모두 OFF (전력 절감) */
|
||||||
{
|
{
|
||||||
uint8_t pwr_off[2] = { 0x1F, 0x00 }; /* reg=PWR_MGMT0, val=0x00 */
|
uint8_t pwr_off[2] = { 0x1F, 0x00 }; /* reg=PWR_MGMT0, val=0x00 */
|
||||||
icm42670_twi_tx(IMU_I2C_ADDR, pwr_off, 2, false);
|
icm42670_twi_tx(IMU_I2C_ADDR, pwr_off, 2, false);
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file app_raw.h
|
||||||
|
* @author CandyPops Co.
|
||||||
|
* @version V1.0.0
|
||||||
|
* @date 2022-09-05
|
||||||
|
* @brief
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* [헤더 개요] ICM42670P IMU 드라이버 상위 레이어 선언
|
||||||
|
*
|
||||||
|
* IMU 센서의 초기화, 설정, 데이터 읽기를 위한 함수 프로토타입과
|
||||||
|
* 동작 모드 설정 매크로를 정의한다.
|
||||||
|
*
|
||||||
|
* 주요 설정 매크로:
|
||||||
|
* 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:레지스터 직접 읽기
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef _APP_RAW_H_
|
||||||
|
#define _APP_RAW_H_
|
||||||
|
#include "sdk_config.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "inv_imu_transport.h"
|
||||||
|
#include "inv_imu_defs.h"
|
||||||
|
#include "inv_imu_driver.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*** 설정 매크로 ***/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCU와 IMU 간 통신 인터페이스 선택
|
||||||
|
* UI_I2C: I2C 통신 사용 (기본)
|
||||||
|
*/
|
||||||
|
#define SERIF_TYPE UI_I2C
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 전원 모드 설정
|
||||||
|
* 1: 저잡음 모드 — 800Hz ODR, 높은 정밀도, 높은 전력 소모
|
||||||
|
* 0: 저전력 모드 — 100Hz ODR, 낮은 전력 소모
|
||||||
|
* 주의: 12.5Hz 미만 ODR에서는 저잡음 모드 사용 불가
|
||||||
|
*/
|
||||||
|
#define USE_LOW_NOISE_MODE 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIFO 해상도 모드 선택
|
||||||
|
* 0: 저해상도 — 16비트 데이터 (기본)
|
||||||
|
* 1: 고해상도 — 20비트 데이터 (FSR이 16g/2000dps로 강제 고정됨)
|
||||||
|
*/
|
||||||
|
#define USE_HIGH_RES_MODE 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 데이터 읽기 방식 선택
|
||||||
|
* 0: 레지스터 직접 읽기 (현재 사용 중)
|
||||||
|
* 1: FIFO에서 읽기
|
||||||
|
*/
|
||||||
|
#define USE_FIFO 0
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief IMU 디바이스를 리셋하고 초기화한다. WHOAMI 확인 포함.
|
||||||
|
* 다른 IMU 접근 함수 호출 전에 반드시 성공적으로 실행되어야 한다.
|
||||||
|
*
|
||||||
|
* \return 0=성공, 음수=에러
|
||||||
|
*/
|
||||||
|
int setup_imu_device(struct inv_imu_serif *icm_serif);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief 자이로 및 가속도계 출력을 위한 디바이스 설정을 수행한다.
|
||||||
|
* FSR, ODR, 전원 모드, FIFO 설정 등을 적용한다.
|
||||||
|
* \return 0=성공, 음수=에러
|
||||||
|
*/
|
||||||
|
int configure_imu_device(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief FIFO 또는 레지스터에서 IMU 데이터를 추출한다.
|
||||||
|
* 내부적으로 imu_callback()이 호출되어 데이터를 처리한다.
|
||||||
|
* \return 0=성공, 음수=에러
|
||||||
|
*/
|
||||||
|
int get_imu_data(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief 센서 데이터 수신 콜백. 마운팅 매트릭스 적용 후
|
||||||
|
* info4/BLE/UART 모드에 따라 데이터를 출력한다.
|
||||||
|
* \param[in] event 하나의 센서 데이터 패킷을 담은 구조체
|
||||||
|
*/
|
||||||
|
void imu_callback(inv_imu_sensor_event_t *event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief 드라이버 API를 우회한 직접 I2C 레지스터 읽기.
|
||||||
|
* DRDY 인터럽트 없이 즉시 센서 데이터를 읽어 BLE로 전송한다.
|
||||||
|
* 읽기 후 IMU를 슬립 모드로 전환하여 전력을 절감한다.
|
||||||
|
* \return 0=성공, 음수=에러
|
||||||
|
*/
|
||||||
|
int imu_read_direct(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !_APP_RAW_H_ */
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file app_raw_main.c
|
||||||
|
* @author CandyPops Co.
|
||||||
|
* @version V1.0.0
|
||||||
|
* @date 2022-09-05
|
||||||
|
* @brief
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* 2026.03.26 jhChun
|
||||||
|
* 현재 이 파일은 실제 런타임에 실행되지 않고 있음
|
||||||
|
* 인터럽트 방식 대신 app_raw.c imu_read_direct()에서 직접 레지스터 읽는 방식 사용 중
|
||||||
|
* 추후 필요 여부에 따라 정리 예정
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* [모듈 개요] ICM42670P 메인 초기화 및 폴링 루프
|
||||||
|
*
|
||||||
|
* ICM42670P IMU 센서의 전체 초기화 시퀀스와 메인 루프를 담당한다.
|
||||||
|
*
|
||||||
|
* 초기화 흐름 (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 인터럽트 설정
|
||||||
|
*
|
||||||
|
* 메인 루프 (icm42670_main):
|
||||||
|
* - INT1 인터럽트 발생 시 irq_from_device 플래그가 세팅됨
|
||||||
|
* - 메인 루프에서 플래그를 확인하고, 세팅되어 있으면 센서 데이터를 읽음
|
||||||
|
* - 인터럽트는 하강 에지(HITOLO)에서 발생 (INT1 핀 풀업 설정)
|
||||||
|
*
|
||||||
|
* 보조 함수:
|
||||||
|
* - inv_imu_sleep_us() - nrf_delay_us 래퍼 (IMU 드라이버가 사용)
|
||||||
|
* - inv_imu_get_time_us() - RTC1 카운터로 타임스탬프 제공
|
||||||
|
******************************************************************************/
|
||||||
|
#include "sdk_config.h"
|
||||||
|
#include "app_raw.h"
|
||||||
|
#include "app_raw_main.h"
|
||||||
|
#include "RingBuffer.h"
|
||||||
|
#include "inv_imu_driver.h"
|
||||||
|
|
||||||
|
#include "system_interface.h"
|
||||||
|
|
||||||
|
/* std */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "nrf.h"
|
||||||
|
#include "app_error.h"
|
||||||
|
#include "boards.h"
|
||||||
|
#include "nrfx_gpiote.h"
|
||||||
|
#include "nrf_delay.h"
|
||||||
|
|
||||||
|
#include "app_util_platform.h"
|
||||||
|
#include "main.h" /* 2026-03-17: cmd_parse.h 삭제 → main.h */
|
||||||
|
#include "i2c_manager.h"
|
||||||
|
/* --------------------------------------------------------------------------------------
|
||||||
|
* Global variables
|
||||||
|
* -------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------
|
||||||
|
* Static variables
|
||||||
|
* -------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMU 인터럽트 플래그
|
||||||
|
* INT1 핀의 하강 에지 인터럽트 발생 시 1로 세팅된다.
|
||||||
|
* 메인 루프에서 이 플래그를 확인 후 데이터를 읽고 0으로 클리어한다.
|
||||||
|
* volatile: ISR에서 변경되므로 컴파일러 최적화 방지
|
||||||
|
*/
|
||||||
|
static volatile int irq_from_device;
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------
|
||||||
|
* Forward declaration
|
||||||
|
* -------------------------------------------------------------------------------------- */
|
||||||
|
static int setup_mcu(struct inv_imu_serif *icm_serif);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Sensor general interrupt handler, calls specific handlers.
|
||||||
|
*
|
||||||
|
* This function is called when an external interrupt is triggered by the sensor,
|
||||||
|
* checks interrupt registers of InvenSense Sensor to determine the source and type of interrupt
|
||||||
|
* and calls the specific interrupt handler accordingly.
|
||||||
|
*
|
||||||
|
* @param[in] NULL
|
||||||
|
*
|
||||||
|
* @param[out] NULL
|
||||||
|
*
|
||||||
|
* @return NULL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* inv_gpio_sensor_interrupt_handler()
|
||||||
|
* INT1 핀 인터럽트 핸들러 (ISR).
|
||||||
|
* 센서가 새 데이터를 준비했을 때 호출되며, 플래그만 세팅하고 즉시 반환한다.
|
||||||
|
* 실제 데이터 처리는 메인 루프(icm42670_main)에서 수행한다.
|
||||||
|
*/
|
||||||
|
static void inv_gpio_sensor_interrupt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
|
||||||
|
{
|
||||||
|
irq_from_device = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* inv_gpio_sensor_irq_init()
|
||||||
|
* INT1(P1.13) GPIO 인터럽트를 초기화한다.
|
||||||
|
*
|
||||||
|
* 설정:
|
||||||
|
* - 트리거: 하강 에지 (HITOLO) — 센서가 INT를 Low로 끌어내릴 때
|
||||||
|
* - 풀업 저항: 내부 풀업 활성화
|
||||||
|
* - 핸들러: inv_gpio_sensor_interrupt_handler
|
||||||
|
* - GPIOTE 모듈이 미초기화 상태이면 먼저 초기화
|
||||||
|
*/
|
||||||
|
void inv_gpio_sensor_irq_init(void)
|
||||||
|
{
|
||||||
|
ret_code_t err_code;
|
||||||
|
|
||||||
|
/* GPIOTE 모듈 초기화 (이미 초기화되어 있으면 건너뜀) */
|
||||||
|
if (!nrfx_gpiote_is_init())
|
||||||
|
{
|
||||||
|
err_code = nrfx_gpiote_init();
|
||||||
|
APP_ERROR_CHECK(err_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 하강 에지 인터럽트 설정: High→Low 전환 시 트리거, 내부 풀업 사용 */
|
||||||
|
nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
|
||||||
|
in_config.pull = NRF_GPIO_PIN_PULLUP;
|
||||||
|
|
||||||
|
/* INT1 핀에 인터럽트 핸들러 등록 */
|
||||||
|
err_code = nrfx_gpiote_in_init(ICM42670_INT1_PIN, &in_config, inv_gpio_sensor_interrupt_handler);
|
||||||
|
APP_ERROR_CHECK(err_code);
|
||||||
|
|
||||||
|
/* 인터럽트 이벤트 활성화 */
|
||||||
|
nrfx_gpiote_in_event_enable(ICM42670_INT1_PIN, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* inv_gpio_sensor_irq_uninit()
|
||||||
|
* INT1 GPIO 인터럽트를 비활성화하고 해제한다.
|
||||||
|
* 센서 비활성화 시 또는 재초기화 전에 호출된다.
|
||||||
|
*/
|
||||||
|
void inv_gpio_sensor_irq_uninit(void)
|
||||||
|
{
|
||||||
|
/* 인터럽트 이벤트 비활성화 */
|
||||||
|
nrfx_gpiote_in_event_disable(ICM42670_INT1_PIN);
|
||||||
|
|
||||||
|
/* INT1 핀 인터럽트 설정 해제 */
|
||||||
|
nrfx_gpiote_in_uninit(ICM42670_INT1_PIN);
|
||||||
|
|
||||||
|
/* GPIOTE 모듈 해제 (초기화된 경우에만) */
|
||||||
|
if (nrfx_gpiote_is_init())
|
||||||
|
{
|
||||||
|
nrfx_gpiote_uninit();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------
|
||||||
|
* Main
|
||||||
|
* -------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* icm42670_init()
|
||||||
|
* ICM42670P 전체 초기화 시퀀스를 수행한다.
|
||||||
|
*
|
||||||
|
* 초기화 순서:
|
||||||
|
* 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 인터럽트 활성화 (데이터 준비 알림)
|
||||||
|
*
|
||||||
|
* 반환값: 0=성공, -1=초기화 실패
|
||||||
|
*/
|
||||||
|
int icm42670_init(void)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
struct inv_imu_serif icm_serif;
|
||||||
|
|
||||||
|
rc |= setup_mcu(&icm_serif);
|
||||||
|
rc |= setup_imu_device(&icm_serif);
|
||||||
|
rc |= configure_imu_device();
|
||||||
|
|
||||||
|
if(rc != 0){
|
||||||
|
printf("!!!error during initialization\r\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 초기화 성공 후 INT1 인터럽트 활성화 — 이후 데이터 준비 시 ISR이 호출됨 */
|
||||||
|
inv_gpio_sensor_irq_init();
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* icm42670_main()
|
||||||
|
* ICM42670P 메인 폴링 루프.
|
||||||
|
* 메인 애플리케이션 루프에서 주기적으로 호출되어야 한다.
|
||||||
|
*
|
||||||
|
* 동작:
|
||||||
|
* 1) I2C 하드웨어가 초기화되었는지 확인 (hw_i2c_init_once)
|
||||||
|
* 2) irq_from_device 플래그 확인 (ISR에서 세팅됨)
|
||||||
|
* 3) 플래그가 세팅되어 있으면 센서 데이터 읽기 (get_imu_data)
|
||||||
|
* 4) 데이터 읽기 완료 후 플래그 클리어
|
||||||
|
*
|
||||||
|
* 참고: 인터럽트 기반 폴링 방식으로, ISR에서는 플래그만 세팅하고 실제 I2C 통신은 메인 컨텍스트에서 수행한다.
|
||||||
|
*/
|
||||||
|
void icm42670_main(void)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
hw_i2c_init_once();
|
||||||
|
/* 인터럽트 발생 여부 확인 후 데이터 읽기 */
|
||||||
|
|
||||||
|
if (irq_from_device) {
|
||||||
|
rc = get_imu_data();
|
||||||
|
|
||||||
|
if(rc < 0) {
|
||||||
|
printf("error while getting data\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 플래그 클리어 — 다음 인터럽트까지 대기 */
|
||||||
|
irq_from_device = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------
|
||||||
|
* Functions definitions
|
||||||
|
* -------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* setup_mcu()
|
||||||
|
* MCU 측 시리얼 인터페이스를 설정한다.
|
||||||
|
*
|
||||||
|
* inv_imu_serif 구조체에 다음을 등록:
|
||||||
|
* - read_reg / write_reg : I2C 읽기/쓰기 콜백 함수 (system_interface.c에서 구현)
|
||||||
|
* - max_read / max_write : 최대 전송 크기 (32KB)
|
||||||
|
* - serif_type : 통신 타입 (UI_I2C)
|
||||||
|
*
|
||||||
|
* 설정 후 inv_io_hal_init()을 호출하여 실제 TWI 하드웨어를 초기화한다.
|
||||||
|
*/
|
||||||
|
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에서 정의) */
|
||||||
|
|
||||||
|
/* TWI 하드웨어 초기화 */
|
||||||
|
rc |= inv_io_hal_init(icm_serif);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------
|
||||||
|
* Extern functions definition
|
||||||
|
* -------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* inv_imu_sleep_us()
|
||||||
|
* IMU 드라이버가 사용하는 마이크로초 단위 슬립 함수.
|
||||||
|
* nrf_delay_us()를 래핑하여 플랫폼 독립적 인터페이스를 제공한다.
|
||||||
|
* 예: 자이로 스타트업 대기(GYR_STARTUP_TIME_US) 시 사용
|
||||||
|
*/
|
||||||
|
void inv_imu_sleep_us(uint32_t us)
|
||||||
|
{
|
||||||
|
nrf_delay_us(us);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* inv_imu_get_time_us()
|
||||||
|
* IMU 드라이버가 사용하는 타임스탬프 함수.
|
||||||
|
* nRF52840의 RTC1 카운터 값을 반환한다.
|
||||||
|
*
|
||||||
|
* 주의: RTC1은 32.768kHz로 동작하므로, 반환값의 단위는 엄밀히
|
||||||
|
* 마이크로초가 아닌 RTC 틱(약 30.5us/tick)이다.
|
||||||
|
* 드라이버 내부에서 상대적 시간 비교 용도로 사용된다.
|
||||||
|
*/
|
||||||
|
uint64_t inv_imu_get_time_us(void)
|
||||||
|
{
|
||||||
|
return NRF_RTC1->COUNTER;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file app_raw_main.h
|
||||||
|
* @author CandyPops Co.
|
||||||
|
* @version V1.0.0
|
||||||
|
* @date 2022-09-05
|
||||||
|
* @brief
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* [헤더 개요] ICM42670P 메인 초기화/폴링 루프 선언
|
||||||
|
*
|
||||||
|
* ICM42670P IMU 센서의 전체 초기화 및 메인 루프 함수를 선언한다.
|
||||||
|
* - icm42670_init() : 전체 초기화 (MCU설정 → IMU초기화 → 센서설정 → 인터럽트 활성화)
|
||||||
|
* - icm42670_main() : 메인 폴링 루프 (INT1 인터럽트 확인 → 데이터 읽기)
|
||||||
|
* - icm42670_uninit() : 해제 (프로토타입만 선언, 구현은 별도)
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef _APP_RAW_MAIN_H_
|
||||||
|
#define _APP_RAW_MAIN_H_
|
||||||
|
#include "sdk_config.h"
|
||||||
|
|
||||||
|
/* ICM42670P 전체 초기화 — MCU I2C 설정 → IMU 드라이버 초기화 → 센서 설정 → 인터럽트 활성화 */
|
||||||
|
int icm42670_init(void);
|
||||||
|
|
||||||
|
/* ICM42670P 메인 폴링 루프 — INT1 인터럽트 플래그 확인 후 센서 데이터 읽기 */
|
||||||
|
void icm42670_main(void);
|
||||||
|
|
||||||
|
/* ICM42670P 해제 (프로토타입 선언) */
|
||||||
|
int icm42670_uninit(void);
|
||||||
|
|
||||||
|
#endif /* !_APP_RAW_MAIN_H_ */
|
||||||
+62
-62
@@ -7,25 +7,25 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* [Module overview] ICM42670P IMU sensor I2C communication interface
|
* [모듈 개요] ICM42670P IMU 센서 I2C 통신 인터페이스
|
||||||
*
|
*
|
||||||
* Low-level interface module for communicating with the ICM42670P IMU sensor
|
* nRF52840의 TWI(I2C) 하드웨어를 사용하여 ICM42670P IMU 센서와 통신하는
|
||||||
* via the nRF52840 TWI (I2C) hardware.
|
* 저수준 인터페이스 모듈이다.
|
||||||
*
|
*
|
||||||
* - I2C slave address: 0x68 (ICM42670P default)
|
* - I2C 슬레이브 주소: 0x68 (ICM42670P 기본 주소)
|
||||||
* - I2C pin config: SCL=P1.14, SDA=P1.15 (defined in system_interface.h)
|
* - I2C 핀 설정: SCL=P1.14, SDA=P1.15 (system_interface.h에서 정의)
|
||||||
* - TWI instance: NRFX_TWI_INSTANCE(0)
|
* - TWI 인스턴스: NRFX_TWI_INSTANCE(0) 사용
|
||||||
* - Bus speed: 100kHz (NRF_TWI_FREQ_100K)
|
* - 통신 속도: 100kHz (NRF_TWI_FREQ_100K)
|
||||||
*
|
*
|
||||||
* Main function flow:
|
* 주요 함수 흐름:
|
||||||
* inv_io_hal_init() -> Initialize I2C or SPI (only I2C implemented)
|
* inv_io_hal_init() → I2C 또는 SPI 초기화 (현재 I2C만 구현)
|
||||||
* inv_io_hal_read_reg() -> Register read (TX address -> RX data)
|
* inv_io_hal_read_reg() → 레지스터 읽기 (TX로 주소 전송 → RX로 데이터 수신)
|
||||||
* inv_io_hal_write_reg() -> Register write (TX address+data at once)
|
* inv_io_hal_write_reg() → 레지스터 쓰기 (주소+데이터를 한번에 TX)
|
||||||
*
|
*
|
||||||
* Error handling: All I2C read/write operations retry once on failure
|
* 에러 처리: 모든 I2C 읽기/쓰기에서 실패 시 1회 재시도 후 에러 출력
|
||||||
*
|
*
|
||||||
* Note: SPI4 code path exists but is not implemented;
|
* 참고: SPI4 인터페이스 코드 경로도 존재하지만 현재 미구현 상태이며,
|
||||||
* only I2C (UI_I2C) is used in production.
|
* 실제 운용에서는 I2C(UI_I2C)만 사용한다.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
/* board driver */
|
/* board driver */
|
||||||
@@ -43,17 +43,17 @@
|
|||||||
#include "system_interface.h"
|
#include "system_interface.h"
|
||||||
#include "nrf_delay.h"
|
#include "nrf_delay.h"
|
||||||
|
|
||||||
/* ICM42670P I2C slave address and max serial write byte count */
|
/* ICM42670P I2C 슬레이브 주소 및 직렬 쓰기 최대 바이트 수 */
|
||||||
#define ICM_I2C_ADDR 0x68
|
#define ICM_I2C_ADDR 0x68
|
||||||
#define INV_MAX_SERIAL_WRITE 16
|
#define INV_MAX_SERIAL_WRITE 16
|
||||||
|
|
||||||
/* TWI (I2C) instance — uses ICM42670_I2C_INSTANCE(0) from system_interface.h */
|
/* TWI(I2C) 인스턴스 생성 — system_interface.h의 ICM42670_I2C_INSTANCE(0)을 사용 */
|
||||||
const nrfx_twi_t m_twi_icm42670 = NRFX_TWI_INSTANCE(ICM42670_I2C_INSTANCE);
|
const nrfx_twi_t m_twi_icm42670 = NRFX_TWI_INSTANCE(ICM42670_I2C_INSTANCE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* inv_i2c_master_uninitialize()
|
* inv_i2c_master_uninitialize()
|
||||||
* Disables the I2C bus and releases the TWI instance.
|
* I2C 버스를 비활성화하고 TWI 인스턴스를 해제한다.
|
||||||
* Called before entering sleep mode or before re-initialization.
|
* 슬립 모드 진입 전 또는 재초기화 전에 호출된다.
|
||||||
*/
|
*/
|
||||||
void inv_i2c_master_uninitialize(void){
|
void inv_i2c_master_uninitialize(void){
|
||||||
nrfx_twi_disable(&m_twi_icm42670);
|
nrfx_twi_disable(&m_twi_icm42670);
|
||||||
@@ -62,11 +62,11 @@ void inv_i2c_master_uninitialize(void){
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* inv_i2c_master_initialize()
|
* inv_i2c_master_initialize()
|
||||||
* Initializes and enables the nRF52840 TWI hardware.
|
* nRF52840 TWI 하드웨어를 초기화하고 활성화한다.
|
||||||
* - SCL: P1.14, SDA: P1.15
|
* - SCL: P1.14, SDA: P1.15
|
||||||
* - Speed: 100kHz
|
* - 속도: 100kHz
|
||||||
* - Interrupt priority: highest (APP_IRQ_PRIORITY_HIGH)
|
* - 인터럽트 우선순위: 최고 (APP_IRQ_PRIORITY_HIGH)
|
||||||
* - No event handler (blocking mode)
|
* - 이벤트 핸들러 없음 (블로킹 모드로 동작)
|
||||||
*/
|
*/
|
||||||
void inv_i2c_master_initialize(void){
|
void inv_i2c_master_initialize(void){
|
||||||
ret_code_t err_code;
|
ret_code_t err_code;
|
||||||
@@ -78,19 +78,19 @@ void inv_i2c_master_initialize(void){
|
|||||||
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
|
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Initialize TWI driver (event handler=NULL -> blocking mode) */
|
/* TWI 드라이버 초기화 (이벤트 핸들러=NULL → 블로킹 모드) */
|
||||||
err_code = nrfx_twi_init(&m_twi_icm42670, &twi_icm42670_config, NULL, NULL);
|
err_code = nrfx_twi_init(&m_twi_icm42670, &twi_icm42670_config, NULL, NULL);
|
||||||
APP_ERROR_CHECK(err_code);
|
APP_ERROR_CHECK(err_code);
|
||||||
|
|
||||||
/* Enable TWI hardware — tx/rx available after this */
|
/* TWI 하드웨어 활성화 — 이후 tx/rx 가능 */
|
||||||
nrfx_twi_enable(&m_twi_icm42670);
|
nrfx_twi_enable(&m_twi_icm42670);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* icm42670_twi_tx()
|
* icm42670_twi_tx()
|
||||||
* I2C transmit wrapper. Calls nrfx_twi_tx to send data.
|
* I2C 전송 래퍼 함수. nrfx_twi_tx를 호출하여 데이터를 송신한다.
|
||||||
* If no_stop=true, STOP condition is omitted (used for Repeated START).
|
* no_stop=true이면 STOP 컨디션을 보내지 않음 (Repeated START를 위해 사용)
|
||||||
*/
|
*/
|
||||||
uint32_t icm42670_twi_tx( uint8_t device_id,
|
uint32_t icm42670_twi_tx( uint8_t device_id,
|
||||||
uint8_t const * p_data,
|
uint8_t const * p_data,
|
||||||
@@ -105,7 +105,7 @@ uint32_t icm42670_twi_tx( uint8_t device_id,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* icm42670_twi_rx()
|
* icm42670_twi_rx()
|
||||||
* I2C receive wrapper. Calls nrfx_twi_rx to receive data.
|
* I2C 수신 래퍼 함수. nrfx_twi_rx를 호출하여 데이터를 수신한다.
|
||||||
*/
|
*/
|
||||||
uint32_t icm42670_twi_rx( uint8_t device_id,
|
uint32_t icm42670_twi_rx( uint8_t device_id,
|
||||||
uint8_t * p_data,
|
uint8_t * p_data,
|
||||||
@@ -119,33 +119,33 @@ uint32_t icm42670_twi_rx( uint8_t device_id,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* inv_i2c_master_read_register()
|
* inv_i2c_master_read_register()
|
||||||
* Reads data from a specific ICM42670P register.
|
* ICM42670P의 특정 레지스터에서 데이터를 읽는다.
|
||||||
*
|
*
|
||||||
* Sequence:
|
* 동작 순서:
|
||||||
* 1) TX: Send 1-byte register address (no_stop=true -> prepare Repeated START)
|
* 1) TX: 레지스터 주소 1바이트 전송 (no_stop=true → Repeated START 준비)
|
||||||
* 2) RX: Receive data of specified length
|
* 2) RX: 지정된 길이만큼 데이터 수신
|
||||||
*
|
*
|
||||||
* Error handling: Retries once on TX or RX failure.
|
* 에러 처리: TX, RX 각각 실패 시 1회 재시도한다.
|
||||||
*/
|
*/
|
||||||
static unsigned long inv_i2c_master_read_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, unsigned char *RegisterValue){
|
static unsigned long inv_i2c_master_read_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, unsigned char *RegisterValue){
|
||||||
//ret_code_t ret;
|
//ret_code_t ret;
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
uint8_t addr8 = (uint8_t)RegisterAddr;
|
uint8_t addr8 = (uint8_t)RegisterAddr;
|
||||||
|
|
||||||
/* Step 1: Send register address to read (no STOP -> uses Repeated START) */
|
/* 1단계: 읽을 레지스터 주소를 전송 (STOP 없이 → Repeated START 사용) */
|
||||||
ret = icm42670_twi_tx(Address, &addr8, 1, true);
|
ret = icm42670_twi_tx(Address, &addr8, 1, true);
|
||||||
if(ret != NRF_SUCCESS) {
|
if(ret != NRF_SUCCESS) {
|
||||||
/* Retry once on failure */
|
/* 실패 시 1회 재시도 */
|
||||||
ret = icm42670_twi_tx(Address, &addr8, 1, true);
|
ret = icm42670_twi_tx(Address, &addr8, 1, true);
|
||||||
if(ret != NRF_SUCCESS) {
|
if(ret != NRF_SUCCESS) {
|
||||||
printf("ERR! i2c read-1\r\n");
|
printf("ERR! i2c read-1\r\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 2: Receive data from the register */
|
/* 2단계: 해당 레지스터에서 데이터 수신 */
|
||||||
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
|
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
|
||||||
if(ret != NRF_SUCCESS) {
|
if(ret != NRF_SUCCESS) {
|
||||||
/* Retry once on failure */
|
/* 실패 시 1회 재시도 */
|
||||||
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
|
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
|
||||||
if(ret != NRF_SUCCESS) {
|
if(ret != NRF_SUCCESS) {
|
||||||
printf("ERR! i2c read-2\r\n");
|
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()
|
* inv_i2c_master_write_register()
|
||||||
* Writes data to a specific ICM42670P register.
|
* ICM42670P의 특정 레지스터에 데이터를 쓴다.
|
||||||
*
|
*
|
||||||
* Sequence:
|
* 동작 순서:
|
||||||
* 1) Place register address in buffer[0], data in buffer[1..N]
|
* 1) 버퍼[0]에 레지스터 주소, 버퍼[1~N]에 쓸 데이터를 배치
|
||||||
* 2) TX: Send address+data at once (no_stop=false -> includes STOP condition)
|
* 2) TX: 주소+데이터를 한번에 전송 (no_stop=false → STOP 컨디션 포함)
|
||||||
*
|
*
|
||||||
* Error handling: Retries once on failure.
|
* 에러 처리: 실패 시 1회 재시도한다.
|
||||||
*/
|
*/
|
||||||
static unsigned long inv_i2c_master_write_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, const unsigned char *RegisterValue){
|
static unsigned long inv_i2c_master_write_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, const unsigned char *RegisterValue){
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
uint8_t buffer[1 + INV_MAX_SERIAL_WRITE]; /* register address (1) + data (max 16 bytes) */
|
uint8_t buffer[1 + INV_MAX_SERIAL_WRITE]; /* 레지스터 주소(1) + 데이터(최대 16바이트) */
|
||||||
|
|
||||||
/* Buffer layout: [register address][data bytes] */
|
/* 버퍼 구성: [레지스터 주소][데이터 바이트들] */
|
||||||
buffer[0] = (uint8_t)RegisterAddr;
|
buffer[0] = (uint8_t)RegisterAddr;
|
||||||
memcpy(buffer+1, RegisterValue, RegisterLen);
|
memcpy(buffer+1, RegisterValue, RegisterLen);
|
||||||
|
|
||||||
/* Send address+data at once */
|
/* 주소+데이터를 한번에 전송 */
|
||||||
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
|
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
|
||||||
if(ret != NRF_SUCCESS) {
|
if(ret != NRF_SUCCESS) {
|
||||||
/* Retry once on failure */
|
/* 실패 시 1회 재시도 */
|
||||||
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
|
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
|
||||||
if(ret != NRF_SUCCESS) {
|
if(ret != NRF_SUCCESS) {
|
||||||
printf("ERR! i2c write\r\n");
|
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()
|
* inv_io_hal_init()
|
||||||
* Initializes the serial interface (I2C or SPI) used by the IMU driver.
|
* IMU 드라이버가 사용하는 시리얼 인터페이스(I2C 또는 SPI)를 초기화한다.
|
||||||
* Branches based on serif->serif_type; only I2C is currently implemented.
|
* serif->serif_type에 따라 분기하며, 현재는 I2C만 구현되어 있다.
|
||||||
* Returns: 0=success, -1=unsupported interface type
|
* 반환값: 0=성공, -1=지원하지 않는 인터페이스 타입
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int inv_io_hal_init(struct inv_imu_serif *serif)
|
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) {
|
switch (serif->serif_type) {
|
||||||
case UI_SPI4:
|
case UI_SPI4:
|
||||||
{
|
{
|
||||||
/* SPI4 init — not implemented (only I2C is used) */
|
/* SPI4 초기화 — 현재 미구현 (I2C만 사용) */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,8 +219,8 @@ int inv_io_hal_init(struct inv_imu_serif *serif)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* inv_io_hal_read_reg()
|
* inv_io_hal_read_reg()
|
||||||
* IMU driver callback: reads data from the specified register.
|
* IMU 드라이버 콜백: 지정된 레지스터에서 데이터를 읽는다.
|
||||||
* Performs I2C or SPI read depending on the serial type.
|
* 시리얼 타입에 따라 I2C 또는 SPI 읽기를 수행한다.
|
||||||
*/
|
*/
|
||||||
int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen)
|
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()
|
* inv_io_hal_write_reg()
|
||||||
* IMU driver callback: writes data to the specified register.
|
* IMU 드라이버 콜백: 지정된 레지스터에 데이터를 쓴다.
|
||||||
* Performs I2C or SPI write depending on the serial type.
|
* 시리얼 타입에 따라 I2C 또는 SPI 쓰기를 수행한다.
|
||||||
*/
|
*/
|
||||||
int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen)
|
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()
|
* cat_read()
|
||||||
* Generic I2C read function (debug/legacy).
|
* 범용 I2C 읽기 함수 (디버그/레거시용).
|
||||||
* Reads 8 bytes, returns the first byte, and prints the data to console.
|
* 8바이트를 읽어 첫 번째 바이트를 반환하고, 읽은 데이터를 콘솔에 출력한다.
|
||||||
* Note: Not used in production; kept for debug purposes.
|
* 참고: 현재 실제 운용에서는 사용되지 않으며, 디버그 목적으로 남아 있다.
|
||||||
*/
|
*/
|
||||||
uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data)
|
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 = 1|(address<<1);
|
||||||
address = (address & 0xFF);
|
address = (address & 0xFF);
|
||||||
|
|
||||||
/* Send register address (no STOP, prepare Repeated START) */
|
/* 레지스터 주소 전송 (STOP 없이, Repeated START 준비) */
|
||||||
err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, &address, 1, true);
|
err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, &address, 1, true);
|
||||||
if (err_code != NRF_SUCCESS) {
|
if (err_code != NRF_SUCCESS) {
|
||||||
// Handle error
|
// Handle error
|
||||||
// return;
|
// return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive 8 bytes of data */
|
/* 8바이트 데이터 수신 */
|
||||||
err_code = nrfx_twi_rx(&m_twi_icm42670, device_id, data, 8);
|
err_code = nrfx_twi_rx(&m_twi_icm42670, device_id, data, 8);
|
||||||
if (err_code != NRF_SUCCESS) {
|
if (err_code != NRF_SUCCESS) {
|
||||||
// Handle error
|
// Handle error
|
||||||
@@ -298,9 +298,9 @@ uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* cat_write()
|
* cat_write()
|
||||||
* Generic I2C write function (debug/legacy).
|
* 범용 I2C 쓰기 함수 (디버그/레거시용).
|
||||||
* Sends 1 byte address + 1 byte data.
|
* 주소 1바이트 + 데이터 1바이트를 전송한다.
|
||||||
* Note: Copies 6 bytes into buffer, but only transmits 2 bytes.
|
* 참고: buffer에 6바이트를 복사하지만, 실제 전송은 2바이트만 수행한다.
|
||||||
*/
|
*/
|
||||||
void cat_write(uint8_t device_id, uint8_t address, uint8_t *data){
|
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;
|
ret_code_t err_code;
|
||||||
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, 0x00, 1, false);
|
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, 0x00, 1, false);
|
||||||
|
|
||||||
/* Address (1 byte) + data (1 byte) = 2 bytes transmitted */
|
/* 주소(1바이트) + 데이터(1바이트) = 2바이트 전송 */
|
||||||
err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, buffer, 2, false);
|
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);
|
// 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);
|
// nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length);
|
||||||
+20
-21
@@ -7,18 +7,17 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* [Header overview] ICM42670P I2C communication interface declarations
|
* [헤더 개요] ICM42670P I2C 통신 인터페이스 선언
|
||||||
*
|
*
|
||||||
* Pin definitions and function prototypes for communicating with the
|
* nRF52840 TWI 하드웨어를 통해 ICM42670P IMU 센서와 통신하기 위한 핀 정의, 함수 프로토타입 선언
|
||||||
* ICM42670P IMU sensor via nRF52840 TWI hardware.
|
|
||||||
*
|
*
|
||||||
* Pin assignment:
|
* 핀 배치:
|
||||||
* - I2C SCL : P1.14
|
* - I2C SCL : P1.14
|
||||||
* - I2C SDA : P1.15
|
* - I2C SDA : P1.15
|
||||||
* - INT1 : P1.13 (data-ready interrupt)
|
* - INT1 : P1.13 (데이터 준비 인터럽트)
|
||||||
* - INT2 : P0.26 (auxiliary interrupt, currently unused)
|
* - INT2 : P0.26 (보조 인터럽트, 현재 미사용)
|
||||||
*
|
*
|
||||||
* TWI instance: 0
|
* TWI 인스턴스: 0번 사용
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#ifndef _SYSTEM_INTERFACE_H_
|
#ifndef _SYSTEM_INTERFACE_H_
|
||||||
@@ -32,42 +31,42 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define ICM42670_I2C_INSTANCE 0 /**< I2C (TWI) instance index */
|
#define ICM42670_I2C_INSTANCE 0 /**< I2C(TWI) 인스턴스 인덱스 */
|
||||||
#define ICM42670_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15) /**< SDA pin: P1.15 */
|
#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 pin: P1.14 */
|
#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 pin: P1.13 (data-ready interrupt) */
|
#define ICM42670_INT1_PIN NRF_GPIO_PIN_MAP(1,13) /**< INT1 핀: P1.13 (데이터 준비 인터럽트) */
|
||||||
#define ICM42670_INT2_PIN NRF_GPIO_PIN_MAP(0,26) /**< INT2 pin: P0.26 (auxiliary, currently unused) */
|
#define ICM42670_INT2_PIN NRF_GPIO_PIN_MAP(0,26) /**< INT2 핀: P0.26 (보조, 현재 미사용) */
|
||||||
|
|
||||||
/* I2C transmit wrapper — if no_stop=true, STOP condition is omitted for Repeated START */
|
/* I2C 전송 래퍼 — no_stop=true이면 Repeated START를 위해 STOP 컨디션 생략 */
|
||||||
uint32_t icm42670_twi_tx( uint8_t device_id,
|
uint32_t icm42670_twi_tx( uint8_t device_id,
|
||||||
uint8_t const * p_data,
|
uint8_t const * p_data,
|
||||||
uint8_t length,
|
uint8_t length,
|
||||||
bool no_stop);
|
bool no_stop);
|
||||||
|
|
||||||
/* I2C receive wrapper */
|
/* I2C 수신 래퍼 */
|
||||||
uint32_t icm42670_twi_rx( uint8_t device_id,
|
uint32_t icm42670_twi_rx( uint8_t device_id,
|
||||||
uint8_t * p_data,
|
uint8_t * p_data,
|
||||||
uint8_t length);
|
uint8_t length);
|
||||||
|
|
||||||
/* Generic I2C read (debug/legacy) — reads 8 bytes and returns the first byte */
|
/* 범용 I2C 읽기 (디버그/레거시용) — 8바이트를 읽어 첫 바이트 반환 */
|
||||||
uint8_t cat_read (uint8_t device_id, uint8_t address, uint8_t *data);
|
uint8_t cat_read (uint8_t device_id, uint8_t address, uint8_t *data);
|
||||||
|
|
||||||
/* Generic I2C write (debug/legacy) — sends address+data 2 bytes */
|
/* 범용 I2C 쓰기 (디버그/레거시용) — 주소+데이터 2바이트 전송 */
|
||||||
void cat_write (uint8_t device_id, uint8_t address, uint8_t *data);
|
void cat_write (uint8_t device_id, uint8_t address, uint8_t *data);
|
||||||
|
|
||||||
/* Release I2C hardware (called before sleep or re-initialization) */
|
/* I2C 하드웨어 해제 (슬립 또는 재초기화 전 호출) */
|
||||||
void inv_i2c_master_uninitialize(void);
|
void inv_i2c_master_uninitialize(void);
|
||||||
|
|
||||||
/* Initialize I2C hardware (100kHz, blocking mode) */
|
/* I2C 하드웨어 초기화 (100kHz, 블로킹 모드) */
|
||||||
void inv_i2c_master_initialize(void);
|
void inv_i2c_master_initialize(void);
|
||||||
|
|
||||||
/* Initialize serial interface for IMU driver (I2C/SPI branch) */
|
/* IMU 드라이버용 시리얼 인터페이스 초기화 (I2C/SPI 분기) */
|
||||||
int inv_io_hal_init(struct inv_imu_serif *serif);
|
int inv_io_hal_init(struct inv_imu_serif *serif);
|
||||||
|
|
||||||
/* IMU driver callback: register read */
|
/* IMU 드라이버 콜백: 레지스터 읽기 */
|
||||||
int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen);
|
int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen);
|
||||||
|
|
||||||
/* IMU driver callback: register write */
|
/* IMU 드라이버 콜백: 레지스터 쓰기 */
|
||||||
int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen);
|
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_ */
|
#endif /* !_SYSTEM_INTERFACE_H_ */
|
||||||
+46
-46
@@ -1,11 +1,11 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* @file led_control.c
|
* @file led_control.c
|
||||||
* @brief Direct LED control driver
|
* @brief LED 직접 제어 드라이버 (BSP 미사용)
|
||||||
* @date 2026-03-30
|
* @date 2026-03-30
|
||||||
*
|
*
|
||||||
* Implements dual-color LED (green/orange) blink patterns with a single app_timer.
|
* app_timer 1개로 2색 LED(녹색/주황)의 복합 blink 패턴 구현
|
||||||
* Simple on/off states use immediate GPIO control without a timer.
|
* 단순 on/off 상태는 타이머 없이 즉시 GPIO 제어
|
||||||
* Complex patterns (e.g. error) are handled by a phase-based state machine.
|
* 복합 패턴(에러 등)은 phase 기반 state machine으로 처리
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#include "led_control.h"
|
#include "led_control.h"
|
||||||
@@ -13,20 +13,20 @@
|
|||||||
#include "app_timer.h"
|
#include "app_timer.h"
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Internal constants
|
* 내부 상수
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
#define MS_TO_TICKS(ms) APP_TIMER_TICKS(ms)
|
#define MS_TO_TICKS(ms) APP_TIMER_TICKS(ms)
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Colors
|
* 색상
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
#define COLOR_NONE 0
|
#define COLOR_NONE 0
|
||||||
#define COLOR_GREEN 1
|
#define COLOR_GREEN 1
|
||||||
#define COLOR_ORANGE 2
|
#define COLOR_ORANGE 2
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Error pattern constants (No.7)
|
* 에러 패턴 상수 (No.7)
|
||||||
* 3Hz blink x3 = 166ms on + 166ms off x 3 = ~1s, then off for 1s
|
* 3Hz 깜빡 3회 = 166ms on + 166ms off × 3 = ~1초, 이후 꺼짐 1초
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
#define ERROR_BLINK_ON_MS 166
|
#define ERROR_BLINK_ON_MS 166
|
||||||
#define ERROR_BLINK_OFF_MS 166
|
#define ERROR_BLINK_OFF_MS 166
|
||||||
@@ -34,42 +34,42 @@
|
|||||||
#define ERROR_PAUSE_MS 1000
|
#define ERROR_PAUSE_MS 1000
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Pattern table (for simple blink)
|
* 패턴 테이블 (단순 blink 용)
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t on_ms; /* LED on duration (ms) */
|
uint32_t on_ms; /* LED 켜짐 시간 (ms) */
|
||||||
uint32_t off_ms; /* LED off duration (ms) */
|
uint32_t off_ms; /* LED 꺼짐 시간 (ms) */
|
||||||
uint8_t color; /* COLOR_GREEN or COLOR_ORANGE */
|
uint8_t color; /* COLOR_GREEN 또는 COLOR_ORANGE */
|
||||||
bool repeat; /* true: repeat forever, false: once then OFF */
|
bool repeat; /* true: 무한 반복, false: 1회 후 OFF */
|
||||||
} led_pattern_t;
|
} led_pattern_t;
|
||||||
|
|
||||||
static const led_pattern_t m_patterns[LED_STATE_COUNT] =
|
static const led_pattern_t m_patterns[LED_STATE_COUNT] =
|
||||||
{
|
{
|
||||||
[LED_STATE_OFF] = { 0, 0, COLOR_NONE, false },
|
[LED_STATE_OFF] = { 0, 0, COLOR_NONE, false },
|
||||||
[LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false }, /* Green on 2s, stay on */
|
[LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → 유지 */
|
||||||
[LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, /* Green on 2s, then OFF */
|
[LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → OFF */
|
||||||
[LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true }, /* Green blink 1s interval */
|
[LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true }, /* 초록 점멸 1초 */
|
||||||
[LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, /* Green 1s on / 3s off */
|
[LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, /* 초록 1초 on / 3초 off */
|
||||||
[LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, /* Orange blink 1s interval */
|
[LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, /* 주황 점멸 1초 */
|
||||||
[LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true }, /* Green 3s on / 1s off */
|
[LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true }, /* 초록 3초 on / 1초 off */
|
||||||
[LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true } /* Separate state machine */
|
[LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true } /* 별도 state machine */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Module variables
|
* 모듈 변수
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
APP_TIMER_DEF(m_led_timer);
|
APP_TIMER_DEF(m_led_timer);
|
||||||
|
|
||||||
static led_state_t m_current_state = LED_STATE_OFF;
|
static led_state_t m_current_state = LED_STATE_OFF;
|
||||||
static bool m_phase_on; /* true: LED on phase, false: off phase */
|
static bool m_phase_on; /* true: LED 켜진 구간, false: 꺼진 구간 */
|
||||||
|
|
||||||
/* Error pattern only */
|
/* 에러 패턴 전용 */
|
||||||
static uint8_t m_error_blink_cnt; /* Blink count so far */
|
static uint8_t m_error_blink_cnt; /* 현재까지 깜빡인 횟수 */
|
||||||
static uint8_t m_error_phase; /* 0: blink-on, 1: blink-off, 2: pause */
|
static uint8_t m_error_phase; /* 0: blink-on, 1: blink-off, 2: pause */
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* GPIO helpers
|
* GPIO 헬퍼
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
|
|
||||||
static inline void led_green_on(void)
|
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)
|
static void timer_start_ms(uint32_t ms)
|
||||||
{
|
{
|
||||||
@@ -131,7 +131,7 @@ static void timer_start_ms(uint32_t ms)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Error pattern state machine (No.7)
|
* 에러 패턴 state machine (No.7)
|
||||||
* phase 0: LED ON (166ms) → phase 1
|
* phase 0: LED ON (166ms) → phase 1
|
||||||
* phase 1: LED OFF (166ms) → cnt++ → cnt<3 ? phase 0 : phase 2
|
* phase 1: LED OFF (166ms) → cnt++ → cnt<3 ? phase 0 : phase 2
|
||||||
* phase 2: PAUSE (1000ms) → cnt=0, phase 0
|
* phase 2: PAUSE (1000ms) → cnt=0, phase 0
|
||||||
@@ -148,30 +148,30 @@ static void error_pattern_tick(void)
|
|||||||
{
|
{
|
||||||
switch (m_error_phase)
|
switch (m_error_phase)
|
||||||
{
|
{
|
||||||
case 0: /* ON phase done -> OFF */
|
case 0: /* ON 구간 끝 → OFF */
|
||||||
led_all_off();
|
led_all_off();
|
||||||
m_error_phase = 1;
|
m_error_phase = 1;
|
||||||
timer_start_ms(ERROR_BLINK_OFF_MS);
|
timer_start_ms(ERROR_BLINK_OFF_MS);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: /* OFF phase done */
|
case 1: /* OFF 구간 끝 */
|
||||||
m_error_blink_cnt++;
|
m_error_blink_cnt++;
|
||||||
if (m_error_blink_cnt < ERROR_BLINK_COUNT)
|
if (m_error_blink_cnt < ERROR_BLINK_COUNT)
|
||||||
{
|
{
|
||||||
/* Back to ON */
|
/* 다시 ON */
|
||||||
m_error_phase = 0;
|
m_error_phase = 0;
|
||||||
led_color_on(COLOR_ORANGE);
|
led_color_on(COLOR_ORANGE);
|
||||||
timer_start_ms(ERROR_BLINK_ON_MS);
|
timer_start_ms(ERROR_BLINK_ON_MS);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* 3 blinks done -> pause */
|
/* 3회 완료 → pause */
|
||||||
m_error_phase = 2;
|
m_error_phase = 2;
|
||||||
timer_start_ms(ERROR_PAUSE_MS);
|
timer_start_ms(ERROR_PAUSE_MS);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: /* Pause done -> restart */
|
case 2: /* pause 끝 → 처음부터 */
|
||||||
m_error_blink_cnt = 0;
|
m_error_blink_cnt = 0;
|
||||||
m_error_phase = 0;
|
m_error_phase = 0;
|
||||||
led_color_on(COLOR_ORANGE);
|
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)
|
static void led_timer_handler(void * p_context)
|
||||||
{
|
{
|
||||||
(void)p_context;
|
(void)p_context;
|
||||||
|
|
||||||
/* Error state handled separately */
|
/* 에러 상태는 별도 처리 */
|
||||||
if (m_current_state == LED_STATE_ERROR)
|
if (m_current_state == LED_STATE_ERROR)
|
||||||
{
|
{
|
||||||
error_pattern_tick();
|
error_pattern_tick();
|
||||||
@@ -201,7 +201,7 @@ static void led_timer_handler(void * p_context)
|
|||||||
|
|
||||||
if (m_phase_on)
|
if (m_phase_on)
|
||||||
{
|
{
|
||||||
/* ON -> OFF transition */
|
/* ON→OFF 전환 */
|
||||||
led_all_off();
|
led_all_off();
|
||||||
m_phase_on = false;
|
m_phase_on = false;
|
||||||
|
|
||||||
@@ -211,40 +211,40 @@ static void led_timer_handler(void * p_context)
|
|||||||
}
|
}
|
||||||
else if (!p->repeat)
|
else if (!p->repeat)
|
||||||
{
|
{
|
||||||
/* One-shot: if off_ms == 0, stay on (POWER_ON) or turn off (POWER_OFF) */
|
/* 1회성: off_ms == 0 이면 그냥 유지 (POWER_ON) 또는 끄기 (POWER_OFF) */
|
||||||
if (m_current_state == LED_STATE_POWER_OFF)
|
if (m_current_state == LED_STATE_POWER_OFF)
|
||||||
{
|
{
|
||||||
led_all_off();
|
led_all_off();
|
||||||
m_current_state = LED_STATE_OFF;
|
m_current_state = LED_STATE_OFF;
|
||||||
}
|
}
|
||||||
/* POWER_ON: stay lit, no timer */
|
/* POWER_ON: 점등 유지 상태 → 타이머 x */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* OFF -> ON transition */
|
/* OFF → ON 전환 */
|
||||||
if (p->repeat)
|
if (p->repeat)
|
||||||
{
|
{
|
||||||
led_color_on(p->color);
|
led_color_on(p->color);
|
||||||
m_phase_on = true;
|
m_phase_on = true;
|
||||||
timer_start_ms(p->on_ms);
|
timer_start_ms(p->on_ms);
|
||||||
}
|
}
|
||||||
/* No current case where repeat == false && off_ms > 0 */
|
/* repeat == false && off_ms > 0 인 경우는 현재 없음 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Public functions
|
* 공개 함수
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
|
|
||||||
void led_init(void)
|
void led_init(void)
|
||||||
{
|
{
|
||||||
/* Configure GPIO outputs */
|
/* GPIO 출력 설정 */
|
||||||
nrf_gpio_cfg_output(LED_PIN_GREEN);
|
nrf_gpio_cfg_output(LED_PIN_GREEN);
|
||||||
nrf_gpio_cfg_output(LED_PIN_ORANGE);
|
nrf_gpio_cfg_output(LED_PIN_ORANGE);
|
||||||
led_all_off();
|
led_all_off();
|
||||||
|
|
||||||
/* Create timer (single-shot) */
|
/* 타이머 생성 (single-shot) */
|
||||||
app_timer_create(&m_led_timer, APP_TIMER_MODE_SINGLE_SHOT, led_timer_handler);
|
app_timer_create(&m_led_timer, APP_TIMER_MODE_SINGLE_SHOT, led_timer_handler);
|
||||||
|
|
||||||
m_current_state = LED_STATE_OFF;
|
m_current_state = LED_STATE_OFF;
|
||||||
@@ -254,7 +254,7 @@ void led_set_state(led_state_t state)
|
|||||||
{
|
{
|
||||||
if (state >= LED_STATE_COUNT) return;
|
if (state >= LED_STATE_COUNT) return;
|
||||||
|
|
||||||
/* Stop previous pattern */
|
/* 이전 패턴 중단 */
|
||||||
app_timer_stop(m_led_timer);
|
app_timer_stop(m_led_timer);
|
||||||
led_all_off();
|
led_all_off();
|
||||||
|
|
||||||
@@ -266,15 +266,15 @@ void led_set_state(led_state_t state)
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case LED_STATE_OFF:
|
case LED_STATE_OFF:
|
||||||
/* Already all off */
|
/* 이미 all off */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Error pattern: separate state machine */
|
/* 에러 패턴: 별도 state machine */
|
||||||
case LED_STATE_ERROR:
|
case LED_STATE_ERROR:
|
||||||
error_pattern_start();
|
error_pattern_start();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* All others: start from ON phase */
|
/* 그 외: on 구간부터 시작 */
|
||||||
default:
|
default:
|
||||||
led_color_on(p->color);
|
led_color_on(p->color);
|
||||||
m_phase_on = true;
|
m_phase_on = true;
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file led_control.h
|
||||||
|
* @brief LED 직접 제어 드라이버 (BSP 미사용)
|
||||||
|
* @date 2026-03-30
|
||||||
|
*
|
||||||
|
* 녹색(P0.12) + 주황(P0.29) 2색 LED를 app_timer 기반으로 제어
|
||||||
|
* 각 상태별 on/off 시간, 색상, 반복 패턴을 테이블로 관리
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef LED_CONTROL_H__
|
||||||
|
#define LED_CONTROL_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* LED 핀 정의
|
||||||
|
*============================================================================*/
|
||||||
|
#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 켜짐) */
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* LED 상태 열거형
|
||||||
|
*============================================================================*/
|
||||||
|
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_t;
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 공개 함수
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/** @brief LED GPIO 초기화 + 타이머 생성 - main()에서 1회 호출 */
|
||||||
|
void led_init(void);
|
||||||
|
|
||||||
|
/** @brief LED 상태 변경 - 이전 패턴을 즉시 중단하고 새 패턴 시작 */
|
||||||
|
void led_set_state(led_state_t state);
|
||||||
|
|
||||||
|
/** @brief 현재 LED 상태 조회 */
|
||||||
|
led_state_t led_get_state(void);
|
||||||
|
|
||||||
|
#endif /* LED_CONTROL_H__ */
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -3,49 +3,43 @@
|
|||||||
* @author CandyPops Co.
|
* @author CandyPops Co.
|
||||||
* @version V1.0.0
|
* @version V1.0.0
|
||||||
* @date 2022-09-05
|
* @date 2022-09-05
|
||||||
* @brief VesiScan BASIC main header file
|
* @brief VesiScan BASIC 메인 헤더 파일
|
||||||
*
|
*
|
||||||
* [System Overview]
|
* [시스템 개요]
|
||||||
* VesiScan BASIC is an nRF52840-based BLE bladder monitoring patch device.
|
* VesiScan BASIC은 nRF52840 기반 BLE 방광 모니터링 패치 디바이스이다.
|
||||||
* This header defines enums, function declarations, and global variables used system-wide.
|
* 본 헤더는 시스템 전역에서 사용하는 열거형, 함수 선언, 전역 변수를 정의한다.
|
||||||
*
|
*
|
||||||
* [Communication]
|
* [통신 방식]
|
||||||
* - BLE NUS (Nordic UART Service): binary protocol with smartphone app
|
* - BLE NUS (Nordic UART Service): 스마트폰 앱과 바이너리 프로토콜 통신
|
||||||
* - Physical UART (1Mbps): debug and factory testing
|
* - 물리 UART (1Mbps): 디버그 및 공장 테스트용
|
||||||
*
|
*
|
||||||
* [Data Transmission Flow]
|
* [데이터 전송 흐름]
|
||||||
* 1. Receive command from app/UART -> received_command_process()
|
* 1. 앱/UART에서 명령 수신 → received_command_process()
|
||||||
* 2. Collect sensor data (battery, temperature, IMU, pressure)
|
* 2. 센서 데이터 수집 (배터리, 온도, IMU, 압력)
|
||||||
* 3. Build binary packets via format_data() family
|
* 3. format_data() 계열 함수로 바이너리 패킷 생성
|
||||||
* 4. Transmit over BLE with CRC16 appended via dr_binary_tx_safe()
|
* 4. dr_binary_tx_safe()로 CRC16 추가 후 BLE 전송
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#ifndef MAIN_H__
|
#ifndef MAIN_H__
|
||||||
#define MAIN_H__
|
#define MAIN_H__
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------
|
/* -------------------------------------------------------------------------
|
||||||
* Firmware Identification Code
|
* Firmware 식별 코드
|
||||||
* - VBTFW0100 = Development (test) build Ver 1.00
|
* - VBTFW0100 = 개발(시험)용 Ver 1.00
|
||||||
* - VB0FW0100 = Production build Ver 1.00
|
* - VB0FW0100 = 양산용 Ver 1.00
|
||||||
*
|
*
|
||||||
* Firmware Version Update History
|
* Firmware Version Update History
|
||||||
* - VBTFW0101 : Merged reb+red packets (single packet per channel), 260330 jhChun
|
* - VBTFW0101 : reb+red 패킷 병합 (채널당 단일 패킷), 260330 jhChun
|
||||||
* - VBTFW0102 : Added LED state command (msl) and re-pairing support, 260331 jhChun
|
* - VBTFW0102 : LED 상태 설정 명령(msl) 추가 및 재페어링 허용 등 260331 jhChun
|
||||||
* - VBTFW0103 260416 jhChun
|
|
||||||
* : Stabilized ADC measurement by clearing buffers before sampling.
|
|
||||||
* : Improved low-battery detection and automatic power-off handling.
|
|
||||||
* : Updated BLE security, bonding, and advertising timeout behavior.
|
|
||||||
* : Cleaned up command parsing and removed unused project files.
|
|
||||||
* - VBTFW0111 260421 jhChun
|
|
||||||
------------------------------------------------------------------------- */
|
------------------------------------------------------------------------- */
|
||||||
#define FIRMWARE_VERSION "VBTFW0111"
|
#define FIRMWARE_VERSION "VBTFW0102"
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Data Length Constants
|
* 데이터 길이 상수
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
#define SERIAL_NO_LENGTH 12 /* Serial number length (e.g. "VB026030000") */
|
#define SERIAL_NO_LENGTH 12 /* 시리얼 번호 길이 (예: "VB026030000") */
|
||||||
#define HW_NO_LENGTH 12 /* Hardware number (version) length */
|
#define HW_NO_LENGTH 12 /* 하드웨어 번호(버전) 길이 */
|
||||||
#define PASSKEY_LENGTH 6 /* BLE pairing passkey length (6 digits) */
|
#define PASSKEY_LENGTH 6 /* BLE 페어링 패스키 길이 (숫자 6자리) */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -55,127 +49,128 @@
|
|||||||
#include "boards.h"
|
#include "boards.h"
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Enum Definitions
|
* 열거형 정의
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
|
|
||||||
/* Device ON/OFF control enum (EEPROM, power, etc.) */
|
/* 디바이스 ON/OFF 제어용 열거형 (EEPROM, 전원 등) */
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
OFF = 0, /* Off */
|
OFF = 0, /* 꺼짐 */
|
||||||
ON = 1 /* On */
|
ON = 1 /* 켜짐 */
|
||||||
}on_off_cont_t;
|
}on_off_cont_t;
|
||||||
|
|
||||||
/* Command source identifier (BLE or UART) */
|
/* 명령 수신 경로 구분 (BLE 또는 UART) */
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
CMD_BLE = 0, /* Command received via BLE NUS */
|
CMD_BLE = 0, /* BLE NUS를 통해 수신된 명령 */
|
||||||
CMD_UART = 1 /* Command received via physical UART */
|
CMD_UART = 1 /* 물리 UART를 통해 수신된 명령 */
|
||||||
}which_cmd_t;
|
}which_cmd_t;
|
||||||
|
|
||||||
/* Chamber auto-test mode (when FEATURE_CHAMBER_AUTO_TEST enabled) */
|
/* 챔버 자동 테스트 모드 (FEATURE_CHAMBER_AUTO_TEST 활성 시) */
|
||||||
#if FEATURE_CHAMBER_AUTO_TEST
|
#if FEATURE_CHAMBER_AUTO_TEST
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
SIMPLE_AUTO_MODE = 0, /* Simple auto mode */
|
SIMPLE_AUTO_MODE = 0, /* 간단 자동 모드 */
|
||||||
HALF_AUTO_MODE = 1, /* Semi-auto mode */
|
HALF_AUTO_MODE = 1, /* 반자동 모드 */
|
||||||
FULL_AUTO_MODE = 2, /* Full auto mode */
|
FULL_AUTO_MODE = 2, /* 전체 자동 모드 */
|
||||||
NONE_AUTO_MODE = 3 /* No auto mode */
|
NONE_AUTO_MODE = 3 /* 자동 모드 없음 */
|
||||||
}auto_meas_mode_t;
|
}auto_meas_mode_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* BLE connection state */
|
/* BLE 연결 상태 */
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
BLE_DISCONNECTED_ST = 0, /* BLE disconnected */
|
BLE_DISCONNECTED_ST = 0, /* BLE 미연결 */
|
||||||
BLE_CONNECTED_ST = 1 /* BLE connected */
|
BLE_CONNECTED_ST = 1 /* BLE 연결됨 */
|
||||||
}ble_status_t;
|
}ble_status_t;
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Function Declarations
|
* 함수 선언
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
|
|
||||||
#if FEATURE_SECURE_CONNECTION
|
#if FEATURE_SECURE_CONNECTION
|
||||||
/* Start BLE advertising (if erase_bonds=true, delete bond info first) */
|
/* BLE 광고 시작 (erase_bonds=true이면 본딩 정보 삭제 후 시작) */
|
||||||
static void advertising_start(bool erase_bonds);
|
static void advertising_start(bool erase_bonds);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Enter sleep mode: show LED, then power off after POWER_OFF_DELAY (3s) */
|
/* 슬립 모드 진입: LED 표시 후 POWER_OFF_DELAY(3초) 후 전원 차단 */
|
||||||
void sleep_mode_enter(void);
|
void sleep_mode_enter(void);
|
||||||
|
|
||||||
/* Power-off timer callback: physically cut power after POWER_OFF_DELAY */
|
/* 전원 OFF 타이머 콜백: POWER_OFF_DELAY 경과 후 실제 전원 차단 */
|
||||||
static void t_power_off_timeout_handler(void * p_context);
|
static void t_power_off_timeout_handler(void * p_context);
|
||||||
|
|
||||||
/* Device power off: show LED, then delayed power cut via timer */
|
/* 디바이스 전원 OFF: LED 표시 후 타이머로 지연 전원 차단 */
|
||||||
void device_power_off(void);
|
void device_power_off(void);
|
||||||
|
|
||||||
/* Power control handler: physical power ON/OFF via POWER_HOLD pin */
|
/* 전원 제어 핸들러: POWER_HOLD 핀으로 물리적 전원 ON/OFF */
|
||||||
static void power_control_handler(on_off_cont_t device_power_st);
|
static void power_control_handler(on_off_cont_t device_power_st);
|
||||||
|
|
||||||
/* Power button state machine (timer callback, 5ms interval):
|
/* 전원 버튼 상태머신 (타이머 콜백, 5ms 간격):
|
||||||
* - Short press (<1.5s): power OFF
|
* - 짧은 눌림(<1.5초): 전원 OFF
|
||||||
* - Medium press (1.5s~10s): start boot sequence
|
* - 중간 눌림(1.5초~10초): 부팅 시퀀스 시작
|
||||||
* - Long press (>10s): factory reset (passkey reset + power OFF) */
|
* - 긴 눌림(>10초): 공장 초기화 (패스키 리셋 + 전원 OFF) */
|
||||||
static void main_s(void * p_context);
|
static void main_s(void * p_context);
|
||||||
|
|
||||||
/* Peer Manager timer callback: force BLE disconnect if reset_status==5 */
|
/* Peer Manager 타이머 콜백: reset_status==5이면 BLE 연결 강제 해제 */
|
||||||
static void PM_s(void * p_context);
|
static void PM_s(void * p_context);
|
||||||
|
|
||||||
//static void main_re(void * p_context);
|
//static void main_re(void * p_context);
|
||||||
|
|
||||||
/* Main routine handler (legacy, currently unused) */
|
/* 메인 루틴 핸들러 (레거시, 현재 미사용) */
|
||||||
static void main_routine_handler(void * p_context);
|
static void main_routine_handler(void * p_context);
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Data Transmission Functions
|
* 데이터 전송 함수
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* Send ASCII text over BLE (up to '\r', CRC16 appended automatically) */
|
/* ASCII 텍스트 BLE 전송 ('\r'까지 전송, CRC16 자동 추가) */
|
||||||
void data_tx_handler(char const *p_data_to_send);
|
void data_tx_handler(char const *p_data_to_send);
|
||||||
|
|
||||||
/* Safe binary data BLE transmission (CRC16 appended, with retry logic)
|
/* 바이너리 데이터 BLE 안전 전송 (CRC16 자동 추가, 재시도 로직 포함)
|
||||||
* @param ble_bin_buff Binary buffer to transmit
|
* @param ble_bin_buff 전송할 바이너리 버퍼
|
||||||
* @param length Data length in uint16_t words (actual bytes = length x 2) */
|
* @param length 데이터 길이 (uint16_t 워드 단위, 실제 바이트 = length × 2) */
|
||||||
void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length);
|
void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length);
|
||||||
|
|
||||||
/* SoftDevice-compatible delay (nrf_delay_ms wrapper) */
|
/* SoftDevice 호환 딜레이 (nrf_delay_ms 래퍼) */
|
||||||
void dr_sd_delay_ms(uint32_t ms);
|
void dr_sd_delay_ms(uint32_t ms);
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Binary Packet Format Functions
|
* 바이너리 패킷 포맷 함수
|
||||||
* Packet structure: [4-byte tag][data][2-byte CRC16]
|
* 패킷 구조: [4바이트 태그][데이터][2바이트 CRC16]
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* Format single uint16_t value: [tag 4B][value 2B] */
|
/* 단일 uint16_t 값 포맷: [tag 4B][value 2B] */
|
||||||
void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value) ;
|
void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value) ;
|
||||||
|
|
||||||
/* Format uint16_t array: [tag 4B][data0 2B][data1 2B]... */
|
/* uint16_t 배열 포맷: [tag 4B][data0 2B][data1 2B]... */
|
||||||
void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length);
|
void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length);
|
||||||
|
|
||||||
/* Format uint8_t byte array: [tag 4B][byte0][byte1]... */
|
/* uint8_t 바이트 배열 포맷: [tag 4B][byte0][byte1]... */
|
||||||
void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length);
|
void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length);
|
||||||
|
|
||||||
/* Format ASCII string: [tag 4B][char0][char1]... */
|
/* ASCII 문자열 포맷: [tag 4B][char0][char1]... */
|
||||||
void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length);
|
void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length);
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Global Variables (extern)
|
* 전역 변수 (extern)
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
extern volatile bool data_tx_in_progress; /* BLE TX in progress flag */
|
extern volatile bool data_tx_in_progress; /* BLE TX 전송 진행 중 플래그 */
|
||||||
extern volatile bool ble_connection_st; /* BLE connection state (0=disconnected, 1=connected) */
|
extern volatile bool ble_connection_st; /* BLE 연결 상태 (0=미연결, 1=연결) */
|
||||||
|
extern volatile bool processing; /* 센서 데이터 처리 중 플래그 (중복 명령 방지) */
|
||||||
|
|
||||||
/* 2026-03-17: Global variables moved from cmd_parse.c to main.c */
|
/* 2026-03-17: cmd_parse.c에서 main.c로 이동한 전역변수 */
|
||||||
extern char SERIAL_NO[SERIAL_NO_LENGTH]; /* Serial number */
|
extern char SERIAL_NO[SERIAL_NO_LENGTH]; /* 시리얼 번호 */
|
||||||
extern char HW_NO[HW_NO_LENGTH]; /* Hardware number */
|
extern char HW_NO[HW_NO_LENGTH]; /* 하드웨어 번호 */
|
||||||
extern char m_static_passkey[PASSKEY_LENGTH]; /* BLE static passkey */
|
extern char m_static_passkey[PASSKEY_LENGTH]; /* BLE 정적 패스키 */
|
||||||
extern bool bond_data_delete; /* Bond data delete request flag */
|
extern bool bond_data_delete; /* 본딩 데이터 삭제 요청 플래그 */
|
||||||
extern uint32_t m_life_cycle; /* Device life cycle counter */
|
extern uint32_t m_life_cycle; /* 디바이스 수명 사이클 카운터 */
|
||||||
extern uint8_t resetCount; /* Communication timeout counter */
|
extern uint8_t resetCount; /* 통신 타임아웃 카운터 */
|
||||||
extern bool info4; /* Measurement with extra info flag */
|
extern bool info4; /* 추가 정보 포함 측정 플래그 */
|
||||||
extern uint8_t m_reset_status; /* Reset status code */
|
extern uint8_t m_reset_status; /* 리셋 상태 코드 */
|
||||||
|
|
||||||
extern uint8_t ble_bin_buffer[]; /* BLE binary response buffer */
|
extern uint8_t ble_bin_buffer[]; /* BLE 바이너리 응답 버퍼 */
|
||||||
|
|
||||||
/* Send error response */
|
/* 에러 응답 전송 */
|
||||||
void param_error(const char *cmd);
|
void param_error(const char *cmd);
|
||||||
|
|
||||||
#endif //MAIN_H__
|
#endif //MAIN_H__
|
||||||
|
|||||||
@@ -0,0 +1,280 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
TEST medi50 Dec 23
|
||||||
|
*******************************************************************************
|
||||||
|
*
|
||||||
|
* [모듈 개요]
|
||||||
|
* 메인 이벤트 루프 타이머 모듈 (10ms 간격, 싱글샷 모드).
|
||||||
|
*
|
||||||
|
* 센서 데이터 수집 및 시스템 제어 이벤트를 플래그 기반으로 디스패치한다.
|
||||||
|
* app_timer 싱글샷 모드를 사용하므로, 이벤트 처리 완료 후
|
||||||
|
* 필요 시 main_timer_start()로 수동 재시작해야 한다.
|
||||||
|
*
|
||||||
|
* [이벤트 플래그 및 처리 순서]
|
||||||
|
* 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 시스템 리셋
|
||||||
|
*
|
||||||
|
* [info4 모드 측정 순서]
|
||||||
|
* IMU 연속 읽기 → go_batt(배터리) → go_temp(온도) → motion_data_once(IMU 단발)
|
||||||
|
* 온도 측정 완료 시 motion_data_once=true로 설정하여 다시 IMU로 돌아간다.
|
||||||
|
*
|
||||||
|
* [타이머 설정]
|
||||||
|
* - 일반 모드: 10ms 간격 (MAIN_LOOP_INTERVAL)
|
||||||
|
* - FEATURE_DETAIL_VALUE_FULL 모드: 80ms 간격 (디테일 프린트아웃용)
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "sdk_common.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "nrf.h"
|
||||||
|
#include "nrf_drv_saadc.h"
|
||||||
|
#include "nrf_drv_ppi.h"
|
||||||
|
#include "nrf_drv_timer.h"
|
||||||
|
#include "boards.h"
|
||||||
|
#include "app_error.h"
|
||||||
|
#include "nrf_delay.h"
|
||||||
|
#include "app_util_platform.h"
|
||||||
|
#include "nrf_pwr_mgmt.h"
|
||||||
|
|
||||||
|
#include "nrf_log.h"
|
||||||
|
#include "nrf_drv_gpiote.h"
|
||||||
|
#include "battery_saadc.h"
|
||||||
|
#include "app_timer.h"
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
#include "app_raw_main.h"
|
||||||
|
#include "main_timer.h"
|
||||||
|
#include "tmp235_q1.h"
|
||||||
|
//#include "fstorage.h"
|
||||||
|
#include "power_control.h"
|
||||||
|
#include "main.h" /* 2026-03-17: cmd_parse.h 삭제 → main.h */
|
||||||
|
#include "debug_print.h"
|
||||||
|
#include "i2c_manager.h" //add cj
|
||||||
|
|
||||||
|
/* 메인 루프 싱글샷 타이머 인스턴스 */
|
||||||
|
APP_TIMER_DEF(m_main_loop_timer_id);
|
||||||
|
|
||||||
|
#if FEATURE_DETAIL_VALUE_FULL
|
||||||
|
/* 디테일 프린트아웃 모드: 80ms 간격으로 메인 루프 실행 */
|
||||||
|
#define MAIN_LOOP_INTERVAL 80 /* 디테일 프린트아웃이 있을경우 Full_Mode Main Prosessing 수행하는 타이머 */
|
||||||
|
|
||||||
|
//extern bool pd_adc_full_a_start;
|
||||||
|
//extern bool pd_adc_full_b_start;
|
||||||
|
//extern bool pd_adc_full_c_start;
|
||||||
|
//extern bool pd_adc_full_d_start;
|
||||||
|
//extern bool pd_adc_full_end;
|
||||||
|
|
||||||
|
extern which_cmd_t cmd_type_t;
|
||||||
|
#else
|
||||||
|
/* 일반 모드: 10ms 간격으로 메인 루프 실행 */
|
||||||
|
#define MAIN_LOOP_INTERVAL 10
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ========================================================================== */
|
||||||
|
/* 이벤트 플래그 (외부 모듈에서 설정, 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회만 읽기) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 메인 이벤트 루프 (싱글샷 타이머 콜백)
|
||||||
|
*
|
||||||
|
* 플래그 기반 이벤트 디스패처로, 설정된 플래그에 따라 해당 처리를 수행한다.
|
||||||
|
* 싱글샷 타이머이므로 연속 실행이 필요한 경우 처리 내부에서 main_timer_start()를
|
||||||
|
* 다시 호출하여 타이머를 재시작해야 한다.
|
||||||
|
*
|
||||||
|
* [처리 우선순위] (코드 순서대로 검사)
|
||||||
|
* 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)
|
||||||
|
*/
|
||||||
|
void main_loop(void * p_context) /* For x ms */
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(p_context);
|
||||||
|
|
||||||
|
#if FEATURE_DETAIL_VALUE_FULL
|
||||||
|
// if(pd_adc_full_a_start == true) { // A mode
|
||||||
|
// main_timer_stop();
|
||||||
|
// printf("main_loop_A\r\n");
|
||||||
|
// full_adc_start();
|
||||||
|
// }else if(pd_adc_full_b_start == true) { // B mode
|
||||||
|
// main_timer_stop();
|
||||||
|
// printf("main_loop_B\r\n");
|
||||||
|
// full_adc_start();
|
||||||
|
// }else if(pd_adc_full_c_start == true) { // C mode
|
||||||
|
// main_timer_stop();
|
||||||
|
// printf("main_loop_C\r\n");
|
||||||
|
// full_adc_start();
|
||||||
|
// }else if(pd_adc_full_d_start == true) { // D mode
|
||||||
|
// main_timer_stop();
|
||||||
|
// printf("main_loop_D\r\n");
|
||||||
|
// full_adc_start();
|
||||||
|
// }else if(pd_adc_full_end == true) { // Completed
|
||||||
|
// pd_adc_full_end = false;
|
||||||
|
// main_timer_stop();
|
||||||
|
// printf("main_loop_END\r\n");
|
||||||
|
|
||||||
|
// if(cmd_type_t == CMD_BLE) {
|
||||||
|
// full_send_timer_start();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// For Motion Data Sampling
|
||||||
|
|
||||||
|
/* ---- IMU 모션 데이터 읽기 ---- */
|
||||||
|
/*
|
||||||
|
* motion_raw_data_enabled가 true이면 IMU(ICM42670P) 데이터를 읽는다.
|
||||||
|
*
|
||||||
|
* motion_data_once == true:
|
||||||
|
* 단발성 읽기 모드. HW I2C를 초기화한 후 icm42670_main()을 1회 호출.
|
||||||
|
* info4 모드에서 배터리/온도 측정 후 다시 IMU로 돌아올 때 사용.
|
||||||
|
*
|
||||||
|
* motion_data_once == false:
|
||||||
|
* 연속 읽기 모드. BLE 전송 대기 중(ble_got_new_data==false)이면
|
||||||
|
* icm42670_main()을 호출하고 10ms 후 타이머를 재시작하여 반복 실행.
|
||||||
|
*/
|
||||||
|
if(motion_raw_data_enabled == true) {
|
||||||
|
main_timer_stop(); /* 타이머 정지 (재진입 방지) */
|
||||||
|
if(motion_data_once == true)
|
||||||
|
{
|
||||||
|
/* 단발성 모드: HW I2C 초기화 후 IMU 데이터 1회 읽기 */
|
||||||
|
hw_i2c_init_once(); /* HW TWI 모드로 전환 (400kHz) */
|
||||||
|
icm42670_main(); /* IMU 데이터 읽기 및 처리 */
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
/* 연속 모드: BLE 전송 대기 중이 아니면 반복 읽기 */
|
||||||
|
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 읽기 */
|
||||||
|
}
|
||||||
|
// else if(ble_got_new_data==true){
|
||||||
|
// motion_data_once = true;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---- 배터리 전압 측정 ---- */
|
||||||
|
/*
|
||||||
|
* go_batt 플래그가 true이면 배터리 레벨을 측정한다.
|
||||||
|
* info4 모드에서 IMU 연속 읽기 이후 호출되는 단계.
|
||||||
|
* 측정 완료 후 타이머가 정지된 상태로 유지된다.
|
||||||
|
*/
|
||||||
|
if(go_batt == true) {
|
||||||
|
DBG_PRINTF("IMU BATT\r\n");
|
||||||
|
main_timer_stop(); /* 타이머 정지 */
|
||||||
|
go_batt = false; /* 플래그 소비 (1회 실행) */
|
||||||
|
// go_temp = true;
|
||||||
|
battery_level_meas(); /* SAADC를 이용한 배터리 전압 측정 */
|
||||||
|
// nrf_delay_ms(20);
|
||||||
|
// m48_adc_start_init();
|
||||||
|
// main_timer_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---- 온도 측정 ---- */
|
||||||
|
/*
|
||||||
|
* go_temp 플래그가 true이면 TMP235-Q1 센서로 온도를 측정한다.
|
||||||
|
* info4 모드에서 배터리 측정 이후 호출되는 단계.
|
||||||
|
* 측정 완료 후 motion_data_once=true로 설정하여
|
||||||
|
* 다음 IMU 읽기는 단발성 모드(HW I2C 재초기화)로 전환된다.
|
||||||
|
*/
|
||||||
|
if(go_temp == true) {
|
||||||
|
DBG_PRINTF("IMU Temp\r\n");
|
||||||
|
main_timer_stop(); /* 타이머 정지 */
|
||||||
|
// go_batt = false;
|
||||||
|
go_temp = false; /* 플래그 소비 (1회 실행) */
|
||||||
|
motion_data_once = true; /* 다음 IMU 읽기를 단발성 모드로 전환 */
|
||||||
|
tmp235_voltage_level_meas(); /* TMP235-Q1 온도 센서 전압 측정 */
|
||||||
|
// motion_raw_data_enabled = true;
|
||||||
|
// main_timer_start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 시스템 제어 이벤트 처리 ---- */
|
||||||
|
|
||||||
|
/* 디바이스 전원 OFF 처리 */
|
||||||
|
if(go_device_power_off == true){
|
||||||
|
main_timer_stop(); /* 타이머 정지 */
|
||||||
|
DBG_PRINTF("Off main_timer\r\n");
|
||||||
|
device_power_off(); /* 디바이스 전원 OFF 실행 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 슬립 모드 진입 처리 */
|
||||||
|
if(go_sleep_mode_enter == true){
|
||||||
|
main_timer_stop(); /* 타이머 정지 */
|
||||||
|
DBG_PRINTF("sleep main timer\r\n");
|
||||||
|
sleep_mode_enter(); /* 슬립 모드 진입 실행 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NVIC 시스템 리셋 처리 */
|
||||||
|
if(go_NVIC_SystemReset == true) {
|
||||||
|
main_timer_stop(); /* 타이머 정지 */
|
||||||
|
NVIC_SystemReset(); /* ARM Cortex-M4 시스템 리셋 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 메인 루프 타이머 시작
|
||||||
|
*
|
||||||
|
* 싱글샷 모드로 MAIN_LOOP_INTERVAL(10ms 또는 80ms) 후 main_loop()를 호출
|
||||||
|
*/
|
||||||
|
void main_timer_start(void)
|
||||||
|
{
|
||||||
|
APP_ERROR_CHECK(app_timer_start(m_main_loop_timer_id, APP_TIMER_TICKS(MAIN_LOOP_INTERVAL), NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 지정된 간격(ms)으로 메인 루프 타이머 시작
|
||||||
|
*
|
||||||
|
* IMU 연속 스트리밍 등 기본 간격과 다른 주기가 필요할 때 사용
|
||||||
|
*/
|
||||||
|
void main_timer_start_ms(uint32_t interval_ms)
|
||||||
|
{
|
||||||
|
APP_ERROR_CHECK(app_timer_start(m_main_loop_timer_id, APP_TIMER_TICKS(interval_ms), NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 메인 루프 타이머 정지
|
||||||
|
*/
|
||||||
|
void main_timer_stop(void)
|
||||||
|
{
|
||||||
|
APP_ERROR_CHECK(app_timer_stop(m_main_loop_timer_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 메인 루프 타이머 초기화 (앱 시작 시 1회 호출)
|
||||||
|
*
|
||||||
|
* 싱글샷 모드 타이머를 생성하고, 콜백으로 main_loop()를 등록
|
||||||
|
* 싱글샷이므로 매 호출마다 main_timer_start()로 수동 재시작해야 함
|
||||||
|
*/
|
||||||
|
void main_timer_init(void)
|
||||||
|
{
|
||||||
|
APP_ERROR_CHECK(app_timer_create(&m_main_loop_timer_id, APP_TIMER_MODE_SINGLE_SHOT, main_loop));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file timer_routine.h
|
||||||
|
* @author CandyPops Co.
|
||||||
|
* @version V1.0.0
|
||||||
|
* @date 2022-09-05
|
||||||
|
* @brief
|
||||||
|
*******************************************************************************
|
||||||
|
*
|
||||||
|
* [헤더 개요]
|
||||||
|
* 메인 이벤트 루프 타이머의 공용 인터페이스 헤더.
|
||||||
|
*
|
||||||
|
* 10ms(일반) 또는 80ms(디테일) 간격의 싱글샷 타이머를 사용하여
|
||||||
|
* main_loop() 콜백에서 센서 데이터 수집 및 시스템 제어를 수행한다.
|
||||||
|
*
|
||||||
|
* [주요 함수]
|
||||||
|
* main_timer_start() : 타이머 시작 (싱글샷, 수동 재시작 필요)
|
||||||
|
* main_timer_stop() : 타이머 정지
|
||||||
|
* main_timer_init() : 타이머 초기화 (앱 시작 시 1회 호출)
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef TIMER_ROUTINE_H__
|
||||||
|
#define TIMER_ROUTINE_H__
|
||||||
|
|
||||||
|
/** @brief 메인 루프 타이머 시작 (싱글샷, MAIN_LOOP_INTERVAL 후 main_loop 호출) */
|
||||||
|
void main_timer_start(void);
|
||||||
|
|
||||||
|
/** @brief 지정된 간격(ms)으로 메인 루프 타이머 시작 */
|
||||||
|
void main_timer_start_ms(uint32_t interval_ms);
|
||||||
|
|
||||||
|
/** @brief 메인 루프 타이머 정지 */
|
||||||
|
void main_timer_stop(void);
|
||||||
|
|
||||||
|
/** @brief 메인 루프 타이머 초기화 (앱 시작 시 1회, 싱글샷 모드로 생성) */
|
||||||
|
void main_timer_init(void);
|
||||||
|
|
||||||
|
#endif //TIMER_ROUTINE_H__
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* battery_saadc.c - Battery voltage ADC measurement
|
|
||||||
*
|
|
||||||
* Measures battery voltage via nRF52840 SAADC on AIN2:
|
|
||||||
* - 12-bit resolution, 4x oversampling
|
|
||||||
* - Periodic safety check via battery_loop timer (60 s interval)
|
|
||||||
* - Sequential: battery -> temperature measurement
|
|
||||||
* - Auto power-off after 5 consecutive readings below 3500 mV or above 40 C
|
|
||||||
* - In info4 mode (bulk sensor collection): stores to info_batt
|
|
||||||
*
|
|
||||||
* Voltage conversion:
|
|
||||||
* mV = ADC_VALUE * (600 / 4095) * 6 * 1.42 (resistor divider correction)
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
#include "sdk_common.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "nrf.h"
|
|
||||||
#include "boards.h"
|
|
||||||
#include "app_error.h"
|
|
||||||
#include "nrf_drv_saadc.h"
|
|
||||||
#include "nrf_drv_timer.h"
|
|
||||||
#include "ble_nus.h"
|
|
||||||
#include "nrf_log.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "app_timer.h"
|
|
||||||
#include "battery_saadc.h"
|
|
||||||
#include "main_timer.h"
|
|
||||||
#include "tmp235_q1.h"
|
|
||||||
#include "dr_piezo.h"
|
|
||||||
#include "debug_print.h"
|
|
||||||
|
|
||||||
/* SAADC internal reference voltage (mV, float) */
|
|
||||||
#define BATTERY_REF_VOLTAGE_IN_MILLIVOLTS 600.0f
|
|
||||||
|
|
||||||
/* 1/3 prescaling compensation (input divided by 3, then x2 = total x6) */
|
|
||||||
#define BATTERY_PRE_SCALING_COMPENSATION 6.0f
|
|
||||||
|
|
||||||
/* 12-bit ADC maximum digital value */
|
|
||||||
#define BATTERY_ADC_RES_12BITS 4095.0f
|
|
||||||
|
|
||||||
/* 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)
|
|
||||||
|
|
||||||
/* Single ADC buffer (uninit after each measurement, no double-buffer needed) */
|
|
||||||
static nrf_saadc_value_t adc_buf;
|
|
||||||
|
|
||||||
/* Battery monitoring repeat timer */
|
|
||||||
APP_TIMER_DEF(m_battery_loop_timer_id);
|
|
||||||
|
|
||||||
/* Battery monitoring interval (ms) */
|
|
||||||
#define BATTERY_LOOP_INTERVAL 60000
|
|
||||||
|
|
||||||
/* Safety check consecutive count threshold */
|
|
||||||
#define SAFETY_CHECK_COUNT 5
|
|
||||||
|
|
||||||
/* Low-battery check flag — set by battery_loop, consumed by handler */
|
|
||||||
bool low_battery_check = false;
|
|
||||||
|
|
||||||
/* Safety check mode flag — set by battery handler, consumed by tmp235 handler */
|
|
||||||
bool safety_check_mode = false;
|
|
||||||
|
|
||||||
/* SAADC callback completion flag — used by all_sensors() to wait */
|
|
||||||
volatile bool battery_saadc_done = false;
|
|
||||||
|
|
||||||
/* Safety check: cached battery voltage for use in safety_check_complete() */
|
|
||||||
static float safety_batt_mv = 0;
|
|
||||||
|
|
||||||
/* Safety check: consecutive counters */
|
|
||||||
static uint8_t low_battery_cnt = 0;
|
|
||||||
static uint8_t over_temp_cnt = 0;
|
|
||||||
|
|
||||||
/* info4: bulk sensor collection mode flag */
|
|
||||||
extern bool info4;
|
|
||||||
|
|
||||||
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
|
||||||
extern bool go_device_power_off;
|
|
||||||
extern which_cmd_t cmd_type_t;
|
|
||||||
extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN];
|
|
||||||
|
|
||||||
/* info4 mode: cached battery voltage (mV) */
|
|
||||||
volatile uint16_t info_batt;
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* safety_check_complete - Called by tmp235 handler after temperature measurement
|
|
||||||
*
|
|
||||||
* Checks both battery voltage and temperature against thresholds.
|
|
||||||
* 5 consecutive readings exceeding either threshold triggers power OFF.
|
|
||||||
*============================================================================*/
|
|
||||||
void safety_check_complete(float temp_c)
|
|
||||||
{
|
|
||||||
//DBG_PRINTF("[SAFETY] Batt=%d mV, Temp=%d.%d C\r\n",
|
|
||||||
// (int)safety_batt_mv, (int)temp_c, ((int)(temp_c * 10)) % 10);
|
|
||||||
|
|
||||||
/* Battery check */
|
|
||||||
if (safety_batt_mv <= LOW_BATTERY_VOLTAGE)
|
|
||||||
{
|
|
||||||
low_battery_cnt++;
|
|
||||||
DBG_PRINTF("[SAFETY] Low batt cnt=%d\r\n", low_battery_cnt);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
low_battery_cnt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Temperature check */
|
|
||||||
if (temp_c >= OVER_TEMPERATURE_THRESHOLD)
|
|
||||||
{
|
|
||||||
over_temp_cnt++;
|
|
||||||
DBG_PRINTF("[SAFETY] Over temp cnt=%d\r\n", over_temp_cnt);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
over_temp_cnt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Power OFF if either threshold exceeded 5 consecutive times */
|
|
||||||
if (low_battery_cnt >= SAFETY_CHECK_COUNT)
|
|
||||||
{
|
|
||||||
low_battery_cnt = 0;
|
|
||||||
DBG_PRINTF("[SAFETY] Low battery -> Power OFF\r\n");
|
|
||||||
go_device_power_off = true;
|
|
||||||
main_timer_start();
|
|
||||||
}
|
|
||||||
else if (over_temp_cnt >= SAFETY_CHECK_COUNT)
|
|
||||||
{
|
|
||||||
over_temp_cnt = 0;
|
|
||||||
DBG_PRINTF("[SAFETY] Over temperature -> Power OFF\r\n");
|
|
||||||
go_device_power_off = true;
|
|
||||||
main_timer_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
dr_piezo_power_off();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* battery_event_handler - SAADC conversion complete callback
|
|
||||||
*
|
|
||||||
* Converts the raw ADC value to battery voltage (mV) and then:
|
|
||||||
* - Low-battery check mode: store voltage, chain temperature measurement
|
|
||||||
* - 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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
register_val = p_event->data.done.p_buffer[0];
|
|
||||||
|
|
||||||
/* Release SAADC — shared with temperature / pressure ADC */
|
|
||||||
nrf_drv_saadc_channel_uninit(0);
|
|
||||||
nrf_drv_saadc_uninit();
|
|
||||||
|
|
||||||
battery_saadc_done = true;
|
|
||||||
|
|
||||||
/* ADC -> mV conversion */
|
|
||||||
batt_lvl_in_milli_volt_0 = BATTERY_RESULT_IN_MILLI_VOLTS(register_val);
|
|
||||||
|
|
||||||
/* Resistor divider correction factor 1.42 */
|
|
||||||
batt_lvl_in_milli_volt_1 = batt_lvl_in_milli_volt_0 * 1.42f;
|
|
||||||
|
|
||||||
/* --- Safety check mode: store voltage, chain temperature measurement --- */
|
|
||||||
if (low_battery_check == true)
|
|
||||||
{
|
|
||||||
low_battery_check = false;
|
|
||||||
safety_batt_mv = batt_lvl_in_milli_volt_1;
|
|
||||||
safety_check_mode = true;
|
|
||||||
|
|
||||||
/* TMP235 shares piezo TX/RX power rail */
|
|
||||||
if (!dr_piezo_is_power_on())
|
|
||||||
{
|
|
||||||
dr_piezo_power_on();
|
|
||||||
}
|
|
||||||
tmp235_voltage_level_meas();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- 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 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* battery_configure - Set up SAADC for battery voltage measurement
|
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
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 */
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1);
|
|
||||||
APP_ERROR_CHECK(err_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* battery_level_meas - Start a single battery voltage measurement
|
|
||||||
*
|
|
||||||
* Configures SAADC and triggers sampling. Result arrives asynchronously
|
|
||||||
* via battery_event_handler.
|
|
||||||
*============================================================================*/
|
|
||||||
void battery_level_meas(void)
|
|
||||||
{
|
|
||||||
ret_code_t err_code;
|
|
||||||
|
|
||||||
battery_configure();
|
|
||||||
err_code = nrf_drv_saadc_sample();
|
|
||||||
APP_ERROR_CHECK(err_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* battery_loop - Periodic battery monitoring timer callback
|
|
||||||
*
|
|
||||||
* Sets the low-battery check flag and starts a measurement.
|
|
||||||
* Skips if info4 mode is active (SAADC conflict).
|
|
||||||
*============================================================================*/
|
|
||||||
void battery_loop(void * p_context)
|
|
||||||
{
|
|
||||||
UNUSED_PARAMETER(p_context);
|
|
||||||
|
|
||||||
if (info4 == true)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
low_battery_check = true;
|
|
||||||
battery_level_meas();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop the battery monitoring timer. */
|
|
||||||
void battery_timer_stop(void)
|
|
||||||
{
|
|
||||||
APP_ERROR_CHECK(app_timer_stop(m_battery_loop_timer_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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));
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* battery_saadc.h - Battery voltage SAADC measurement interface
|
|
||||||
*
|
|
||||||
* Uses the nRF52840 SAADC to measure battery voltage on AIN2.
|
|
||||||
*
|
|
||||||
* API:
|
|
||||||
* battery_level_meas() : one-shot measurement (async, result via callback)
|
|
||||||
* battery_timer_init/start/stop() : 60-second periodic monitoring timer
|
|
||||||
*
|
|
||||||
* Periodic safety check (every 60 s):
|
|
||||||
* Battery -> Temperature sequential measurement via SAADC.
|
|
||||||
* Auto power-off after 5 consecutive readings below LOW_BATTERY_VOLTAGE (3500 mV)
|
|
||||||
* or above OVER_TEMPERATURE_THRESHOLD (40 C).
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
#ifndef _BATTERY_SAADC_H_
|
|
||||||
#define _BATTERY_SAADC_H_
|
|
||||||
|
|
||||||
/* Low-battery threshold (mV) — 5 consecutive readings below this -> power OFF */
|
|
||||||
#define LOW_BATTERY_VOLTAGE 3500
|
|
||||||
|
|
||||||
/* Over-temperature threshold (deg C) — 5 consecutive readings above this -> power OFF */
|
|
||||||
#define OVER_TEMPERATURE_THRESHOLD 40.0f
|
|
||||||
|
|
||||||
/* SAADC callback completion flag (used by all_sensors() to wait) */
|
|
||||||
extern volatile bool battery_saadc_done;
|
|
||||||
|
|
||||||
/* Safety check mode flag — set by battery_loop, consumed by tmp235 handler */
|
|
||||||
extern bool safety_check_mode;
|
|
||||||
|
|
||||||
/* Called by tmp235 handler when safety check temperature measurement completes */
|
|
||||||
void safety_check_complete(float temp_c);
|
|
||||||
|
|
||||||
/* Start a single async battery measurement. Result handled in callback. */
|
|
||||||
void battery_level_meas(void);
|
|
||||||
/* Start the 60-second periodic battery monitoring timer. */
|
|
||||||
void battery_timer_start(void);
|
|
||||||
/* Stop the battery monitoring timer. */
|
|
||||||
void battery_timer_stop(void);
|
|
||||||
/* Initialise the battery monitoring timer (call once at app start). */
|
|
||||||
void battery_timer_init(void);
|
|
||||||
|
|
||||||
#endif //_BATTERY_SAADC_H_
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* @file app_raw.h
|
|
||||||
* @author CandyPops Co.
|
|
||||||
* @version V1.0.0
|
|
||||||
* @date 2022-09-05
|
|
||||||
* @brief
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* [Header overview] ICM42670P IMU driver application layer declarations
|
|
||||||
*
|
|
||||||
* Function prototypes and operating mode configuration macros for
|
|
||||||
* IMU sensor initialization, configuration, and data reading.
|
|
||||||
*
|
|
||||||
* 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_
|
|
||||||
#define _APP_RAW_H_
|
|
||||||
#include "sdk_config.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "inv_imu_transport.h"
|
|
||||||
#include "inv_imu_defs.h"
|
|
||||||
#include "inv_imu_driver.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*** Configuration macros ***/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* MCU-IMU communication interface selection
|
|
||||||
* UI_I2C: use I2C communication (default)
|
|
||||||
*/
|
|
||||||
#define SERIF_TYPE UI_I2C
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 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
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Data read method selection
|
|
||||||
* 0: Direct register read (currently in use)
|
|
||||||
* 1: Read from FIFO
|
|
||||||
*/
|
|
||||||
#define USE_FIFO 0
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Resets and initializes the IMU device. Includes WHOAMI verification.
|
|
||||||
* Must complete successfully before calling any other IMU access functions.
|
|
||||||
*
|
|
||||||
* \return 0=success, negative=error
|
|
||||||
*/
|
|
||||||
int setup_imu_device(struct inv_imu_serif *icm_serif);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \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 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 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 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);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* !_APP_RAW_H_ */
|
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* @file app_raw_main.c
|
|
||||||
* @author CandyPops Co.
|
|
||||||
* @version V1.0.0
|
|
||||||
* @date 2022-09-05
|
|
||||||
* @brief
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* 2026.03.26 jhChun
|
|
||||||
* 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.
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* [Module overview] ICM42670P main initialization and polling loop
|
|
||||||
*
|
|
||||||
* Handles the full initialization sequence and main loop for the ICM42670P
|
|
||||||
* IMU sensor.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* 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"
|
|
||||||
#include "app_raw_main.h"
|
|
||||||
#include "RingBuffer.h"
|
|
||||||
#include "inv_imu_driver.h"
|
|
||||||
|
|
||||||
#include "system_interface.h"
|
|
||||||
|
|
||||||
/* std */
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "nrf.h"
|
|
||||||
#include "app_error.h"
|
|
||||||
#include "boards.h"
|
|
||||||
#include "nrfx_gpiote.h"
|
|
||||||
#include "nrf_delay.h"
|
|
||||||
|
|
||||||
#include "app_util_platform.h"
|
|
||||||
#include "main.h" /* 2026-03-17: removed cmd_parse.h, using main.h */
|
|
||||||
#include "i2c_manager.h"
|
|
||||||
/* --------------------------------------------------------------------------------------
|
|
||||||
* Global variables
|
|
||||||
* -------------------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------
|
|
||||||
* Static variables
|
|
||||||
* -------------------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------
|
|
||||||
* Forward declaration
|
|
||||||
* -------------------------------------------------------------------------------------- */
|
|
||||||
static int setup_mcu(struct inv_imu_serif *icm_serif);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief Sensor general interrupt handler, calls specific handlers.
|
|
||||||
*
|
|
||||||
* This function is called when an external interrupt is triggered by the sensor,
|
|
||||||
* checks interrupt registers of InvenSense Sensor to determine the source and type of interrupt
|
|
||||||
* and calls the specific interrupt handler accordingly.
|
|
||||||
*
|
|
||||||
* @param[in] NULL
|
|
||||||
*
|
|
||||||
* @param[out] NULL
|
|
||||||
*
|
|
||||||
* @return NULL
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* inv_gpio_sensor_interrupt_handler()
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
irq_from_device = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* inv_gpio_sensor_irq_init()
|
|
||||||
* Initializes INT1 (P1.13) GPIO interrupt.
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/* Initialize GPIOTE module (skip if already initialized) */
|
|
||||||
if (!nrfx_gpiote_is_init())
|
|
||||||
{
|
|
||||||
err_code = nrfx_gpiote_init();
|
|
||||||
APP_ERROR_CHECK(err_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
/* 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()
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
/* Release INT1 pin interrupt configuration */
|
|
||||||
nrfx_gpiote_in_uninit(ICM42670_INT1_PIN);
|
|
||||||
|
|
||||||
/* Release GPIOTE module (only if initialized) */
|
|
||||||
if (nrfx_gpiote_is_init())
|
|
||||||
{
|
|
||||||
nrfx_gpiote_uninit();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------
|
|
||||||
* Main
|
|
||||||
* -------------------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* icm42670_init()
|
|
||||||
* Performs the full ICM42670P initialization sequence.
|
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
*
|
|
||||||
* Returns: 0=success, -1=initialization failure
|
|
||||||
*/
|
|
||||||
int icm42670_init(void)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
struct inv_imu_serif icm_serif;
|
|
||||||
|
|
||||||
rc |= setup_mcu(&icm_serif);
|
|
||||||
rc |= setup_imu_device(&icm_serif);
|
|
||||||
rc |= configure_imu_device();
|
|
||||||
|
|
||||||
if(rc != 0){
|
|
||||||
printf("!!!error during initialization\r\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enable INT1 interrupt after successful init — ISR fires on data ready */
|
|
||||||
inv_gpio_sensor_irq_init();
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* icm42670_main()
|
|
||||||
* ICM42670P main polling loop.
|
|
||||||
* Must be called periodically from the main application loop.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* 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();
|
|
||||||
|
|
||||||
if(rc < 0) {
|
|
||||||
printf("error while getting data\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear flag — wait for next interrupt */
|
|
||||||
irq_from_device = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------
|
|
||||||
* Functions definitions
|
|
||||||
* -------------------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* setup_mcu()
|
|
||||||
* Configures the MCU-side serial interface.
|
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/* 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) */
|
|
||||||
|
|
||||||
/* Initialize TWI hardware */
|
|
||||||
rc |= inv_io_hal_init(icm_serif);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------
|
|
||||||
* Extern functions definition
|
|
||||||
* -------------------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* inv_imu_sleep_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)
|
|
||||||
{
|
|
||||||
nrf_delay_us(us);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* inv_imu_get_time_us()
|
|
||||||
* Timestamp function used by the IMU driver.
|
|
||||||
* Returns the nRF52840 RTC1 counter value.
|
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
return NRF_RTC1->COUNTER;
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* @file app_raw_main.h
|
|
||||||
* @author CandyPops Co.
|
|
||||||
* @version V1.0.0
|
|
||||||
* @date 2022-09-05
|
|
||||||
* @brief
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* [Header overview] ICM42670P main initialization/polling loop declarations
|
|
||||||
*
|
|
||||||
* 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 full init — MCU I2C config -> IMU driver init -> sensor config -> enable IRQ */
|
|
||||||
int icm42670_init(void);
|
|
||||||
|
|
||||||
/* ICM42670P main polling loop — check INT1 interrupt flag, then read sensor data */
|
|
||||||
void icm42670_main(void);
|
|
||||||
|
|
||||||
/* ICM42670P release (prototype declaration) */
|
|
||||||
int icm42670_uninit(void);
|
|
||||||
|
|
||||||
#endif /* !_APP_RAW_MAIN_H_ */
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* dr_piezo.h - Piezo Transducer Driver (2 MHz Signal Generator)
|
|
||||||
*
|
|
||||||
* Hardware: nRF52840 + MD1822K6-G MOSFET Driver + TC7920K6-G MOSFET
|
|
||||||
* Output: +/-20V at 2 MHz, 3..7 cycles burst
|
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "nrf_gpio.h"
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* Power control pin (+/-20V DC/DC converter)
|
|
||||||
*============================================================================*/
|
|
||||||
#define DR_PIEZO_PWR_EN NRF_GPIO_PIN_MAP(1, 9)
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* 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 */
|
|
||||||
#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 control pins (echo signal path selection, 8 channels)
|
|
||||||
* MUXA handles CH0..CH3, MUXB handles CH4..CH7.
|
|
||||||
* Only one MUX is enabled at a time.
|
|
||||||
*============================================================================*/
|
|
||||||
#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 */
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* Configuration
|
|
||||||
*============================================================================*/
|
|
||||||
#define DR_PIEZO_FREQ_HZ 2100000 /**< Target frequency (set PIEZO_FREQ_MHZ in .c) */
|
|
||||||
#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) */
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* Power control
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
void dr_piezo_power_on(void);
|
|
||||||
void dr_piezo_power_off(void);
|
|
||||||
|
|
||||||
/** @return true if power is ON */
|
|
||||||
bool dr_piezo_is_power_on(void);
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* TX driver
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
void dr_piezo_init(void);
|
|
||||||
void dr_piezo_uninit(void);
|
|
||||||
void dr_piezo_burst(uint8_t cycles);
|
|
||||||
void dr_piezo_pulse(void);
|
|
||||||
void dr_piezo_enable(void);
|
|
||||||
void dr_piezo_disable(void);
|
|
||||||
bool dr_piezo_is_busy(void);
|
|
||||||
void dr_piezo_set_frequency(uint32_t freq_hz);
|
|
||||||
void dr_piezo_test_pins(void);
|
|
||||||
void dr_piezo_mux_init(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* System functions (power + TX combined)
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
void dr_piezo_system_init(void);
|
|
||||||
void dr_piezo_system_uninit(void);
|
|
||||||
void dr_piezo_transmit(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):
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
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 */
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* tmp235_q1.c - TMP235-Q1 analogue temperature sensor driver
|
|
||||||
*
|
|
||||||
* Reads the TMP235-Q1 analogue output via SAADC AIN3 and converts to deg C.
|
|
||||||
*
|
|
||||||
* 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"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "nrf.h"
|
|
||||||
#include "boards.h"
|
|
||||||
#include "app_error.h"
|
|
||||||
#include "nrf_drv_saadc.h"
|
|
||||||
#include "ble_nus.h"
|
|
||||||
#include "tmp235_q1.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "main_timer.h"
|
|
||||||
#include "battery_saadc.h"
|
|
||||||
#include "debug_print.h"
|
|
||||||
|
|
||||||
/* 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)
|
|
||||||
|
|
||||||
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 which_cmd_t cmd_type_t;
|
|
||||||
extern bool info4;
|
|
||||||
extern bool go_temp;
|
|
||||||
|
|
||||||
/* info4 mode: cached temperature (deg C x 100, integer) */
|
|
||||||
volatile uint16_t info_temp;
|
|
||||||
extern bool motion_raw_data_enabled;
|
|
||||||
|
|
||||||
/* SAADC completion flag — used by all_sensors() to wait */
|
|
||||||
volatile bool tmp235_saadc_done = false;
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* tmp235_voltage_handler - SAADC conversion complete callback
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
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;
|
|
||||||
|
|
||||||
adc_result = p_event->data.done.p_buffer[0];
|
|
||||||
|
|
||||||
/* Release SAADC — shared with battery / pressure ADC */
|
|
||||||
nrf_drv_saadc_channel_uninit(0);
|
|
||||||
nrf_drv_saadc_uninit();
|
|
||||||
|
|
||||||
/* ADC -> TMP235 output voltage (mV) */
|
|
||||||
tmp235_voltage_in_milli_volts = TMP235_VOUT_IN_MILLI_VOLTS(adc_result);
|
|
||||||
|
|
||||||
/* 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Safety check mode: pass temperature to battery module for judgment --- */
|
|
||||||
if (safety_check_mode == true)
|
|
||||||
{
|
|
||||||
safety_check_mode = false;
|
|
||||||
safety_check_complete(led_temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- info4 mode: store value for mbb? bulk response --- */
|
|
||||||
else if (info4 == true)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp235_saadc_done = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* tmp235_init - Initialise SAADC for TMP235 and start measurement
|
|
||||||
*
|
|
||||||
* AIN3, single-ended, 12-bit, 4x oversampling, burst enabled.
|
|
||||||
* Triggers sampling immediately; result arrives via tmp235_voltage_handler.
|
|
||||||
*============================================================================*/
|
|
||||||
void tmp235_init(void)
|
|
||||||
{
|
|
||||||
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_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);
|
|
||||||
|
|
||||||
err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1);
|
|
||||||
APP_ERROR_CHECK(err_code);
|
|
||||||
|
|
||||||
err_code = nrf_drv_saadc_sample();
|
|
||||||
APP_ERROR_CHECK(err_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*==============================================================================
|
|
||||||
* tmp235_voltage_level_meas - External entry point for one-shot reading
|
|
||||||
*
|
|
||||||
* Calls tmp235_init() which both initialises and triggers sampling.
|
|
||||||
*============================================================================*/
|
|
||||||
void tmp235_voltage_level_meas(void)
|
|
||||||
{
|
|
||||||
tmp235_init();
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
/*==============================================================================
|
|
||||||
* tmp235_q1.h - TMP235-Q1 analogue temperature sensor driver interface
|
|
||||||
*
|
|
||||||
* Reads the TMP235-Q1 analogue voltage output via SAADC (AIN3) and converts
|
|
||||||
* it to temperature (deg C).
|
|
||||||
*
|
|
||||||
* Conversion: Ta(C) = (Vout_mV - 500) / 10.0 (valid 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_
|
|
||||||
|
|
||||||
/* Initialise SAADC for TMP235 and start measurement (AIN3). */
|
|
||||||
void tmp235_init(void);
|
|
||||||
/* External entry point for a single temperature reading. */
|
|
||||||
void tmp235_voltage_level_meas(void);
|
|
||||||
|
|
||||||
#endif /* !_TMP235_Q1_H_ */
|
|
||||||
+240
-288
File diff suppressed because it is too large
Load Diff
+63
-103
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user