From bc22fa18d6cf6568066fd1f9f62d9d4ba6b4128f Mon Sep 17 00:00:00 2001 From: 1624952103 Date: Tue, 9 Jun 2026 18:53:54 +0800 Subject: [PATCH] =?utf8?q?=E5=BF=83=E7=8E=87=E6=95=B0=E6=8D=AE=E5=8A=A0?= =?utf8?q?=E5=85=A5=E5=B9=B3=E6=BB=91=E5=A4=84=E7=90=86=EF=BC=8C=E5=8A=A0?= =?utf8?q?=E5=85=A5=E6=A8=A1=E6=8B=9F=E6=B4=BB=E4=BD=93=E6=A3=80=E6=B5=8B?= =?utf8?q?=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../nativeui/heartrate/src/HeartModel.cpp | 2 +- code/sdk/zhihui/drivers/ppg/hal/ppg_hal.c | 11 + code/sdk/zhihui/drivers/ppg/hal/ppg_hal.h | 7 + .../ppg/pah831x/pah_sensor/pah831x_main.c | 302 +++++++++++++++++- .../ppg/pah831x/pah_sensor/pah831x_main.h | 4 +- 5 files changed, 314 insertions(+), 12 deletions(-) diff --git a/code/sdk/application/wearable/n9/nativeapp/nativeui/heartrate/src/HeartModel.cpp b/code/sdk/application/wearable/n9/nativeapp/nativeui/heartrate/src/HeartModel.cpp index 60f18f1ea..8362fb760 100644 --- a/code/sdk/application/wearable/n9/nativeapp/nativeui/heartrate/src/HeartModel.cpp +++ b/code/sdk/application/wearable/n9/nativeapp/nativeui/heartrate/src/HeartModel.cpp @@ -575,7 +575,7 @@ void HeartModel::Heart_Result() { thresholdValue = ConfigModel::GetInstance()->get_configData_int( ConfigModel::CONFIGMODEL_ITEM_HEARTRATE_THRESHOLD_VALUE); - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "心率提醒状态:%d,阈值:%d,當前值:%d", + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "心率提醒状态:%d,阈值:%d,当前值:%d", heartRemindState, thresholdValue, currentheart); if (heartRemindState) { diff --git a/code/sdk/zhihui/drivers/ppg/hal/ppg_hal.c b/code/sdk/zhihui/drivers/ppg/hal/ppg_hal.c index a294eec75..ce80a1caf 100644 --- a/code/sdk/zhihui/drivers/ppg/hal/ppg_hal.c +++ b/code/sdk/zhihui/drivers/ppg/hal/ppg_hal.c @@ -159,6 +159,17 @@ bool is_ppg_bp_need_cali(void) return 1; } +uint32_t ppg_read_ecg_raw(int32_t *buf, uint32_t buf_len) +{ +#ifdef PPG_PAH831X + return pah831x_read_ecg_raw(buf, buf_len); +#else + (void)buf; + (void)buf_len; + return 0; +#endif +} + uint8_t ppg_get_wear_status(void) { uint8_t wear_status = 0; diff --git a/code/sdk/zhihui/drivers/ppg/hal/ppg_hal.h b/code/sdk/zhihui/drivers/ppg/hal/ppg_hal.h index c221c31d6..13704351c 100644 --- a/code/sdk/zhihui/drivers/ppg/hal/ppg_hal.h +++ b/code/sdk/zhihui/drivers/ppg/hal/ppg_hal.h @@ -77,6 +77,13 @@ void ppg_set_bp_cali_data(int *sbp,int *dbp); //是否需要血压校准 1是 0否 bool is_ppg_bp_need_cali(void); +/* 读取 ECG 原始 ADC(int32 样本)。 + * buf: 输出缓冲,建议 int32_t buf[128]; + * buf_len: buf 可容纳的样本个数,建议传 PPG_ECG_RAW_READ_BUF_SIZE; + * 返回: 本次读到的样本数(<= buf_len),0 表示暂无新数据。 + * 开始/停止 ecgbp、ecgafib、ecgraw 时驱动内部自动清缓冲。 */ +uint32_t ppg_read_ecg_raw(int32_t *buf, uint32_t buf_len); + #ifdef __cplusplus #if __cplusplus } diff --git a/code/sdk/zhihui/drivers/ppg/pah831x/pah_sensor/pah831x_main.c b/code/sdk/zhihui/drivers/ppg/pah831x/pah_sensor/pah831x_main.c index 957d5e7b3..402298954 100644 --- a/code/sdk/zhihui/drivers/ppg/pah831x/pah_sensor/pah831x_main.c +++ b/code/sdk/zhihui/drivers/ppg/pah831x/pah_sensor/pah831x_main.c @@ -12,6 +12,7 @@ #include "pah831x_main.h" #include "pah831x_comm.h" #include "cmsis_os2.h" +#include "osal_mutex.h" // c #include #include @@ -138,15 +139,12 @@ static uint16_t acc_num = 50; static int32_t factory_data = 0; static uint8_t work_mode = 0; -static uint8_t wear_status = 0; static osTimerId_t g_pah831x_timer = NULL; static FILE *logFp = NULL; static uint8_t *log_swtich = 0; static int time_out = 200; -static uint8_t time_cnt = 0; -static uint8_t wear_cnt = 0; static int history_wear_status = -1; static uint8_t ppg_work_status = 0 ; @@ -157,6 +155,87 @@ static uint8_t ret_wear = 0; static ecg_data_out _ecg_data_out_alg; static uint8_t bp_cali_cnt = 0; + +#ifdef ENABLE_PAH831X_ECG_FUNCTION +#define ECG_RAW_RING_CAPACITY 8192 +static int32_t g_ecg_raw_ring[ECG_RAW_RING_CAPACITY]; +static uint32_t g_ecg_raw_wr = 0; +static uint32_t g_ecg_raw_count = 0; +static osal_mutex g_ecg_raw_mutex; +static uint8_t g_ecg_raw_mutex_inited = 0; + +static uint8_t living_status = 0; + + +#define HRS_WINDOW_SIZE 7 +static uint8_t hrs_num = 0; +static int32_t sum_hrs = 0; +static int32_t hrs_data[7] = {0}; +static uint8_t left = 0; +static uint8_t right = 0; +static uint8_t time_cnt = 0; +static uint8_t living_wear_cnt = 0; + +static void ecg_raw_mutex_init(void) +{ + if (g_ecg_raw_mutex_inited == 0) { + if (osal_mutex_init(&g_ecg_raw_mutex) == 0) { + g_ecg_raw_mutex_inited = 1; + } + } +} + +static void ecg_raw_mutex_lock(void) +{ + ecg_raw_mutex_init(); + if (g_ecg_raw_mutex_inited) { + osal_mutex_lock(&g_ecg_raw_mutex); + } +} + +static void ecg_raw_mutex_unlock(void) +{ + if (g_ecg_raw_mutex_inited) { + osal_mutex_unlock(&g_ecg_raw_mutex); + } +} + +static uint32_t ecg_raw_ring_rd_pos(void) +{ + return (g_ecg_raw_wr + ECG_RAW_RING_CAPACITY - g_ecg_raw_count) % ECG_RAW_RING_CAPACITY; +} + +static void ecg_raw_ring_reset(void) +{ + ecg_raw_mutex_lock(); + g_ecg_raw_wr = 0; + g_ecg_raw_count = 0; + ecg_raw_mutex_unlock(); +} + +static void ecg_raw_ring_push(const int32_t *data, uint32_t num) +{ + uint32_t i; + + if (data == NULL || num == 0) { + return; + } + + ecg_raw_mutex_lock(); + for (i = 0; i < num; i++) { + g_ecg_raw_ring[g_ecg_raw_wr] = data[i]; + g_ecg_raw_wr = (g_ecg_raw_wr + 1) % ECG_RAW_RING_CAPACITY; + if (g_ecg_raw_count < ECG_RAW_RING_CAPACITY) { + g_ecg_raw_count++; + } + } + ecg_raw_mutex_unlock(); +} +#else +static void ecg_raw_ring_reset(void) +{ +} +#endif //void demo_pah831x(void); static bool sensor_basic_verification(main_state *this, pah_plat *plat); @@ -815,6 +894,55 @@ ecg_data_out get_ecg_data_out(void) return _ecg_data_out ; } +#ifdef ENABLE_PAH831X_ECG_FUNCTION +uint32_t pah831x_read_ecg_raw(int32_t *buf, uint32_t buf_len) +{ + uint32_t to_read = 0; + uint32_t rd = 0; + uint32_t i = 0; + + if (buf == NULL || buf_len == 0) { + return 0; + } + + ecg_raw_mutex_lock(); + to_read = buf_len; + if (to_read > g_ecg_raw_count) { + to_read = g_ecg_raw_count; + } + + rd = ecg_raw_ring_rd_pos(); + for (i = 0; i < to_read; i++) { + buf[i] = g_ecg_raw_ring[(rd + i) % ECG_RAW_RING_CAPACITY]; + } + g_ecg_raw_count -= to_read; + ecg_raw_mutex_unlock(); + + return to_read; +} + +static void pah831x_notify_ecg_raw(const ecg_data_out *out) +{ + if (out == NULL || out->ecg_raw_data == NULL || out->sample_num_per_ch == 0) { + return; + } + + ecg_raw_ring_push((const int32_t *)out->ecg_raw_data, out->sample_num_per_ch); +} +#else +uint32_t pah831x_read_ecg_raw(int32_t *buf, uint32_t buf_len) +{ + (void)buf; + (void)buf_len; + return 0; +} + +static void pah831x_notify_ecg_raw(const ecg_data_out *out) +{ + (void)out; +} +#endif + uint8_t get_ecg_enable(void) { return _state.config.ecg_is_enabled ; @@ -898,6 +1026,61 @@ uint16_t get_pah831x_touch_value() } +static void get_avg_hrs(uint8_t hrs) +{ + if (hrs_num >= HRS_WINDOW_SIZE) { + sum_hrs -= hrs_data[left]; + left = (left + 1) % HRS_WINDOW_SIZE; + hrs_num--; + } + + hrs_data[right] = hrs; + sum_hrs += hrs; + right = (right + 1) % HRS_WINDOW_SIZE; + hrs_num++; + + + if (hrs_num > 2) + { + + uint8_t first_idx = left; + int32_t min_hrs = hrs_data[first_idx]; + int32_t max_hrs = hrs_data[first_idx]; + + + for (uint8_t i = 0; i < hrs_num; i++) { + uint8_t current_idx = (left + i) % HRS_WINDOW_SIZE; + uint8_t current_hrs = hrs_data[current_idx]; + + if (current_hrs < min_hrs) { + min_hrs = current_hrs; + } + if (current_hrs > max_hrs) { + max_hrs = current_hrs; + } + } + + uint8_t valid_count = hrs_num - 2; + ret_hrs = (sum_hrs - max_hrs - min_hrs) / valid_count; + + + // if (last_hrs >= 30 && last_hrs <= 210) + // { + // int16_t diff = avg_hrs - last_hrs; + // if (abs(diff) > 10) + // { + // if (diff > 0) + // avg_hrs = last_hrs + 5; + // else + // avg_hrs = last_hrs - 5; + // } + // } + // last_hrs = avg_hrs; + // printf("max_hrs[%d],min_hrs[%d],avg_hrs[%d]\n",max_hrs,min_hrs,avg_hrs); + } +} + + static void pah831x_hr_timer_handler(void) { pah831x_sensor_task(); @@ -946,11 +1129,71 @@ static void pah831x_hr_timer_handler(void) ); LOG_PRINT("_ppg_data_out_alg.is_touched %d\n", _ppg_data_out_alg.is_touched ); LOG_PRINT("hr_out:%d,hr_trust_level_out:%d,acc_num:%d\n", hr_out,hr_trust_level_out,acc_num); + LOG_PRINT("living_status:%d\n",living_status); -// - ret_hrs = hr_out; ret_wear = _ppg_data_out_alg.is_touched; + if(hr_out) + { + if(hr_trust_level_out < 2) + living_status = 2; + else + living_status = 1; + } + else + living_status = 0; + +// + time_cnt++; + + if(time_cnt >= 5) + { + time_cnt = 0; + if(uapi_dev_runtime_get_train_status()) + { + if(hr_out >= 30 && hr_out <= 210) + get_avg_hrs(hr_out); + } + else + { + if(living_status == 1) + { + living_wear_cnt = 0; + + if(hr_out >= 30 && hr_out <= 210) + get_avg_hrs(hr_out); + + living_status = 1; + #ifdef SUPPORT_CYWEE_SERVICE + cywee_wear_status_update(&living_status); + #endif + } + else if(living_status == 2) + { + living_wear_cnt++; + if(living_wear_cnt >= 5) + { + ret_hrs = 0; + living_status = 2; + ret_wear = 0; + history_wear_status = 0; + // hx3695_unwearstatus_update(); + + #ifdef SUPPORT_CYWEE_SERVICE + cywee_wear_status_update(&living_status); + #endif + living_wear_cnt = 0; + } + } + else + { + living_wear_cnt = 0; + living_status = 0; + } + } + } + + clear_ppg_data_ready(); } if((get_ppg_enable())!=(ppg_work_status)){ @@ -983,11 +1226,36 @@ static void pah831x_wear_timer_handler(void) // printf("history_wear_status:%d,ret_wear:%d\n",history_wear_status,ret_wear); if(history_wear_status != ret_wear) { + if(uapi_dev_runtime_get_train_status() && !ret_wear) + ret_hrs = 0; + history_wear_status = ret_wear; - #ifdef SUPPORT_CYWEE_SERVICE - // do_cywee_service_check_msg(CYWEE_SERVICE_CMD_WEAR_STATUS_CHANGE,&wear_status); - cywee_wear_status_update(&ret_wear); - #endif + + if(uapi_dev_runtime_get_sleep_monitoring_status()) + { + if(!ret_wear) + { + #ifdef SUPPORT_CYWEE_SERVICE + cywee_wear_status_update(&ret_wear); + #endif + } + else if(living_status == 1 && ret_wear) + { + #ifdef SUPPORT_CYWEE_SERVICE + cywee_wear_status_update(&ret_wear); + #endif + } + } + else + { + if(living_status != 2) + { + + #ifdef SUPPORT_CYWEE_SERVICE + cywee_wear_status_update(&ret_wear); + #endif + } + } } clear_ppg_data_ready(); @@ -1385,6 +1653,8 @@ static void pah831x_ecg_raw_timer_handler(void) { _ecg_data_out_alg = get_ecg_data_out() ; + pah831x_notify_ecg_raw(&_ecg_data_out_alg); + report_ecg_fifo_data( (uint32_t)_ecg_data_out_alg.end_timestamp, _ecg_data_out_alg.duration, @@ -1534,6 +1804,7 @@ void pah831x_ecg_afib_start(void) void pah831x_ecg_raw_start(void) { + ecg_raw_ring_reset(); pah831x_sensor_init(); pah831x_sensor_start(PAH831X_WORK_POLLING, PAH831X_INIT_ALWAYS_RUN, PAH831X_PPG_REG_SETTING_IDLE, PAH831X_ECG_REG_SETTING_1, 250, 2); ecg_filter_init(); @@ -1550,6 +1821,16 @@ void pah831x_hr_stop(void) { pah_sensor_stop(); hr_algorithm_close(); + + hrs_num = 0; + sum_hrs = 0; + memset(hrs_data,0,7); + + left = 0; + right = 0; + ret_hrs = 0; + living_wear_cnt = 0; + living_status = 0; } void pah831x_hrv_stop(void) @@ -1589,6 +1870,7 @@ void pah831x_rr_stop(void) void pah831x_ecg_raw_stop(void) { + ecg_raw_ring_reset(); pah_sensor_stop(); ecg_filter_close(); } @@ -1675,10 +1957,10 @@ void pah831x_sensor_stop(void) else if(work_mode == 9) pah831x_ecg_raw_stop(); - ret_hrs = 0; ret_spo2 = 0; ret_hrv = 0; ret_wear = 0; + history_wear_status = -1; memset(acc_data, 0, sizeof(acc_data)); } diff --git a/code/sdk/zhihui/drivers/ppg/pah831x/pah_sensor/pah831x_main.h b/code/sdk/zhihui/drivers/ppg/pah831x/pah_sensor/pah831x_main.h index 3a62944be..6d4c424af 100644 --- a/code/sdk/zhihui/drivers/ppg/pah831x/pah_sensor/pah831x_main.h +++ b/code/sdk/zhihui/drivers/ppg/pah831x/pah_sensor/pah831x_main.h @@ -378,4 +378,6 @@ void pah831x_save_calidata(void); void pah831x_set_bp_cali_data(int *sbp,int *dbp); -bool is_pah831x_need_cali(void); \ No newline at end of file +bool is_pah831x_need_cali(void); + +uint32_t pah831x_read_ecg_raw(int32_t *buf, uint32_t buf_len); \ No newline at end of file -- 2.17.1