Initial commit
This commit is contained in:
@@ -0,0 +1,456 @@
|
||||
/*==============================================================================
|
||||
* 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 = 3; /* 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");
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*==============================================================================
|
||||
* 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_ */
|
||||
@@ -0,0 +1,125 @@
|
||||
/*==============================================================================
|
||||
* 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");
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*==============================================================================
|
||||
* 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
|
||||
Reference in New Issue
Block a user