增加ECG电极触摸标志获取 3461/1 3461/head 3462/target/1
author1624952103 <pl@DESKTOP-EN9LKR5.localdomain>
Fri, 12 Jun 2026 03:51:35 +0000 (11:51 +0800)
committer1624952103 <pl@DESKTOP-EN9LKR5.localdomain>
Fri, 12 Jun 2026 03:51:35 +0000 (11:51 +0800)
code/sdk/zhihui/drivers/ppg/hal/ppg_hal.c
code/sdk/zhihui/drivers/ppg/hal/ppg_hal.h
code/sdk/zhihui/drivers/ppg/pah831x/pah_sensor/pah831x_main.c
code/sdk/zhihui/drivers/ppg/pah831x/pah_sensor/pah831x_main.h
code/sdk/zhihui/drivers/ppg/pah831x/pah_sensor/pah831x_reg_default_note_0109_2LED_20251226.h

index df95e00..2570cb6 100644 (file)
@@ -203,6 +203,15 @@ void ppg_get_ecg_afib(uint8_t *ecg_hr, uint8_t *afib_result, uint8_t *status, ui
 #endif
 }
 
+uint8_t ppg_get_ecg_electrode_touch(void)
+{
+#if defined PPG_PAH831X
+    return pah831x_get_ecg_electrode_touch();
+#else
+    return 0;
+#endif
+}
+
 uint8_t ppg_get_wear_status(void)
 {
     uint8_t wear_status = 0;
index 3112e9b..736c87c 100644 (file)
@@ -109,6 +109,9 @@ void ppg_get_ecg_afib(uint8_t *ecg_hr, uint8_t *afib_result, uint8_t *status, ui
  * touch_flag: 电极触摸标志 0 未触摸  1 已触摸 */
 void ppg_get_ecg_bp(int *sbp, int *dbp, uint8_t *status, uint8_t *ecg_touch_flag);
 
+/* ECG 电极触摸: 0 未触摸, 1 已触摸 */
+uint8_t ppg_get_ecg_electrode_touch(void);
+
 #ifdef __cplusplus
 #if __cplusplus
 }
index c282cd1..fa3ac46 100644 (file)
@@ -63,6 +63,9 @@ typedef enum
     TOUCH_STATUS__OFF,\r
 } touch_status;\r
 \r
