13 changed files with 412 additions and 619 deletions
-
3.vscode/settings.json
-
186app/app.uvoptx
-
20app/app.uvprojx
-
6app/src/board/board.h
-
12app/src/board/board_ecg_sensor.c
-
8app/src/device_state.h
-
108app/src/heart_ware_sample_data_mgr.c
-
9app/src/heart_ware_sample_data_mgr.h
-
107app/src/heart_wave_sample_data_pre_process.c
-
9app/src/heart_wave_sample_data_pre_process.h
-
227app/src/heart_wave_sample_service.c
-
313app/src/heart_wave_sample_service.c.bak
-
23app/src/one_conduction_main.c
@ -0,0 +1,108 @@ |
|||
#include "app_event.h" |
|||
#include "app_event_distribute.h" |
|||
#include "basic/qrs_time_domain_zh.h" |
|||
#include "board/board_ecg_sensor.h" |
|||
#include "heart_wave_sample_service.h" |
|||
#include "nrfx_timer.h" |
|||
|
|||
static uint32_t m_frame_index = 0; // 帧绝对序号 |
|||
/*********************************************************************************************************************** |
|||
* ab buffer * |
|||
***********************************************************************************************************************/ |
|||
#define FRAME_BUFFER_SIZE (256 / sizeof(one_frame_t)) |
|||
|
|||
static one_frame_t m_capture_buffer_a[FRAME_BUFFER_SIZE]; |
|||
static one_frame_t m_capture_buffer_b[FRAME_BUFFER_SIZE]; |
|||
static one_frame_t* m_capture_buffer; |
|||
static one_frame_t m_capture_buffer_index = 0; |
|||
|
|||
static void eeprom_cache_buffer_swap() { |
|||
if (m_capture_buffer == NULL) { |
|||
m_capture_buffer = m_capture_buffer_a; |
|||
m_capture_buffer_index = 0; |
|||
return; |
|||
} |
|||
|
|||
if (m_capture_buffer == m_capture_buffer_a) { |
|||
m_capture_buffer = m_capture_buffer_b; |
|||
} else { |
|||
m_capture_buffer = m_capture_buffer_a; |
|||
} |
|||
m_capture_buffer_index = 0; |
|||
return; |
|||
} |
|||
|
|||
/*********************************************************************************************************************** |
|||
* littlebuffer * |
|||
***********************************************************************************************************************/ |
|||
static one_frame_t m_prepare_data_cache[LITTLE_DATA_BLOCK_FRAME_NUM]; |
|||
static uint32_t m_prepare_data_cache_index; |
|||
|
|||
static inline void preview_data_cache_push_data(one_frame_t data) { |
|||
if (m_prepare_data_cache_index >= LITTLE_DATA_BLOCK_FRAME_NUM) { |
|||
return; |
|||
} |
|||
m_prepare_data_cache[m_prepare_data_cache_index] = data; |
|||
m_prepare_data_cache_index++; |
|||
} |
|||
|
|||
static inline bool preview_data_cache_is_full(void) { |
|||
if (m_prepare_data_cache_index >= LITTLE_DATA_BLOCK_FRAME_NUM) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
static inline void preview_data_cache_clear(void) { m_prepare_data_cache_index = 0; } |
|||
static inline void preview_data_trigger_event() { |
|||
static app_event_t event; |
|||
event.eventType = kevent_capture_little_data_block_event; |
|||
for (uint32_t i = 0; i < LITTLE_DATA_BLOCK_FRAME_NUM; i++) { |
|||
event.val.little_data_block.data[i] = m_prepare_data_cache[i]; |
|||
} |
|||
event.val.little_data_block.frameIndex = m_frame_index - LITTLE_DATA_BLOCK_FRAME_NUM; |
|||
AppEvent_pushEvent(&event); |
|||
} |
|||
|
|||
/*********************************************************************************************************************** |
|||
* EXT * |
|||
***********************************************************************************************************************/ |
|||
|
|||
void hwsd_mgr_push_one_frame(one_frame_t data) { |
|||
m_frame_index++; |
|||
|
|||
/*********************************************************************************************************************** |
|||
* EEPROM数据采样存储 * |
|||
***********************************************************************************************************************/ |
|||
if (m_capture_buffer == NULL) eeprom_cache_buffer_swap(); |
|||
|
|||
if (m_capture_buffer_index < FRAME_BUFFER_SIZE) { |
|||
m_capture_buffer[m_capture_buffer_index++] = data; |
|||
} |
|||
|
|||
if (m_capture_buffer_index == FRAME_BUFFER_SIZE) { |
|||
app_event_t evt; |
|||
evt.eventType = kevent_capture_256data_event; |
|||
evt.val.capture_data_cache = (uint8_t*)m_capture_buffer; |
|||
eeprom_cache_buffer_swap(); |
|||
AppEvent_pushEvent(&evt); |
|||
} |
|||
|
|||
/******************************************************************************* |
|||
* 实时采样数据事件上报 * |
|||
*******************************************************************************/ |
|||
/** |
|||
* @brief 缓存数据,并触发小数据块事件 |
|||
*/ |
|||
preview_data_cache_push_data(data); |
|||
if (preview_data_cache_is_full()) { |
|||
preview_data_trigger_event(); |
|||
preview_data_cache_clear(); |
|||
} |
|||
} |
|||
|
|||
void hwsd_mgr_reset_buffer(){ |
|||
m_frame_index = 0; |
|||
m_capture_buffer = NULL; |
|||
m_capture_buffer_index = 0; |
|||
preview_data_cache_clear(); |
|||
} |
@ -0,0 +1,9 @@ |
|||
#pragma once |
|||
#include <stdint.h> |
|||
|
|||
#include "app_event.h" |
|||
#include "app_event_distribute.h" |
|||
#include "nrfx_timer.h" |
|||
|
|||
void hwsd_mgr_push_one_frame(one_frame_t data); |
|||
void hwsd_mgr_reset_buffer(); |
@ -0,0 +1,107 @@ |
|||
#include "heart_wave_sample_data_pre_process.h" |
|||
|
|||
/*********************************************************************************************************************** |
|||
* ALGO * |
|||
***********************************************************************************************************************/ |
|||
|
|||
typedef struct { |
|||
float value; |
|||
float efectiveFactor; |
|||
} filter_t; |
|||
|
|||
typedef struct { |
|||
float coef[2]; |
|||
float v_out[2]; |
|||
} LPFilter; |
|||
#define PI 3.14159265358979323846f |
|||
|
|||
static float Filter(filter_t* filter, float newInput) { |
|||
float newv = ((float)filter->value * (1.0f - filter->efectiveFactor)) + ((float)newInput * filter->efectiveFactor); |
|||
filter->value = newv; |
|||
return newv; |
|||
} |
|||
|
|||
void LPFilter_Init(LPFilter* filter, float cutoffFreqHz, float sampleTimeS) { |
|||
float RC = 0.0; |
|||
RC = 1.0 / (2 * PI * cutoffFreqHz); |
|||
filter->coef[0] = sampleTimeS / (sampleTimeS + RC); |
|||
filter->coef[1] = RC / (sampleTimeS + RC); |
|||
|
|||
filter->v_out[0] = 0.0; |
|||
filter->v_out[1] = 0.0; |
|||
} |
|||
|
|||
float LPFilter_Update(LPFilter* filter, float v_in) { |
|||
filter->v_out[1] = filter->v_out[0]; |
|||
filter->v_out[0] = (filter->coef[0] * v_in) + (filter->coef[1] * filter->v_out[1]); |
|||
|
|||
return (filter->v_out[0]); |
|||
} |
|||
|
|||
/** |
|||
* @brief 放大显示数据 |
|||
* |
|||
* @param val |
|||
* @param valcener |
|||
* @param amp |
|||
* @return float |
|||
*/ |
|||
static float amp_display_val(uint16_t val, uint16_t valcener, float amp) { |
|||
float valf = (float)val - valcener; |
|||
valf = valf * amp; |
|||
valf += valcener; |
|||
|
|||
if (valf >= 100) { |
|||
valf = 100; |
|||
} |
|||
|
|||
if (valf <= 0) { |
|||
valf = 0; |
|||
} |
|||
return valf; |
|||
} |
|||
|
|||
uint16_t getRecommendedMagnification() { |
|||
// return 0; |
|||
uint16_t max = QRS_getMaxValueLastVal(); |
|||
if (max == 0) { |
|||
return 15; |
|||
} |
|||
// 3750.0f |
|||
if (max <= (3750 / 2)) { |
|||
return 15; |
|||
} |
|||
float af = (3750 / 2) / (max - 3750 / 2); |
|||
if (af > 15) { |
|||
return 15; |
|||
} |
|||
return af; |
|||
} |
|||
|
|||
/*********************************************************************************************************************** |
|||
* VAR * |
|||
***********************************************************************************************************************/ |
|||
static one_frame_t m_datacache; |
|||
static one_frame_t m_displaydata; |
|||
filter_t m_filter = {0, 0.8}; |
|||
LPFilter m_lpfilter_01; |
|||
LPFilter m_lpfilter_02; |
|||
|
|||
void hwsd_pre_processer_init() { QRS_resetBuf(); } |
|||
void hwsd_pre_processer_process(one_frame_t framdata) { // |
|||
m_datacache = framdata; |
|||
m_displaydata = framdata; |
|||
/******************************************************************************* |
|||
* 显示数据计算并赋值 * |
|||
*******************************************************************************/ |
|||
// QRS_getMaxValueLastVal(); |
|||
// float lowpassf_val = LPFilter_Update(&m_lpfilter_01, framdata); |
|||
// QRS_processData(lowpassf_val); |
|||
// float val_af100 = (float)lowpassf_val / 3750.0f * 100; // 参考电压为3.6v,但信号范围为3.3v |
|||
// val_af100 = amp_val(val_af100, 50, getRecommendedMagnification()); |
|||
// val_af100 = LPFilter_Update(&m_lpfilter_02, val_af100); |
|||
// m_displaydata = m_datacache; |
|||
} |
|||
|
|||
uint16_t hwsd_pre_processer_get_display_data() { return m_displaydata; } |
|||
one_frame_t hwsd_pre_processer_get_storage_data() { return m_datacache; } |
@ -0,0 +1,9 @@ |
|||
#pragma once |
|||
#include <stdint.h> |
|||
|
|||
#include "app_event.h" |
|||
|
|||
void hwsd_pre_processer_init(); |
|||
void hwsd_pre_processer_process(one_frame_t framdata); |
|||
one_frame_t hwsd_pre_processer_get_display_data(); |
|||
one_frame_t hwsd_pre_processer_get_storage_data(); |
@ -1,313 +0,0 @@ |
|||
#include "heart_wave_sample_service.h" |
|||
|
|||
#include "app_event.h" |
|||
#include "app_event_distribute.h" |
|||
#include "basic/FIR.h" |
|||
#include "basic/HC_Chen_detect.h" |
|||
#include "basic/So_Chen_detect.h" |
|||
#include "basic/adaptive_algorithm.h" |
|||
#include "basic/Pan_Tompkins_detect.h" |
|||
#include "board/board_ecg_sensor.h" |
|||
#include "nrfx_timer.h" |
|||
|
|||
|
|||
static uint16_t m_capture_buffer_a[128]; |
|||
static uint16_t m_capture_buffer_b[128]; |
|||
|
|||
static uint16_t* m_capture_buffer; |
|||
static uint16_t m_capture_buffer_index = 0; |
|||
|
|||
volatile static float m_sensor_display_data = 0; // 0->100 |
|||
static uint32_t m_start_capture_tp; |
|||
static uint32_t m_frame_index = 0; |
|||
|
|||
static one_frame_data_t m_sensor_little_frame_cache[LITTLE_DATA_BLOCK_FRAME_NUM]; |
|||
static uint32_t m_little_frame_index; |
|||
static bool m_prestart_flag; |
|||
|
|||
static nrfx_timer_t m_timer = NRFX_TIMER_INSTANCE(HEART_WAVE_SAMPLE_TMR_INSTANCE); |
|||
|
|||
typedef struct { |
|||
int heartSignalCnt; |
|||
int heart_rate; |
|||
uint32_t last_qrs_point; |
|||
uint32_t time_cnt; |
|||
uint32_t origin_time_cnt; |
|||
|
|||
uint32_t index; |
|||
float index_f; |
|||
} QRS_t; |
|||
|
|||
QRS_t m_qrs; |
|||
|
|||
void QRS_reset() { |
|||
m_qrs.heartSignalCnt = 0; |
|||
m_qrs.heart_rate = 0; |
|||
m_qrs.time_cnt = 0; |
|||
m_qrs.index = 0; |
|||
m_qrs.index_f = 0; |
|||
FIR_reset_buffer(); |
|||
} |
|||
|
|||
void QRS_process(float value) { |
|||
bool isPeak = false; |
|||
#if 1 |
|||
float result = value; |
|||
SignalPoint sp; |
|||
sp.value = result; |
|||
sp.index = m_qrs.time_cnt; |
|||
SignalPoint peak = So_Chen_detect(sp, SAMPLING_RATE * 0.25f, 4, 16); |
|||
if (peak.index != -1) { |
|||
isPeak = true; |
|||
} |
|||
#endif |
|||
|
|||
#if 0 |
|||
isPeak = HC_Chen_detect(value); |
|||
#endif |
|||
|
|||
#if 0 |
|||
static const float CV_LIMIT = 50.0f; |
|||
static const float THRESHOLD_FACTOR = 3.0f; |
|||
double mean = CalculateMean(value); |
|||
double rms = CalculateRootMeanSquare(value); |
|||
double cv = CalculateCoefficientOfVariation(value); |
|||
double threshold; |
|||
if (cv > CV_LIMIT) { |
|||
threshold = rms; |
|||
} else { |
|||
threshold = rms * (cv / 100.0f) * THRESHOLD_FACTOR; |
|||
} |
|||
bool is_peak; |
|||
SignalPoint result; |
|||
result = PeakDetect(value, m_qrs.time_cnt, threshold, &is_peak); |
|||
if (result.index != -1) { |
|||
if (is_peak) { |
|||
isPeak = true; |
|||
} |
|||
} |
|||
#endif |
|||
|
|||
#if 0 |
|||
|
|||
|
|||
double result = value; |
|||
double bandpass; |
|||
double integral; |
|||
double square; |
|||
|
|||
bandpass = result; |
|||
result = Derivative(result); |
|||
result = Squar(result); |
|||
square = result; |
|||
result = MovingWindowIntegral(result); |
|||
integral = result; |
|||
|
|||
SignalPoint peak = ThresholdCalculate(m_qrs.time_cnt,value,bandpass,square,integral); |
|||
|
|||
if(peak.index != -1){ |
|||
isPeak = true; |
|||
} |
|||
|
|||
#endif |
|||
|
|||
// if (isPeak) { |
|||
// uint32_t time_diff = m_qrs.time_cnt - m_qrs.last_qrs_point; |
|||
// m_qrs.last_qrs_point = m_qrs.time_cnt; |
|||
// m_qrs.heartSignalCnt++; |
|||
// // m_qrs.heart_rate = m_qrs.heartSignalCnt; |
|||
// if (m_qrs.last_qrs_point != 0) { |
|||
// m_qrs.heart_rate = 60 * (1 / (time_diff * 1.0 / SAMPLING_RATE)); |
|||
// } |
|||
// } |
|||
|
|||
m_qrs.time_cnt++; |
|||
} |
|||
|
|||
static const void compute_heart_rate(float sample_data) { |
|||
ZASSERT(SAMPLE_RATE == 500); |
|||
m_qrs.index_f = m_frame_index / 500.0 * 360; |
|||
if ((m_qrs.index_f - m_qrs.index) > 1) { |
|||
m_qrs.index = m_qrs.index_f; |
|||
float val = sample_data; |
|||
QRS_process(FIR_filter(val)); |
|||
} |
|||
} |
|||
|
|||
static void swap_buffer() { |
|||
if (m_capture_buffer == NULL) { |
|||
m_capture_buffer = m_capture_buffer_a; |
|||
m_capture_buffer_index = 0; |
|||
return; |
|||
} |
|||
|
|||
if (m_capture_buffer == m_capture_buffer_a) { |
|||
m_capture_buffer = m_capture_buffer_b; |
|||
} else { |
|||
m_capture_buffer = m_capture_buffer_a; |
|||
} |
|||
m_capture_buffer_index = 0; |
|||
return; |
|||
} |
|||
static float amp_val(uint16_t val, uint16_t valcener, float amp) { |
|||
float valf = (float)val - valcener; |
|||
valf = valf * amp; |
|||
valf += valcener; |
|||
|
|||
if (valf >= 100) { |
|||
valf = 100; |
|||
} |
|||
|
|||
if (valf <= 0) { |
|||
valf = 0; |
|||
} |
|||
return valf; |
|||
} |
|||
|
|||
typedef struct { |
|||
float value; |
|||
float efectiveFactor; |
|||
} filter_t; |
|||
|
|||
filter_t m_filter = {0, 0.8}; |
|||
|
|||
static float Filter(filter_t* filter, float newInput) { |
|||
float newv = ((float)filter->value * (1.0f - filter->efectiveFactor)) + ((float)newInput * filter->efectiveFactor); |
|||
filter->value = newv; |
|||
return newv; |
|||
} |
|||
|
|||
/******************************************************************************* |
|||
* 小包数据上报 * |
|||
*******************************************************************************/ |
|||
static inline void prvf_little_block_cache_push_one_frame(uint16_t data) { |
|||
if (m_little_frame_index >= LITTLE_DATA_BLOCK_FRAME_NUM) { |
|||
return; |
|||
} |
|||
m_sensor_little_frame_cache[m_little_frame_index].data = data; |
|||
m_little_frame_index++; |
|||
} |
|||
|
|||
static inline bool prvf_light_block_cache_is_full(void) { |
|||
if (m_little_frame_index >= LITTLE_DATA_BLOCK_FRAME_NUM) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
static inline void prvf_light_block_cache_clear(void) { m_little_frame_index = 0; } |
|||
static inline void prvf_light_block_trigger_event() { |
|||
static app_event_t event; |
|||
event.eventType = kevent_capture_little_data_block_event; |
|||
for (uint32_t i = 0; i < LITTLE_DATA_BLOCK_FRAME_NUM; i++) { |
|||
event.val.little_data_block.data[i].data = m_sensor_little_frame_cache[i].data; |
|||
} |
|||
event.val.little_data_block.frameIndex = m_frame_index - LITTLE_DATA_BLOCK_FRAME_NUM; |
|||
// ZLOGI("%d", event.val.little_data_block.frameIndex); |
|||
AppEvent_pushEvent(&event); |
|||
} |
|||
|
|||
void nrfx_timer_event_handler(nrf_timer_event_t event_type, void* p_context) { // |
|||
uint16_t val = BoardEcgSensor_plod_get_ecg_val(); // 12bit |
|||
float val_af100 = (float)val / 4096.0f * 100; |
|||
if (m_prestart_flag) { |
|||
compute_heart_rate(val_af100); |
|||
return; |
|||
} else { |
|||
compute_heart_rate(val_af100); |
|||
} |
|||
|
|||
/******************************************************************************* |
|||
* 显示数据计算并赋值 * |
|||
*******************************************************************************/ |
|||
|
|||
m_frame_index++; |
|||
val_af100 = amp_val(val_af100, 45, 3.5f); |
|||
val_af100 = Filter(&m_filter, val_af100); |
|||
m_sensor_display_data = val_af100; |
|||
|
|||
/******************************************************************************* |
|||
* 采样数据缓存 * |
|||
*******************************************************************************/ |
|||
if (m_capture_buffer == NULL) { |
|||
swap_buffer(); |
|||
} |
|||
|
|||
if (m_capture_buffer_index < 128) { |
|||
m_capture_buffer[m_capture_buffer_index++] = val; |
|||
} |
|||
|
|||
if (m_capture_buffer_index == 128) { |
|||
app_event_t evt; |
|||
evt.eventType = kevent_capture_256data_event; |
|||
evt.val.capture_data_cache = (uint8_t*)m_capture_buffer; |
|||
swap_buffer(); |
|||
AppEvent_pushEvent(&evt); |
|||
} |
|||
|
|||
/******************************************************************************* |
|||
* 实时采样数据事件上报 * |
|||
*******************************************************************************/ |
|||
/** |
|||
* @brief 缓存数据,并触发小数据块事件 |
|||
*/ |
|||
prvf_little_block_cache_push_one_frame(val); |
|||
if (prvf_light_block_cache_is_full()) { |
|||
prvf_light_block_trigger_event(); |
|||
prvf_light_block_cache_clear(); |
|||
} |
|||
} |
|||
|
|||
void hwss_init(void) { |
|||
static bool m_timer_inited = false; |
|||
if (!m_timer_inited) { |
|||
/** |
|||
* @brief 初始化定时器 |
|||
*/ |
|||
static nrfx_timer_config_t timer_cfg = { |
|||
.frequency = NRF_TIMER_FREQ_500kHz, |
|||
.mode = NRF_TIMER_MODE_TIMER, |
|||
.bit_width = NRF_TIMER_BIT_WIDTH_24, |
|||
.p_context = NULL, |
|||
.interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY, |
|||
}; |
|||
|
|||
// nrfx_timer_init(&m_timer, &timer_cfg, nrfx_timer_event_handler); |
|||
ZERROR_CHECK(nrfx_timer_init(&m_timer, &timer_cfg, nrfx_timer_event_handler)); |
|||
uint32_t timer_ticks = nrfx_timer_ms_to_ticks(&m_timer, 2); // |
|||
ZASSERT(SAMPLE_RATE == 500); |
|||
nrfx_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, timer_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true); |
|||
m_timer_inited = true; |
|||
} |
|||
} |
|||
void hwss_uninit(void) { nrfx_timer_disable(&m_timer); } |
|||
|
|||
void hwss_pre_start_capture(void) { |
|||
m_start_capture_tp = znordic_getpower_on_s(); |
|||
swap_buffer(); |
|||
m_frame_index = 0; |
|||
|
|||
QRS_reset(); |
|||
|
|||
prvf_light_block_cache_clear(); |
|||
nrfx_timer_enable(&m_timer); |
|||
m_prestart_flag = true; |
|||
} |
|||
|
|||
void hwss_start_capture(void) { m_prestart_flag = false; } |
|||
void hwss_stop_capture(void) { |
|||
nrfx_timer_disable(&m_timer); |
|||
m_frame_index = 0; |
|||
prvf_light_block_cache_clear(); |
|||
} |
|||
|
|||
float hwss_read_val(void) { |
|||
__disable_irq(); |
|||
float val = m_sensor_display_data; |
|||
__enable_irq(); |
|||
return val; |
|||
} |
|||
float hwss_read_heart_rate(void) { // |
|||
return m_qrs.heart_rate; |
|||
} |
|||
|
|||
int hwss_has_captured_time_ms() { return (znordic_getpower_on_s() - m_start_capture_tp) * 1000; } |
Write
Preview
Loading…
Cancel
Save
Reference in new issue