mcu_hi3321_watch/tjd/driver/hr/hx3602/hr_drv_HX3602.c
2025-05-26 20:15:20 +08:00

652 lines
21 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*----------------------------------------------------------------------------
* Copyright (c) TJD Technologies Co., Ltd. 2020. All rights reserved.
*
* Description: hr_drv_HX3602.c
*
* Author: liangjianfei
*
* Create: 2024-4-26
*--------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include "hr_drv_HX3602.h"
#include "hrs3602_reg_init.h"
#include "hr_port.h"
#include "tyhx_hrs_alg.h"
#include "sys_config.h"
#define ENABLE_PRINT_INFO 1
#if ENABLE_PRINT_INFO
#define static_print_info(...) sys_hr_log_i(__VA_ARGS__) //一般信息打印宏控制
#define static_print_warn(...) sys_hr_log_w(__VA_ARGS__) //警告信息打印一般常开
#define static_print_error(...) sys_hr_log_e(__VA_ARGS__) //错误信息打印一般常开
#else
#define static_print_info(...)
#define static_print_warn(...)
#define static_print_error(...)
#endif
static hr_wear_status_t wear_status = MSG_CHECK_INIT;
static hr_wear_status_t wear_status_pre = MSG_CHECK_INIT;
const uint32_t hx3602_wear_thre_high = 5000;
const uint32_t hx3602_wear_thre_low = 4000;
const uint32_t hx3602_agc_adj_thre_high = 450000;
const uint32_t hx3602_agc_adj_thre_low = 150000;
static int32_t check_touch_data_min = 300000;
static int32_t check_touch_data_max = 0;
static int32_t check_touch_data_fifo[8] = {0};
static uint8_t agc_cnt = 0;
static uint8_t agc_offset = 0;
static uint8_t check_touch_cnt = 0;
/**********************************************************************************************************************
* LOCAL FUNCTIONS
*/
static bool Hrs3602_write_reg(uint8_t addr, uint8_t data)
{
if(hrsensor_write_reg(addr, data) != 0){
return -1;
}
return 0;
}
static bool Hrs3602_read_reg(uint8_t addr, uint8_t *recvbuf)
{
if(hrsensor_read_reg(addr, recvbuf, 1) != 0){
return -1;
}
return 0;
}
static bool Hrs3602_brust_read_reg(uint8_t addr, uint8_t *buf, uint8_t length)
{
if(hrsensor_read_reg(addr, buf, length) != 0){
return -1;
}
return 0;
}
static void Hrs3602_driv_init(void)
{
wear_status = MSG_CHECK_INIT;
wear_status_pre = MSG_CHECK_INIT;
agc_cnt = 0;
agc_offset = 0;
check_touch_cnt = 10;
}
static void Hrs3602_read_data_packet(void)
{
uint8_t databuf1[6] = {0};
uint8_t databuf2[6] = {0};
int32_t P0 = 0, P1 = 0, P2 = 0, P3 = 0;
Hrs3602_brust_read_reg(0xa0, databuf1, 6);
Hrs3602_brust_read_reg(0xa6, databuf2, 6);
P0 = ((databuf1[0]) | (databuf1[1] << 8) | (databuf1[2] << 16));
P1 = ((databuf1[3]) | (databuf1[4] << 8) | (databuf1[5] << 16));
P2 = ((databuf2[0]) | (databuf2[1] << 8) | (databuf2[2] << 16));
P3 = ((databuf2[3]) | (databuf2[4] << 8) | (databuf2[5] << 16));
Hrs3602_brust_read_reg(0xa0, databuf1, 6);
Hrs3602_brust_read_reg(0xa6, databuf2, 6);
// DEBUG_PRINTF("%d %d %d %d" ,P0,P1,P2,P3);
}
static bool Hrs3602_read_hrs(int32_t *hrm_data, int32_t *als_data)
{
uint8_t databuf[6] = {0};
int32_t P0 = 0, P1 = 0;
Hrs3602_brust_read_reg(0xa0, databuf, 6);
P0 = ((databuf[0]) | (databuf[1] << 8) | (databuf[2] << 16));
P1 = ((databuf[3]) | (databuf[4] << 8) | (databuf[5] << 16));
// DEBUG_PRINTF(0," %d %d " , P0, P1);
if (P0 > P1)
{
*hrm_data = P0 - P1;
}
else
{
*hrm_data = 0;
}
*als_data = P0;
return 0;
}
static bool Hrs3602_read_ps1(int32_t *infrared_data)
{
uint8_t databuf[6] = {0};
int32_t P0 = 0, P1 = 0;
Hrs3602_brust_read_reg(0xa6, databuf, 6);
P0 = ((databuf[0]) | (databuf[1] << 8) | (databuf[2] << 16));
P1 = ((databuf[3]) | (databuf[4] << 8) | (databuf[5] << 16));
if (P0 > P1)
{
*infrared_data = P0 - P1;
}
else
{
*infrared_data = 0;
}
return 0;
}
static bool Hrs3602_chip_init(void)
{
int i = 0;
uint8_t chip_id = 0;
Hrs3602_read_reg(0x00, &chip_id);
if (chip_id != 0x22)
{
return -1;
}
for (i = 0; i < INIT_ARRAY_SIZE; i++)
{
if (Hrs3602_write_reg(init_register_array[i][0], init_register_array[i][1]) != 0)
{
return -2;
}
}
return 0;
}
static void Hrs3602_check_touch(int32_t infrared_data)
{
if (infrared_data > hx3602_wear_thre_high)
{
if (check_touch_cnt >= 20)
{
check_touch_cnt = 20;
wear_status = MSG_TOUCH;
if (wear_status != wear_status_pre)
{
Hrs3602_normal_power();
tyhx_hrs_alg_open_deep();
}
}
else
{
check_touch_cnt++;
}
}
else if (infrared_data < hx3602_wear_thre_low)
{
if (check_touch_cnt <= 0)
{
check_touch_cnt = 0;
wear_status = MSG_NO_TOUCH;
if (wear_status != wear_status_pre)
{
Hrs3602_low_power();
}
}
else
{
check_touch_cnt--;
}
}
}
static void Hrs3602_check_touch_diff(int32_t infrared_data)
{
uint8_t ii = 0, jj = 0;
int32_t ir_data_temp = 0;
int32_t check_fifo[8];
int32_t check_data_sum = 0;
int32_t check_data_ave = 0;
for (ii = 0; ii < 7; ii++)
{
check_touch_data_fifo[ii] = check_touch_data_fifo[ii + 1];
check_fifo[ii] = check_touch_data_fifo[ii];
check_data_sum = check_data_sum + check_touch_data_fifo[ii];
}
check_touch_data_fifo[7] = infrared_data;
check_fifo[7] = check_touch_data_fifo[7];
check_data_sum = check_data_sum + check_touch_data_fifo[7];
for (ii = 0; ii < 7; ii++)
{
for (jj = 0; jj < 7 - ii; jj++)
{
if (check_fifo[jj] > check_fifo[jj + 1])
{
ir_data_temp = check_fifo[jj];
check_fifo[jj] = check_fifo[jj + 1];
check_fifo[jj + 1] = ir_data_temp;
}
}
}
if (abs(check_fifo[7] - check_fifo[0]) < 4000 && check_fifo[0] > 0)
{
check_data_ave = check_data_sum >> 3;
}
if (check_data_ave < check_touch_data_min && check_data_ave > 0)
{
check_touch_data_min = check_data_ave;
}
if (check_data_ave > check_touch_data_max && check_data_ave > 0)
{
check_touch_data_max = check_data_ave;
}
if (check_touch_data_max > 0 && check_touch_data_min > 0 && check_touch_data_max > check_touch_data_min + hx3602_wear_thre_high)
{
if (infrared_data > check_touch_data_min + hx3602_wear_thre_high)
{
if (check_touch_cnt >= 20)
{
check_touch_cnt = 20;
wear_status = MSG_TOUCH;
if (wear_status != wear_status_pre)
{
Hrs3602_normal_power();
tyhx_hrs_alg_open_deep();
}
}
else
{
check_touch_cnt++;
}
}
else if (infrared_data < check_touch_data_min + hx3602_wear_thre_low)
{
if (check_touch_cnt <= 0)
{
check_touch_cnt = 0;
wear_status = MSG_NO_TOUCH;
if (wear_status != wear_status_pre)
{
Hrs3602_low_power();
}
}
else
{
check_touch_cnt--;
}
}
}
else
{
if (infrared_data > hx3602_wear_thre_high)
{
if (check_touch_cnt >= 20)
{
check_touch_cnt = 20;
wear_status = MSG_TOUCH;
if (wear_status != wear_status_pre)
{
Hrs3602_normal_power();
tyhx_hrs_alg_open_deep();
}
}
else
{
check_touch_cnt++;
}
}
else if (infrared_data < hx3602_wear_thre_low)
{
if (check_touch_cnt <= 0)
{
check_touch_cnt = 0;
wear_status = MSG_NO_TOUCH;
if (wear_status != wear_status_pre)
{
Hrs3602_low_power();
}
}
else
{
check_touch_cnt--;
}
}
}
}
static void Hrs3602_alg_config(void)
{
/*para init begin ....*/
uint32_t prf_temp_clk_num = 0; /*temperatrue phase clk num in each prf */
uint32_t prf_hrs_clk_num = 0; /*hrs phase clk num in each prf */
uint32_t prf_ps_clk_num =0; /*ps phase clk num in each prf */
uint32_t prf_wait_clk_num =0; /*wait time clk num in each prf */
uint32_t en2rst_delay_clk_num =0; /*en signal to first reset delay time clk num in each prf */
uint16_t rst_clk_num = 0;
uint16_t hrs_ckafe_clk_num = 0;
uint16_t ps_ckafe_clk_num = 0;
/*para init end ....*/
/*chip config begin ....*/
uint16_t sample_rate = 25; /*config the data rate of chip frog2 ,uint is Hz*/
uint32_t prf_clk_num = 2620000/sample_rate; /*period in clk num, num = Fclk/fs */
uint8_t temperature_enable = 0; /*temperature test function enable , 1 mean enable ; 0 mean disable */
uint8_t hrs_enable = 1; /*hrs function enable , 1 mean enable ; 0 mean disable */
uint8_t ps_enable = 1; /*ps function enable , 1 mean enable ; 0 mean disable */
uint8_t temperature_adc_osr = 0; /* 0 = 128 ; 1 = 256 ; 2 = 512 ; 3 = 1024 ;*/
uint8_t hrs_adc_osr = 3; /* 0 = 128 ; 1 = 256 ; 2 = 512 ; 3 = 1024 ;*/
uint8_t ps_adc_osr = 3; /* 0 = 128 ; 1 = 256 ; 2 = 512 ; 3 = 1024 ;*/
uint8_t led_dr_cfg = 3 ; /* 0 = 12.5mA ; 1 = 25mA ; 2 =50mA(default) 3 = 100mA*/
uint8_t tia_res = 2 ; /* 0 = 54k(default) ; 1 = 108k ; 2 =216k 3 = 432k */
en2rst_delay_clk_num = 64 ; /* 0 ~ 127 clk num config */
rst_clk_num = 6 ; /* range from 0 to 7 ; 0=1 , 1=3, 2=7,....,7=255 */
hrs_ckafe_clk_num = 128 ; /* hrm phase led on time : 0~ 255 , 实际宽度10位低两位默认为0*/
ps_ckafe_clk_num = 10 ; /* ps phase led on time : 0~ 255 , 实际宽度10位低两位默认为0*/
/*红外离pd远的要适当调大 比如苹果结构距离9mm的 可以设置到96以上*/
/*计算PRF_WAIT的时间*/
if(temperature_enable == 1)
{
prf_temp_clk_num = (en2rst_delay_clk_num + 10) + 2*((2<<rst_clk_num) - 1) + 2*(128<<temperature_adc_osr);
}
if(hrs_enable == 1)
{
prf_hrs_clk_num = (en2rst_delay_clk_num + 10) +
2*(2*((2<<rst_clk_num) - 1) + (hrs_ckafe_clk_num<<4) + 10 + (128<<hrs_adc_osr));
}
if(ps_enable == 1)
{
prf_ps_clk_num = (en2rst_delay_clk_num + 10) +
2*(2*((2<<rst_clk_num) - 1) + (ps_ckafe_clk_num<<4) + 10 + (128<<ps_adc_osr));
}
prf_wait_clk_num = prf_clk_num - prf_temp_clk_num - prf_hrs_clk_num - prf_ps_clk_num ;
/*计算PRF_WAIT的时间*/
Hrs3602_write_reg(0xc1, 0x08); // config external pd as input and cap mode enable
Hrs3602_write_reg(0x03, 0x8f); // all phase use same external pd
/*register configration begin ....*/
Hrs3602_write_reg(0x01, (temperature_enable<<2)|temperature_adc_osr);
Hrs3602_write_reg(0x02, (hrs_enable<<2)|(hrs_adc_osr)|(ps_adc_osr<<4)|(ps_enable<<6));
Hrs3602_write_reg(0x04, hrs_ckafe_clk_num);
Hrs3602_write_reg(0x05, ps_ckafe_clk_num);
Hrs3602_write_reg(0x0a, prf_wait_clk_num);
Hrs3602_write_reg(0x0b, prf_wait_clk_num>>8);
Hrs3602_write_reg(0x0c, prf_wait_clk_num>>16);
Hrs3602_write_reg(0x11, rst_clk_num);
Hrs3602_write_reg(0x12, en2rst_delay_clk_num);
//GPIO config
Hrs3602_write_reg(0xc0, (0x84 | led_dr_cfg)); /* led dr config , 0x84 = 12.5mA , 0x85 = 25mA ,0x86 = 50 mA ,0x87 = 100mA */
Hrs3602_write_reg(0xc2, (0x00 | tia_res)); /* tia res config , 0x00 = 54K , 0x01 = 108K ,0x02 = 216K ,0x03 = 432K */
/*register configration end ....*/
Hrs3602_write_reg(0x09, 0x02 );
#ifdef IR_CHECK_TOUCH
Hrs3602_write_reg(0X14, 0x80);
Hrs3602_write_reg(0X15, 0x40);
#else
Hrs3602_write_reg(0X14, 0x40);
Hrs3602_write_reg(0X15, 0x40);
#endif
#ifdef TIMER_READ_MODE
Hrs3602_write_reg( 0x07, 0x00 );
Hrs3602_write_reg( 0x08, 0x00 ); // self clear int
#else //int mode
Hrs3602_write_reg( 0x07, 0x01 );
Hrs3602_write_reg( 0x08, 0x00 ); // self clear int
#endif
return ;
}
static hr_wear_status_t Hrs3602_agc(int32_t als_raw_data, int32_t infrared_data)
{
#ifdef CHECK_TOUCH_DIFF
Hrs3602_check_touch_diff(infrared_data);
#else
Hrs3602_check_touch(infrared_data);
#endif
if (wear_status == MSG_TOUCH)
{
if (als_raw_data > hx3602_agc_adj_thre_high)
{
if (agc_cnt == 3)
{
agc_cnt = 0;
if (agc_offset < 32)
{
agc_offset++;
Hrs3602_write_reg(0x15, (0x40 | agc_offset));
}
}
else
{
agc_cnt++;
}
}
else if (als_raw_data < hx3602_agc_adj_thre_low)
{
if (agc_cnt == 3)
{
agc_cnt = 0;
if (agc_offset > 0)
{
agc_offset--;
Hrs3602_write_reg(0x15, (0x40 | agc_offset));
}
}
else
{
agc_cnt++;
}
}
}
else
{
agc_cnt = 0;
agc_offset = 0;
}
wear_status_pre = wear_status;
// DEBUG_PRINTF("min=%d ir=%d status=%d offset=%d" ,check_touch_data_min, infrared_data, wear_status, agc_offset);
return wear_status;
}
/**********************************************************************************************************************
* PUBLIC FUNCTIONS
*/
void Hrs3602_wear_config(void)
{
/*para init begin ....*/
uint32_t prf_temp_clk_num = 0; /*temperatrue phase clk num in each prf */
uint32_t prf_hrs_clk_num = 0; /*hrs phase clk num in each prf */
uint32_t prf_ps_clk_num =0; /*ps phase clk num in each prf */
uint32_t prf_wait_clk_num =0; /*wait time clk num in each prf */
uint32_t en2rst_delay_clk_num =0; /*en signal to first reset delay time clk num in each prf */
uint16_t rst_clk_num = 0;
uint16_t hrs_ckafe_clk_num = 0;
uint16_t ps_ckafe_clk_num = 0;
uint16_t sample_rate = 25; /*config the data rate of chip frog2 ,uint is Hz*/
uint32_t prf_clk_num = 2620000/sample_rate; /*period in clk num, num = Fclk/fs */
uint8_t temperature_enable = 0; /*temperature test function enable , 1 mean enable ; 0 mean disable */
uint8_t hrs_enable = 0; /*hrs function enable , 1 mean enable ; 0 mean disable */
uint8_t ps_enable = 1; /*ps function enable , 1 mean enable ; 0 mean disable */
uint8_t temperature_adc_osr = 0; /* 0 = 128 ; 1 = 256 ; 2 = 512 ; 3 = 1024 ;*/
uint8_t hrs_adc_osr = 3; /* 0 = 128 ; 1 = 256 ; 2 = 512 ; 3 = 1024 ;*/
uint8_t ps_adc_osr = 3; /* 0 = 128 ; 1 = 256 ; 2 = 512 ; 3 = 1024 ;*/
uint8_t led_dr_cfg = 3 ; /* 0 = 12.5mA ; 1 = 25mA ; 2 =50mA(default) 3 = 100mA*/
uint8_t tia_res = 2 ; /* 0 = 54k(default) ; 1 = 108k ; 2 =216k 3 = 432k */
en2rst_delay_clk_num = 64 ; /* 0 ~ 127 clk num config */
rst_clk_num = 6 ; /* range from 0 to 7 ; 0=1 , 1=3, 2=7,....,7=255 */
hrs_ckafe_clk_num = 4 ; /* hrm phase led on time : 0~ 255 , 实际宽度10位低两位默认为0*/
ps_ckafe_clk_num = 16 ; /* ps phase led on time : 0~ 255 , 实际宽度10位低两位默认为0*/
/*计算PRF_WAIT的时间*/
if(temperature_enable == 1)
{
prf_temp_clk_num = (en2rst_delay_clk_num + 10) + 2*((2<<rst_clk_num) - 1) + 2*(128<<temperature_adc_osr);
}
if(hrs_enable == 1)
{
prf_hrs_clk_num = (en2rst_delay_clk_num + 10) +
2*(2*((2<<rst_clk_num) - 1) + (hrs_ckafe_clk_num<<4) + 10 + (128<<hrs_adc_osr));
}
if(ps_enable == 1)
{
prf_ps_clk_num = (en2rst_delay_clk_num + 10) +
2*(2*((2<<rst_clk_num) - 1) + (ps_ckafe_clk_num<<4) + 10 + (128<<ps_adc_osr));
}
prf_wait_clk_num = prf_clk_num - prf_temp_clk_num - prf_hrs_clk_num - prf_ps_clk_num ;
/*计算PRF_WAIT的时间*/
Hrs3602_write_reg(0xc1, 0x08); // config external pd as input and cap mode enable
Hrs3602_write_reg(0x03, 0x8f); // all phase use same external pd
/*register configration begin ....*/
Hrs3602_write_reg(0x01, (temperature_enable<<2)|temperature_adc_osr);
Hrs3602_write_reg(0x02, (hrs_enable<<2)|(hrs_adc_osr)|(ps_adc_osr<<4)|(ps_enable<<6));
Hrs3602_write_reg(0x04, hrs_ckafe_clk_num);
Hrs3602_write_reg(0x05, ps_ckafe_clk_num);
Hrs3602_write_reg(0x0a, prf_wait_clk_num);
Hrs3602_write_reg(0x0b, prf_wait_clk_num>>8);
Hrs3602_write_reg(0x0c, prf_wait_clk_num>>16);
Hrs3602_write_reg(0x11, rst_clk_num);
Hrs3602_write_reg(0x12, en2rst_delay_clk_num);
//GPIO config
Hrs3602_write_reg(0xc0, (0x84 | led_dr_cfg)); /* led dr config , 0x84 = 12.5mA , 0x85 = 25mA ,0x86 = 50 mA ,0x87 = 100mA */
Hrs3602_write_reg(0xc2, (0x00 | tia_res)); /* tia res config , 0x00 = 54K , 0x01 = 108K ,0x02 = 216K ,0x03 = 432K */
/*register configration end ....*/
Hrs3602_write_reg(0x09, 0x02);
#ifdef IR_CHECK_TOUCH
Hrs3602_write_reg(0X14, 0x80);
Hrs3602_write_reg(0X15, 0x40);
#else
Hrs3602_write_reg(0X14, 0x40);
Hrs3602_write_reg(0X15, 0x40);
#endif
Hrs3602_write_reg(0x16, 0x04);
Hrs3602_write_reg(0x07, 0x00);
Hrs3602_write_reg(0x08, 0x00);
return ;
}
void Hrs3602_write_efuse(void)
{
Hrs3602_write_reg(0x85, 0x20);
Hrs3602_write_reg(0x7f, 0x10);
Hrs3602_write_reg(0x80, 0x08);
Hrs3602_write_reg(0x81, 0x44);
Hrs3602_write_reg(0x82, 0x40);
Hrs3602_write_reg(0x85, 0x00);
}
void Hrs3602_chip_disable(void)
{
Hrs3602_write_reg(0x09, 0x03);
}
void Hrs3602_low_power(void)
{
Hrs3602_write_reg(0x02, 0x70);
Hrs3602_write_reg(0x15, 0x40);
// Hrs3602_write_reg(0xc0, 0x87); /* led dr config , 0x84 = 12.5mA , 0x85 = 25mA ,0x86 = 50 mA ,0x87 = 100mA */
Hrs3602_write_reg(0x16, 0x31); // detect interval is 1s when loss
}
void Hrs3602_normal_power(void)
{
Hrs3602_write_reg(0x02, 0x77);
// Hrs3602_write_reg(0xc0, 0x87);
Hrs3602_write_reg(0x16, 0x00); // detect interval is 40mS
}
void Hrs3602_int_clear(void)
{
Hrs3602_write_reg(0x06, 0x7f);
return;
}
int tjd_hrx3602_init(void)
{
int ret;
if((ret = Hrs3602_chip_init()) != 0){
static_print_info("Hrs3602_chip_init fail");
return -1;
}else{
static_print_info("Hrs3602_chip_init succ");
}
Hrs3602_alg_config();
Hrs3602_driv_init();
tyhx_hrs_alg_open(); //算法初始化
return 0;
}
hrsensor_data_t tjd_hrx3602_processing_handle(int16_t *gsensor_data)
{
int32_t hrm_raw_data;
int32_t als_raw_data;
int32_t infrared_data;
hrsensor_data_t hrsensor_data;
hr_wear_status_t wear_status = MSG_NO_TOUCH;
hrs_results_t hr_alg_results = {MSG_HRS_ALG_NOT_OPEN, 0, 0, 0, 0, 0, 0, 0};
bp_results_t bp_alg_results = {MSG_BP_ALG_NOT_OPEN, 0, 0, 0, 0, false};
Hrs3602_read_hrs(&hrm_raw_data, &als_raw_data);
Hrs3602_read_ps1(&infrared_data);
// static_print_info("hrm_raw_data:%d als_raw_data:%d", hrm_raw_data, als_raw_data);
// static_print_info("gsensor_data:%d %d %d\n", gsensor_data[0], gsensor_data[1], gsensor_data[2]);
wear_status = Hrs3602_agc(als_raw_data, infrared_data);
if (wear_status == MSG_TOUCH)
{
tyhx_hrs_alg_send_data(&hrm_raw_data, 1, gsensor_data+0,gsensor_data+1,gsensor_data+2);
hr_alg_results = tyhx_hrs_alg_get_results();
bp_alg_results = tyhx_alg_get_bp_results();
}
// static_print_info("HR=%d, wear_status=%d", hr_alg_results.hr_result, wear_status);
hrsensor_data.wear_status = wear_status;
hrsensor_data.hr_result = hr_alg_results.hr_result;
hrsensor_data.sbp_result = bp_alg_results.sbp;
hrsensor_data.dbp_result = bp_alg_results.dbp;
return hrsensor_data;
}