+/* 0x2F LOD: bit0=LA, bit3=RA; 测量模式下 1=lead-off, 0=contact */\r
+#define ECG_LOD_FLAG_LA    0x01u\r
+#define ECG_LOD_FLAG_RA    0x08u\r
 \r
 typedef struct\r
 {\r
@@ -87,8 +90,9 @@ typedef struct
     // ecg\r
     uint64_t ecg_prev_timestamp;\r
     \r
-    // touch\r
+    // touch: IR 佩戴 / ECG 电极(0=未触摸 1=已触摸)\r
     touch_status touch_status;\r
+    uint8_t ecg_electrode_touch;\r
     //Dri or Polling\r
     uint8_t work_mode;\r
     \r
@@ -251,6 +255,9 @@ static void start_touch(main_state *this);
 \r
 static void read_fifo_callback(void *user_data, uint8_t *fifo, uint16_t data_count, float read_size_ratio);\r
 static void send_fifo_data(main_state *this, uint8_t *fifo, uint16_t data_count, float read_size_ratio);\r
+static void ecg_electrode_touch_reset(void);\r
+static void ecg_electrode_touch_update(const pah831x_int_flag *int_flag);\r
+static touch_status ecg_electrode_touch_for_alg(void);\r
 static float update_sample_interval(main_state *this, uint64_t start_timestamp, uint64_t end_timestamp, uint32_t sample_num, float read_size_ratio);\r
 \r
 static void send_ppg_event(main_state *this, uint64_t timestamp, const pah831x_ppg_sample *ppg_sample);\r
@@ -361,6 +368,7 @@ void pah831x_data_ready_interrupt(main_state *this)
         \r
         pah831x_int_flag int_flag;\r
         pah831x_read_int_flag(&_state.core, &int_flag);\r
+        ecg_electrode_touch_update(&int_flag);\r
         DEBUG_PRINT("int_flag(data[1],fifo,touch): 0x%02X, %d, %d,\n", int_flag.data[1], (int)PAH831X_HAS_FIFO_INT(int_flag), (int)PAH831X_HAS_TOUCH_INT(int_flag));\r
         //uint16_t touch_value = 0 ;\r
         //bool touch_flag = false;\r
@@ -473,6 +481,7 @@ void pah831x_polling(main_state *this)
         now_timestamp = pxi_get_tick_count();\r
         pah831x_int_flag int_flag;\r
         pah831x_read_int_flag(&_state.core, &int_flag);\r
+        ecg_electrode_touch_update(&int_flag);\r
         DEBUG_PRINT("int_flag(data[1],fifo,touch): 0x%02X, %d, %d\n", int_flag.data[1], (int)PAH831X_HAS_FIFO_INT(int_flag), (int)PAH831X_HAS_TOUCH_INT(int_flag));\r
         pah831x_read_touch_flag(&_state.core,&_state.core.touch_flag);\r
         pah831x_read_touch_value(&_state.core, &_state.core.touch_value);\r
@@ -726,6 +735,33 @@ static void read_fifo_callback(void *user_data, uint8_t *fifo, uint16_t data_cou
     send_fifo_data(this, fifo, data_count, read_size_ratio);\r
 }\r
 \r
+static void ecg_electrode_touch_reset(void)\r
+{\r
+    _state.ecg_electrode_touch = 0;\r
+}\r
+\r
+static void ecg_electrode_touch_update(const pah831x_int_flag *int_flag)\r
+{\r
+    uint8_t lod;\r
+\r
+    if (_state.ecg_reg_setting == PAH831X_ECG_REG_SETTING_IDLE) {\r
+        return;\r
+    }\r
+\r
+    lod = int_flag->data[0];\r
+    _state.ecg_electrode_touch = ((lod & (ECG_LOD_FLAG_LA | ECG_LOD_FLAG_RA)) == 0) ? 1 : 0;\r
+}\r
+\r
+static touch_status ecg_electrode_touch_for_alg(void)\r
+{\r
+    return _state.ecg_electrode_touch ? TOUCH_STATUS__ON : TOUCH_STATUS__OFF;\r
+}\r
+\r
+uint8_t pah831x_get_ecg_electrode_touch(void)\r
+{\r
+    return _state.ecg_electrode_touch;\r
+}\r
+\r
 static void send_fifo_data(main_state *this, uint8_t *fifo, uint16_t data_count, float read_size_ratio)\r
 {\r
     uint16_t ppg_sample_num = 0;\r
@@ -784,7 +820,7 @@ static void send_fifo_data(main_state *this, uint8_t *fifo, uint16_t data_count,
         duration =(uint32_t)(this->task_timestamp - this->prev_task_timestamp );\r
         _ecg_data_out.ecg_data_ready++;\r
         _ecg_data_out.duration = duration;\r
-        _ecg_data_out.is_touched = _state.core.touch_flag ? 1U : 0U;\r
+        _ecg_data_out.is_touched = ecg_electrode_touch_for_alg();\r
         _ecg_data_out.ch_num = 1  ;\r
         _ecg_data_out.sample_num_per_ch = ecg_sample_num ;\r
         \r
@@ -811,7 +847,7 @@ static void send_fifo_data(main_state *this, uint8_t *fifo, uint16_t data_count,
         duration =(uint32_t)(this->task_timestamp - this->prev_task_timestamp );\r
         _ecg_data_out.ecg_data_ready++;\r
         _ecg_data_out.duration = duration;\r
-        _ecg_data_out.is_touched = _state.core.touch_flag ? 1U : 0U;\r
+        _ecg_data_out.is_touched = ecg_electrode_touch_for_alg();\r
         _ecg_data_out.ch_num = 1  ;\r
         _ecg_data_out.sample_num_per_ch = ecg_sample_num ;\r
         _ppg_data_out.ppg_data_ready++;\r
@@ -1021,33 +1057,15 @@ void set_ir_r_led_switch_status(bool status)
     _state.core.ir_r_led_switch_new_status = status;\r
 }\r
 \r
-uint16_t get_pah831x_touch_value(void)\r
+uint16_t get_pah831x_touch_value()\r
 {\r
-    uint16_t touch_value = 0;\r
-\r
-    pah831x_read_touch_value(&_state.core, &touch_value);\r
-    return touch_value;\r
-}\r
-\r
-static void pah831x_fill_ecg_touch_flag(uint8_t *touch_flag)\r
-{\r
-    bool flag = false;\r
-\r
-    if (touch_flag == NULL) {\r
-        return;\r
-    }\r
+    uint16_t pah831x_touch_value = 0 ;\r
 \r
-    pah831x_read_touch_flag(&_state.core, &flag);\r
-    *touch_flag = flag ? 1U : 0U;\r
+    pah831x_read_touch_value(&_state.core, &pah831x_touch_value);\r
+    \r
+    return pah831x_touch_value ;\r
 }\r
 \r
-static void pah831x_reset_ecg_touch_cache(void)\r
-{\r
-    _state.touch_status = TOUCH_STATUS__UNKNOWN;\r
-    _state.core.touch_flag = false;\r
-    _state.core.touch_value = 0;\r
-    _ecg_data_out.is_touched = 0;\r
-}\r
 \r
 static void get_avg_hrs(uint8_t hrs)\r
 {\r
@@ -1487,9 +1505,6 @@ static void pah831x_ecg_bp_timer_handler(void)
         ret_ecg_bp_status = pah_ecg_bp_get_status();\r
 \r
         // LOG_PRINT("got_time_out =%d\n",got_time_out);\r
-        // LOG_PRINT("got_BP =%d\n",got_BP);\r
-        // LOG_PRINT("_ecg_data_out_alg.is_touched =%d\n",_ecg_data_out_alg.is_touched);\r
-        // LOG_PRINT("ECG_PPG_BP_CALI_cali_value = %d, %d, %d, %d,\n", cal_sbp_in,cal_dbp_in,CalValue_in,CalValue1_in);\r
         \r
         clear_ecg_data_ready();\r
         clear_ppg_data_ready();\r
@@ -1772,6 +1787,7 @@ void pah831x_hr_start(void)
 void pah831x_ecg_bp_start(uint8_t cmd)\r
 {\r
     ret_ecg_bp_status = PAH_ECG_BP_STATUS_MEASURING;\r
+    ecg_electrode_touch_reset();\r
     ecg_param_init = 0;\r
 \r
     if(cmd == 0)\r
@@ -1786,14 +1802,12 @@ void pah831x_ecg_bp_start(uint8_t cmd)
     if(calibration_mode==ECG_PPG_BP_CALI)\r
     {\r
         pah831x_sensor_init();\r
-        pah831x_reset_ecg_touch_cache();\r
         pah831x_sensor_start(PAH831X_WORK_POLLING, PAH831X_INIT_ALWAYS_RUN, PAH831X_PPG_REG_SETTING_IDLE, PAH831X_ECG_REG_SETTING_2, 128, 3.90625f);\r
         ecg_bp_algorithm_init();\r
         \r
     }else if(calibration_mode==ECG_PPG_BP_ESTIMATE)\r
     {\r
         pah831x_sensor_init();\r
-        pah831x_reset_ecg_touch_cache();\r
         pah831x_sensor_start(PAH831X_WORK_POLLING, PAH831X_INIT_ALWAYS_RUN, PAH831X_PPG_REG_SETTING_IDLE, PAH831X_ECG_REG_SETTING_2, 128, 3.90625f);\r
         ecg_bp_algorithm_init();\r
         //calibration_mode = ECG_PPG_BP_ESTIMATE;\r
@@ -1834,9 +1848,9 @@ void pah831x_ecg_afib_start(void)
     ret_ecg_afib_hr = 0;\r
     ret_ecg_afib_result = PAH_ECG_AFIB_RESULT_INVALID;\r
     ret_ecg_afib_status = PAH_ECG_AFIB_STATUS_MEASURING;\r
+    ecg_electrode_touch_reset();\r
     ecg_afib_algorithm_init();\r
     pah831x_sensor_init();\r
-    pah831x_reset_ecg_touch_cache();\r
     pah831x_sensor_start(PAH831X_WORK_POLLING ,PAH831X_INIT_ALWAYS_RUN,PAH831X_PPG_REG_SETTING_IDLE ,PAH831X_ECG_REG_SETTING_1,256,3.91f  );\r
 }\r
 \r
@@ -1884,7 +1898,6 @@ void pah831x_ecgbp_stop(void)
 \r
     pah_sensor_stop();\r
     ecg_bp_algorithm_close();\r
-    pah831x_reset_ecg_touch_cache();\r
 \r
     if(calibration_mode == ECG_PPG_BP_CALI)\r
     {\r
@@ -1897,11 +1910,11 @@ void pah831x_ecg_afib_stop(void)
     pah_sensor_stop();\r
     ecg_afib_algorithm_close();\r
     ecg_afib_algorithm_init();\r
-    pah831x_reset_ecg_touch_cache();\r
 \r
     ret_ecg_afib_hr = 0;\r
     ret_ecg_afib_result = PAH_ECG_AFIB_RESULT_INVALID;\r
     ret_ecg_afib_status = PAH_ECG_AFIB_STATUS_IDLE;\r
+    ecg_electrode_touch_reset();\r
 }\r
 \r
 void pah831x_stress_stop(void)\r
@@ -2198,7 +2211,7 @@ uint8_t pah831x_get_hrv(void)
     return ret_hrv;\r
 }\r
 \r
-void pah831x_get_ecg_afib(uint8_t *ecg_hr, uint8_t *afib_result, uint8_t *status, uint8_t *touch_flag)\r
+void pah831x_get_ecg_afib(uint8_t *ecg_hr, uint8_t *afib_result, uint8_t *status ,uint8_t *touch_flag)\r
 {\r
     if (ecg_hr != NULL) {\r
         *ecg_hr = ret_ecg_afib_hr;\r
@@ -2209,7 +2222,9 @@ void pah831x_get_ecg_afib(uint8_t *ecg_hr, uint8_t *afib_result, uint8_t *status
     if (status != NULL) {\r
         *status = ret_ecg_afib_status;\r
     }\r
-    pah831x_fill_ecg_touch_flag(touch_flag);\r
+    if (touch_flag != NULL) {\r
+        *touch_flag = pah831x_get_ecg_electrode_touch();\r
+    }\r
 }\r
 \r
 void pah831x_get_ecg_bp(int *sbp, int *dbp, uint8_t *status, uint8_t *touch_flag)\r
@@ -2218,7 +2233,9 @@ void pah831x_get_ecg_bp(int *sbp, int *dbp, uint8_t *status, uint8_t *touch_flag
     if (status != NULL) {\r
         *status = ret_ecg_bp_status;\r
     }\r
-    pah831x_fill_ecg_touch_flag(touch_flag);\r
+    if (touch_flag != NULL) {\r
+        *touch_flag = pah831x_get_ecg_electrode_touch();\r
+    }\r
 }\r
 \r
 uint8_t pah831x_get_wear_status(void)\r
index d287b01..6120f84 100644 (file)
@@ -373,6 +373,9 @@ void pah831x_get_ecg_afib(uint8_t *ecg_hr, uint8_t *afib_result, uint8_t *status
 \r
 void pah831x_get_ecg_bp(int *sbp, int *dbp, uint8_t *status, uint8_t *touch_flag);\r
 \r
+/* ECG 电极触摸: 0 未触摸, 1 已触摸 (LOD, 与 IR 佩戴无关) */\r
+uint8_t pah831x_get_ecg_electrode_touch(void);\r
+\r
 void pah8311_get_bp(int *sbp,int *dbp);\r
 \r
 void pah831x_read_calidata(void);\r
index 40020ca..9534952 100644 (file)
@@ -1224,7 +1224,7 @@ static const uint8_t PAH831X_REG_ECG_1[][2] = {
     { 0x4F , 0x04 }, //R_NoTouchDetection_Count_Th[4:0]\r
     { 0x24 , 0x08 }, //RDB_ChT_report_divider[7:0]\r
     { 0x2E , 0x03 }, \r
-    { 0x1B , 0x30 }, \r
+    { 0x1B , 0x70 }, /* ECG measure: RDB_LOD_EnH; 产测用独立 reg 表  { 0x1B , 0x30 } */\r
     { 0x68 , 0x00 }, \r
     { 0x7F , 0x01 }, \r
     { 0x05 , 0x80 }, \r
@@ -1341,7 +1341,7 @@ static const uint8_t PAH831X_REG_ECG_2[][2] = {
     { 0x4F , 0x04 }, //R_NoTouchDetection_Count_Th[4:0]\r
     { 0x24 , 0x08 }, //RDB_ChT_report_divider[7:0]\r
     { 0x2E , 0x03 }, //{fp_lpf_mask_cnt[7:4], Reserved[3], fp_lpf_sel[2:1](0:128/1:256/2:512/3:1024Hz), fp_lpf_enh[0]}\r
-    { 0x1B , 0x21 }, //{Reserved[7], RDB_LOD_EnH[6], RDB_ECG_EnH[5], RDB_ChT_EnH[4], RDB_ChD_EnH[3], RDB_ChC_EnH[2], RDB_ChB_EnH[1], RDB_ChA_EnH[0]}\r
+    { 0x1B , 0x61 }, /* ECG+PPG BP: RDB_LOD_EnH  { 0x1B , 0x21 }  */  \r
     { 0x50 , 0x05 },//{1'b0, R_PPG_FIFO_Write_skip_A_EnH, R_AGC_DAC_TIA_A_EnH[1:0], R_AGC_PPG2_A_Sel, R_AGC_PPG1_A_Sel, R_PPG2_A_EnH, R_PPG1_A_EnH};\r
     { 0x68 , 0x00 }, //{Dummy[5:0], RDB_ChF_EnH, RDB_ChE_EnH}; \r
     { 0x7F , 0x01 }, //Bank1\r