From dbf25246f880193f1acaa67ba0c014b01639d368 Mon Sep 17 00:00:00 2001 From: penglei <1624952103@qq.com> Date: Tue, 24 Dec 2024 21:12:24 +0800 Subject: [PATCH] =?utf8?q?=E6=B7=BB=E5=8A=A0u8=EF=BC=8Cl22=20=E5=BA=95?= =?utf8?q?=E5=B1=82=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../brandy/brandy_standard/service_init.c | 24 + .../brandy/brandy_standard/thread_pri_init.c | 7 + .../config/target_config/brandy/config.py | 24 +- .../brandy_evb/board_config/board_lt22.h | 22 +- .../boards/brandy_evb/board_config/board_u8.h | 9 + code/sdk/zhihui/drivers/CMakeLists.txt | 5 + .../zhihui/drivers/barometer/CMakeLists.txt | 44 + .../drivers/barometer/driver/lqbp002ta.c | 496 +++++ .../drivers/barometer/driver/lqbp002ta.h | 62 + .../drivers/barometer/service/lqbp_at_cmd.c | 74 + .../drivers/barometer/service/lqbp_at_cmd.h | 40 + .../drivers/barometer/service/lqbp_service.c | 173 ++ .../drivers/barometer/service/lqbp_service.h | 45 + code/sdk/zhihui/drivers/charge/CMakeLists.txt | 46 + .../sdk/zhihui/drivers/charge/hp4570/hp4570.c | 766 +++++++ .../sdk/zhihui/drivers/charge/hp4570/hp4570.h | 505 +++++ .../drivers/charge/service/charge_at_cmd.c | 99 + .../drivers/charge/service/charge_at_cmd.h | 40 + .../drivers/charge/service/charge_service.c | 185 ++ .../drivers/charge/service/charge_service.h | 54 + code/sdk/zhihui/drivers/mag/mmc5603/MMC5603.c | 36 +- .../drivers/mag/mmc5603/mmc5603_at_cmd.c | 8 + .../zhihui/drivers/mag/mmc5603/mmc5603_hal.h | 3 + .../zhihui/drivers/mag/mmc5603/mmc5603_main.c | 34 + .../zhihui/drivers/mag/mmc5603/mmc_customer.h | 10 +- .../zhihui/drivers/mag/service/mag_service.c | 32 +- .../zhihui/drivers/mag/service/mag_service.h | 8 + .../zhihui/drivers/ppg/service/CMakeLists.txt | 1 + .../zhihui/drivers/ppg/service/ppg_at_cmd.cpp | 8 + .../drivers/ppg/service/ppg_service.cpp | 151 +- .../zhihui/drivers/ppg/service/ppg_service.h | 7 +- .../zhihui/drivers/ppg/vc9202/CMakeLists.txt | 63 + .../drivers/ppg/vc9202/inc/BIODetect_V15_1.h | 16 + code/sdk/zhihui/drivers/ppg/vc9202/inc/algo.h | 70 + .../zhihui/drivers/ppg/vc9202/inc/spo2Algo.h | 14 + .../ppg/vc9202/inc/vc30x91x92x_common.h | 87 + .../drivers/ppg/vc9202/inc/vc9202_common.h | 77 + .../drivers/ppg/vc9202/inc/vc9202_driver.h | 184 ++ .../drivers/ppg/vc9202/inc/vc9202_hal.h | 56 + .../drivers/ppg/vc9202/inc/vc9202_user.h | 58 + .../ppg/vc9202/inc/vcSportMotionIntAlgo.h | 13 + .../zhihui/drivers/ppg/vc9202/lib/.WeDrive | 1 + .../vc9202/lib/lib_BIO_V151_MiniSdk_NA_01.a | Bin 0 -> 18232 bytes .../lib/lib_BIO_v151_int_miniSDK_log_01.a | Bin 0 -> 20668 bytes .../lib/lib_BIO_v151_int_miniSDK_log_02.a | Bin 0 -> 20796 bytes .../ppg/vc9202/lib/lib_Sp_Int_HaiSi_01.a | Bin 0 -> 16452 bytes .../lib/lib_hr_v640ed04_miniSDK_NA_01.a | Bin 0 -> 351376 bytes .../lib/lib_spo2_9202A4_limit_HaiSi_NA_01.a | Bin 0 -> 162592 bytes .../drivers/ppg/vc9202/src/vc9202_driver.c | 1621 ++++++++++++++ .../ppg/vc9202/src/vc9202_driver_testmode.c | 176 ++ .../drivers/ppg/vc9202/src/vc9202_hal.c | 211 ++ .../drivers/ppg/vc9202/src/vc9202_user.c | 569 +++++ .../ppg/vc9202/src/vc9202_user_debug.c | 188 ++ .../zhihui/drivers/ppg/vc921x/CMakeLists.txt | 62 + .../drivers/ppg/vc921x/inc/BIODetect_V14_1.h | 19 + code/sdk/zhihui/drivers/ppg/vc921x/inc/algo.h | 70 + .../drivers/ppg/vc921x/inc/spo2Algo_16bit.h | 57 + .../drivers/ppg/vc921x/inc/vc921x_core.h | 325 +++ .../drivers/ppg/vc921x/inc/vc921x_core_com.h | 458 ++++ .../drivers/ppg/vc921x/inc/vc921x_driver.h | 127 ++ .../ppg/vc921x/inc/vc921x_driver_com.h | 148 ++ .../ppg/vc921x/inc/vc921x_driver_config.h | 115 + .../drivers/ppg/vc921x/inc/vc921x_hal.h | 53 + .../ppg/vc921x/inc/vc921x_sample_result.h | 158 ++ .../drivers/ppg/vc921x/inc/vc921x_user_app.h | 47 + .../ppg/vc921x/inc/vcSportMotionIntAlgo.h | 12 + .../drivers/ppg/vc921x/inc/vcare_device_com.h | 200 ++ .../zhihui/drivers/ppg/vc921x/lib/.WeDrive | 1 + .../lib/lib_BIO_v141_int_miniSDK_NA_01.a | Bin 0 -> 19284 bytes .../lib/lib_hr_vH102_int_miniSDK_NA_01.a | Bin 0 -> 353584 bytes .../lib_spo2_vc30FS_float_V07_miniSDK_NA_01.a | Bin 0 -> 184840 bytes .../lib_sportMotion_std_int_miniSDK_NA_01.a | Bin 0 -> 16730 bytes .../drivers/ppg/vc921x/src/vc921x_core.c | 956 +++++++++ .../drivers/ppg/vc921x/src/vc921x_driver.c | 1907 +++++++++++++++++ .../drivers/ppg/vc921x/src/vc921x_hal.c | 75 + .../vc921x/src/vc921x_original_data_send.c | 267 +++ .../drivers/ppg/vc921x/src/vc921x_user_app.c | 897 ++++++++ .../drivers/rotary_encoder/CMakeLists.txt | 11 +- .../rotary_encoder/hx3509/hx3509_at_cmd.c | 104 + .../rotary_encoder/hx3509/hx3509_at_cmd.h | 40 + .../rotary_encoder/hx3509/hx3509_hal.c | 131 ++ .../rotary_encoder/hx3509/hx3509_hal.h | 32 + .../hx3509/tyhx_OTS_hx3509_custom.c | 687 ++++++ .../hx3509/tyhx_OTS_hx3509_custom.h | 189 ++ .../rotary_encoder/rotary_encoder_drv.c | 11 +- code/sdk/zhihui/utils/custom_utils.c | 2 + 86 files changed, 13507 insertions(+), 76 deletions(-) create mode 100644 code/sdk/zhihui/drivers/barometer/CMakeLists.txt create mode 100644 code/sdk/zhihui/drivers/barometer/driver/lqbp002ta.c create mode 100644 code/sdk/zhihui/drivers/barometer/driver/lqbp002ta.h create mode 100644 code/sdk/zhihui/drivers/barometer/service/lqbp_at_cmd.c create mode 100644 code/sdk/zhihui/drivers/barometer/service/lqbp_at_cmd.h create mode 100644 code/sdk/zhihui/drivers/barometer/service/lqbp_service.c create mode 100644 code/sdk/zhihui/drivers/barometer/service/lqbp_service.h create mode 100644 code/sdk/zhihui/drivers/charge/CMakeLists.txt create mode 100644 code/sdk/zhihui/drivers/charge/hp4570/hp4570.c create mode 100644 code/sdk/zhihui/drivers/charge/hp4570/hp4570.h create mode 100644 code/sdk/zhihui/drivers/charge/service/charge_at_cmd.c create mode 100644 code/sdk/zhihui/drivers/charge/service/charge_at_cmd.h create mode 100644 code/sdk/zhihui/drivers/charge/service/charge_service.c create mode 100644 code/sdk/zhihui/drivers/charge/service/charge_service.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/CMakeLists.txt create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/inc/BIODetect_V15_1.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/inc/algo.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/inc/spo2Algo.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/inc/vc30x91x92x_common.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_common.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_driver.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_hal.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_user.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/inc/vcSportMotionIntAlgo.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/lib/.WeDrive create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_BIO_V151_MiniSdk_NA_01.a create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_BIO_v151_int_miniSDK_log_01.a create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_BIO_v151_int_miniSDK_log_02.a create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_Sp_Int_HaiSi_01.a create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_hr_v640ed04_miniSDK_NA_01.a create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_spo2_9202A4_limit_HaiSi_NA_01.a create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_driver.c create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_driver_testmode.c create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_hal.c create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_user.c create mode 100644 code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_user_debug.c create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/CMakeLists.txt create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/BIODetect_V14_1.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/algo.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/spo2Algo_16bit.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_core.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_core_com.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver_com.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver_config.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_hal.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_sample_result.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_user_app.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/vcSportMotionIntAlgo.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/inc/vcare_device_com.h create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/lib/.WeDrive create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/lib/lib_BIO_v141_int_miniSDK_NA_01.a create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/lib/lib_hr_vH102_int_miniSDK_NA_01.a create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/lib/lib_spo2_vc30FS_float_V07_miniSDK_NA_01.a create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/lib/lib_sportMotion_std_int_miniSDK_NA_01.a create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_core.c create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_driver.c create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_hal.c create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_original_data_send.c create mode 100644 code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_user_app.c create mode 100644 code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_at_cmd.c create mode 100644 code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_at_cmd.h create mode 100644 code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_hal.c create mode 100644 code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_hal.h create mode 100644 code/sdk/zhihui/drivers/rotary_encoder/hx3509/tyhx_OTS_hx3509_custom.c create mode 100644 code/sdk/zhihui/drivers/rotary_encoder/hx3509/tyhx_OTS_hx3509_custom.h diff --git a/code/sdk/application/brandy/brandy_standard/service_init.c b/code/sdk/application/brandy/brandy_standard/service_init.c index d6ce281ce..37ca92674 100644 --- a/code/sdk/application/brandy/brandy_standard/service_init.c +++ b/code/sdk/application/brandy/brandy_standard/service_init.c @@ -77,6 +77,15 @@ #ifdef CONFIG_OTA_UPDATE_SUPPORT #include "upg.h" #endif +#ifdef SUPPORT_AT_HX3509 +#include "hx3509_at_cmd.h" +#endif +#ifdef SUPPORT_AT_LQBP002TA +#include "lqbp_at_cmd.h" +#endif +#ifdef SUPPORT_AT_CHARGE +#include "charge_at_cmd.h" +#endif static void register_at_service(void) { #ifdef SUPPORT_AT_GRAPHIC @@ -170,6 +179,21 @@ static void register_at_service(void) at_cmd_table_t *rtc_table = get_rtc_cmd_table(&rtc_cmd_count); at_cmd_add_moudle(rtc_table, rtc_cmd_count); #endif +#ifdef SUPPORT_AT_HX3509 + uint32_t hx3509_cmd_count; + at_cmd_table_t *hx3509_table = get_hx3509_cmd_table(&hx3509_cmd_count); + at_cmd_add_moudle(hx3509_table, hx3509_cmd_count); +#endif +#ifdef SUPPORT_AT_LQBP002TA + uint32_t lqbp002ta_cmd_count; + at_cmd_table_t *lqbp002ta_table = get_lqbp002ta_cmd_table(&lqbp002ta_cmd_count); + at_cmd_add_moudle(lqbp002ta_table, lqbp002ta_cmd_count); +#endif +#ifdef SUPPORT_AT_CHARGE + uint32_t charge_cmd_count; + at_cmd_table_t *charge_table = get_charge_cmd_table(&charge_cmd_count); + at_cmd_add_moudle(charge_table, charge_cmd_count); +#endif } #ifdef CONFIG_OTA_UPDATE_SUPPORT diff --git a/code/sdk/application/brandy/brandy_standard/thread_pri_init.c b/code/sdk/application/brandy/brandy_standard/thread_pri_init.c index 9b37131b7..30847a3ed 100644 --- a/code/sdk/application/brandy/brandy_standard/thread_pri_init.c +++ b/code/sdk/application/brandy/brandy_standard/thread_pri_init.c @@ -119,6 +119,10 @@ #include "nv.h" #include "nv_upg.h" +#ifdef ENABLE_LQBP002TA +#include "lqbp_service.h" +#endif + #define EMMC_BASE_ADDR 0x52062000 #define TASK_PRIORITY_APP (osPriority_t)(17) #define TICK_TEN_MS (10U) @@ -514,6 +518,9 @@ uapi_pm_remove_sleep_veto(PM_ID_DEBUG); uapi_cywee_service_init(); #endif + #ifdef ENABLE_LQBP002TA + uapi_lqbp_service_init(); + #endif } #endif diff --git a/code/sdk/build/config/target_config/brandy/config.py b/code/sdk/build/config/target_config/brandy/config.py index df3a3bfaa..7c009aade 100644 --- a/code/sdk/build/config/target_config/brandy/config.py +++ b/code/sdk/build/config/target_config/brandy/config.py @@ -430,7 +430,7 @@ target = { 'FT_SINGLE_UART', # 'WEARABLE_LOG_DEBUG', 'SUPPORT_RC_CALIBRATION', - 'PPG_HX3695', + #'PPG_HX3695', 'VIB_USE_PWM', 'CONFIG_PWM_SUPPORT', # 'ACC_LIS2DOC', @@ -466,6 +466,8 @@ target = { 'SUPPORT_AUTO_OTA_SERVER', 'CONFIG_DFX_SUPPORT_DIAG_UP_MACHINE=1', 'TPTYPE_CST86XX', + 'PPG_VC9202', + 'ROTARYENCODER_HX3507', 'NANDFLASH_DS35X' ], 'ram_component': [ @@ -497,7 +499,10 @@ target = { # "hid_service", "gnss", "gnss_sample", - "cywee" + "cywee", + "lqbp002ta", + "ppg_sensor_vc9202", + "charge_ic" ], 'image_analysis': False, 'fs_image_path':'evb', @@ -532,7 +537,8 @@ target = { 'defines': [ 'BOARD_W9', 'FT_SINGLE_UART', - 'SUPPORT_AUTO_OTA_SERVER', + 'SUPPORT_AUTO_OTA_SERVER', + 'CFG_FLASH_SIZE=8', 'CONFIG_DFX_SUPPORT_DIAG_UP_MACHINE=1', 'TPTYPE_CST86XX', 'GNSS_UART1', @@ -546,6 +552,7 @@ target = { #自定义配置项,选择硬件及功能参数 'defines': [ 'BOARD_W9', + 'CFG_FLASH_SIZE=8', 'FT_SINGLE_UART', 'NANDFLASH_F35UQA' ] @@ -556,6 +563,7 @@ target = { 'defines': [ 'BOARD_W9', 'FT_SINGLE_UART', + 'CFG_FLASH_SIZE=8', 'GPIO_POWERON_FLASH', 'NANDFLASH_F35UQA' ] @@ -567,7 +575,7 @@ target = { 'FT_SINGLE_UART', # 'WEARABLE_LOG_DEBUG', 'SUPPORT_RC_CALIBRATION', - 'PPG_HX3695', + #'PPG_HX3695', 'VIB_USE_PWM', 'CONFIG_PWM_SUPPORT', # 'ACC_LIS2DOC', @@ -603,7 +611,9 @@ target = { 'GPS_CAT1_RESET_TEST', 'TPTYPE_CST86XX', 'TEST_4G_UI', - 'NANDFLASH_F35UQA' + 'NANDFLASH_F35UQA', + 'PPG_VC921X', + 'ROTARYENCODER_HX3509' ], 'ram_component': [ #'baidumapsdk', @@ -634,7 +644,9 @@ target = { # "blemoder", # "factorytest_service", # "hid_service", - "cywee" + "cywee", + "charge_ic", + "ppg_sensor_vc921x", ], 'image_analysis': False, 'fs_image_path':'evb', diff --git a/code/sdk/drivers/boards/brandy_evb/board_config/board_lt22.h b/code/sdk/drivers/boards/brandy_evb/board_config/board_lt22.h index 4b23b3101..ec77f3cd7 100644 --- a/code/sdk/drivers/boards/brandy_evb/board_config/board_lt22.h +++ b/code/sdk/drivers/boards/brandy_evb/board_config/board_lt22.h @@ -11,6 +11,10 @@ #include "gpio_porting.h" #include "platform_types.h" +#ifdef PPG_VC921X +extern void ppg_int_callback(pin_t pin, uintptr_t param); +#endif + /** * @addtogroup connectivity_config_boards @@ -76,9 +80,15 @@ const hal_pio_config_t g_pio_function_config[PIN_MAX_NUMBER] = { { HAL_PIO_FUNC_EMMC, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_UP, HAL_PIO_IE_ENBALE }, // S_HGPIO14 { HAL_PIO_FUNC_EMMC, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_MAX, HAL_PIO_IE_DISABLE }, // S_HGPIO15 emmc clk #endif + { HAL_PIO_FUNC_SPI0, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_DOWN, HAL_PIO_IE_DISABLE }, // S_MGPIO0 NC +#ifdef BOARD_L22 + { HAL_PIO_FUNC_I2C2_M3, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_UP, HAL_PIO_IE_ENBALE }, // S_MGPIO1 I2C2 SCL Sensor 6è½´ 指南针 心率 + { HAL_PIO_FUNC_I2C2_M3, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_UP, HAL_PIO_IE_ENBALE }, // S_MGPIO2 I2C2 SDA Sensor +#else { HAL_PIO_FUNC_SPI0, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_DOWN, HAL_PIO_IE_DISABLE }, // S_MGPIO1 NC { HAL_PIO_FUNC_SPI0, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_DOWN, HAL_PIO_IE_DISABLE }, // S_MGPIO2 NC +#endif { HAL_PIO_FUNC_SPI0, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_DOWN, HAL_PIO_IE_DISABLE }, // S_MGPIO3 NC // #if (defined BRANDY_PRODUCT_EVB4) @@ -98,8 +108,13 @@ const hal_pio_config_t g_pio_function_config[PIN_MAX_NUMBER] = { { HAL_PIO_FUNC_I2C0_M1, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_MAX, HAL_PIO_IE_ENBALE }, // S_MGPIO12 I2C0 SDA { HAL_PIO_FUNC_I2C1_M1, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_MAX, HAL_PIO_IE_ENBALE }, // S_MGPIO13 I2C1 SCL touch i2c { HAL_PIO_FUNC_I2C1_M1, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_MAX, HAL_PIO_IE_ENBALE }, // S_MGPIO14 I2C1 SDA - { HAL_PIO_FUNC_I2C2_M1, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_MAX, HAL_PIO_IE_ENBALE }, // S_MGPIO15 I2C2 SCL Sensor 6è½´ 指南针 心率 - { HAL_PIO_FUNC_I2C2_M1, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_MAX, HAL_PIO_IE_ENBALE }, // S_MGPIO16 I2C2 SDA Sensor +#ifdef BOARD_L22 + { HAL_PIO_FUNC_GPIO, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_DOWN, HAL_PIO_IE_DISABLE }, // S_MGPIO15 I2C2 SCL Sensor 6è½´ 指南针 心率 + { HAL_PIO_FUNC_GPIO, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_DOWN, HAL_PIO_IE_DISABLE }, // S_MGPIO16 I2C2 SDA Sensor +#else + { HAL_PIO_FUNC_I2C2_M1, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_UP, HAL_PIO_IE_ENBALE }, // S_MGPIO15 I2C2 SCL Sensor 6è½´ 指南针 心率 + { HAL_PIO_FUNC_I2C2_M1, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_UP, HAL_PIO_IE_ENBALE }, // S_MGPIO16 I2C2 SDA Sensor +#endif { HAL_PIO_FUNC_I2C3_M1, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_MAX, HAL_PIO_IE_ENBALE }, // S_MGPIO17 i2c3 SCL smart pa\ 预留马达控制 \安全芯片 { HAL_PIO_FUNC_I2C3_M1, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_MAX, HAL_PIO_IE_ENBALE }, // S_MGPIO18 I2C3 SDA { HAL_PIO_FUNC_UART1, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_MAX, HAL_PIO_IE_DISABLE }, // S_MGPIO19 uart1 tx @@ -313,6 +328,9 @@ const hal_gpio_config_t g_gpio_function_config[PIN_MAX_NUMBER] = { /**S_AGPIO_L32 GPO gps reset*/ // { S_AGPIO_L32, HAL_GPIO_DIRECTION_OUTPUT, HAL_GPIO_LEVEL_LOW, 0, NULL }, +#ifdef PPG_VC921X + { S_AGPIO_R6, HAL_GPIO_DIRECTION_INPUT, NULL, GPIO_INTERRUPT_RISING_EDGE, ppg_int_callback}, +#endif }; /** diff --git a/code/sdk/drivers/boards/brandy_evb/board_config/board_u8.h b/code/sdk/drivers/boards/brandy_evb/board_config/board_u8.h index 6387590c3..f188974e3 100644 --- a/code/sdk/drivers/boards/brandy_evb/board_config/board_u8.h +++ b/code/sdk/drivers/boards/brandy_evb/board_config/board_u8.h @@ -11,6 +11,10 @@ #include "gpio_porting.h" #include "platform_types.h" +#ifdef PPG_VC9202 +extern void ppg_int_callback(pin_t pin, uintptr_t param); +#endif + #ifdef ENABLE_CYWEE extern void cywee_int_callback(pin_t pin, uintptr_t param); #endif @@ -313,6 +317,11 @@ const hal_gpio_config_t g_gpio_function_config[PIN_MAX_NUMBER] = { #ifdef SUPPORT_A5133_SERVICE //{ S_AGPIO_L28, HAL_GPIO_DIRECTION_INPUT, NULL, GPIO_INTERRUPT_RISING_EDGE, a5133_cko_isr_callback}, #endif + +#ifdef PPG_VC9202 + { S_AGPIO_R6, HAL_GPIO_DIRECTION_INPUT, NULL, GPIO_INTERRUPT_RISING_EDGE, ppg_int_callback}, +#endif + }; /** * @} diff --git a/code/sdk/zhihui/drivers/CMakeLists.txt b/code/sdk/zhihui/drivers/CMakeLists.txt index 039208b13..db812e45f 100644 --- a/code/sdk/zhihui/drivers/CMakeLists.txt +++ b/code/sdk/zhihui/drivers/CMakeLists.txt @@ -6,6 +6,8 @@ add_subdirectory_if_exist(acc) add_subdirectory_if_exist(mag) add_subdirectory_if_exist(ppg) +add_subdirectory_if_exist(ppg/vc9202) +add_subdirectory_if_exist(ppg/vc921x) add_subdirectory_if_exist(vibrator) add_subdirectory_if_exist(slip) add_subdirectory_if_exist(slip/cat1Service) @@ -14,3 +16,6 @@ add_subdirectory_if_exist(vibrator/service) add_subdirectory_if_exist(cywee) add_subdirectory_if_exist(a5133) add_subdirectory_if_exist(entest) +add_subdirectory_if_exist(barometer) +add_subdirectory_if_exist(charge) +add_subdirectory_if_exist(charge/hp4570) diff --git a/code/sdk/zhihui/drivers/barometer/CMakeLists.txt b/code/sdk/zhihui/drivers/barometer/CMakeLists.txt new file mode 100644 index 000000000..24a0ffc8e --- /dev/null +++ b/code/sdk/zhihui/drivers/barometer/CMakeLists.txt @@ -0,0 +1,44 @@ +#=============================================================================== +# @brief cmake file +# Copyright (c) @CompanyNameMagicTag 2023-2023. All rights reserved. +#=============================================================================== +set(COMPONENT_NAME "lqbp002ta") + +set(SOURCES ${SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/driver/lqbp002ta.c + ${CMAKE_CURRENT_SOURCE_DIR}/service/lqbp_at_cmd.c + ${CMAKE_CURRENT_SOURCE_DIR}/service/lqbp_service.c +) +set(PUBLIC_HEADER + ${CMAKE_CURRENT_SOURCE_DIR}/driver + ${CMAKE_CURRENT_SOURCE_DIR}/service +) +set(PUBLIC_DEFINES + SUPPORT_AT_LQBP002TA + ENABLE_LQBP002TA +) + +set(PRIVATE_HEADER +) + +set(PRIVATE_DEFINES +) + +# use this when you want to add ccflags like -include xxx +set(COMPONENT_PUBLIC_CCFLAGS +) + +set(COMPONENT_CCFLAGS + -Wno-incompatible-function-pointer-types + -Wno-unused-function +) + +set(WHOLE_LINK + true +) + +set(MAIN_COMPONENT + false +) + +build_component() \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/barometer/driver/lqbp002ta.c b/code/sdk/zhihui/drivers/barometer/driver/lqbp002ta.c new file mode 100644 index 000000000..930518b46 --- /dev/null +++ b/code/sdk/zhihui/drivers/barometer/driver/lqbp002ta.c @@ -0,0 +1,496 @@ +#include "lqbp002ta.h" +#include "custom_utils.h" +#include "platform_core.h" + +#define LQBP002TA_I2C_BUS I2C_BUS_2 + +static struct LQBP002TA_t LQBP002TA = {0}; +static struct LQBP002TA_t *p_LQBP002TA = NULL; + +void LQBP002TA_write(uint8_t hwadr, uint8_t regadr, uint8_t val); +uint8_t LQBP002TA_read(uint8_t hwadr, uint8_t regadr); +void LQBP002TA_get_calib_param(void); + +/***************************************************************************** + Function: LQBP002TA_write + Description: this function will write data to specofic register through software I2C bus + Input: uint8_t hwadr hardware I2C address + uint8_t regadr register address + uint8_t val write-in value + Output: + Return: + Calls: + Called By: +*****************************************************************************/ +void LQBP002TA_write(uint8_t hwadr, uint8_t regadr, uint8_t val) +{ + // SoftI2C_start(); + // SoftI2C_write_byte(hwadr << 1); + // SoftI2C_write_byte(regadr); + // SoftI2C_write_byte(val); + // SoftI2C_stop(); + custom_i2c_write_reg(hwadr, regadr, 1, &val, 1, LQBP002TA_I2C_BUS); +} + +/***************************************************************************** + Function: LQBP002TA_read + Description: this function will read register data through software I2C bus + Input: uint8_t hwadr hardware I2C address + uint8_t regadr register address + Output: + Return: uint8_t readout value + Calls: + Called By: +*****************************************************************************/ +uint8_t LQBP002TA_read(uint8_t hwadr, uint8_t regadr) +{ + uint8_t val = 0; + // SoftI2C_start(); + // SoftI2C_write_byte(hwadr << 1); + // SoftI2C_write_byte(regadr); + // SoftI2C_start(); + // SoftI2C_write_byte((hwadr << 1)|0x01); + // val = SoftI2C_read_byte(1); + // SoftI2C_stop(); + custom_i2c_read_regs(hwadr, regadr, 1, &val, 1, LQBP002TA_I2C_BUS); + return val; +} + +void read_chip_id(void) +{ + p_LQBP002TA->chip_id = LQBP002TA_read(HW_ADR, 0x0D); + //printf("check chip id =0x%x\n",p_LQBP002TA->chip_id); +} + +uint8_t is_LQBP002TA(void) +{ + if(p_LQBP002TA->chip_id == 0x11) + return 1; + else + return 0; +} + +/***************************************************************************** + Function: LQBP002TA_init + Description: initialization + Input: void + Output: + Return: void + Calls: + Called By: +*****************************************************************************/ +void LQBP002TA_init(void) +{ + p_LQBP002TA = &LQBP002TA; /* read Chip Id */ + p_LQBP002TA->i32rawPressure = 0; + p_LQBP002TA->i32rawTemperature = 0; + // p_LQBP002TA->chip_id = 0x11; + read_chip_id(); + + LQBP002TA_get_calib_param(); + // sampling rate = 2Hz; Pressure oversample = 4; + LQBP002TA_rateset(PRESSURE_SENSOR,2, 4); + // sampling rate = 2Hz; Temperature oversample = 2; + LQBP002TA_rateset(TEMPERATURE_SENSOR,2, 2); + //Start background measurement + +} + +/***************************************************************************** + Function: LQBP002TA_rateset + Description: set sample rate and over sample rate per second for specific sensor + Input: uint8_t u8OverSmpl oversample rate Maximal = 128 + uint8_t u8SmplRate sample rate(Hz) Maximal = 128 + uint8_t iSensor 0: Pressure; 1: Temperature + Output: + Return: void + Calls: + Called By: +*****************************************************************************/ +void LQBP002TA_rateset(uint8_t iSensor, uint8_t u8SmplRate, uint8_t u8OverSmpl) +{ + uint8_t reg = 0; + int32_t i32kPkT = 0; + switch(u8SmplRate) + { + case 2: + reg |= (1<<4); + break; + case 4: + reg |= (2<<4); + break; + case 8: + reg |= (3<<4); + break; + case 16: + reg |= (4<<4); + break; + case 32: + reg |= (5<<4); + break; + case 64: + reg |= (6<<4); + break; + case 128: + reg |= (7<<4); + break; + case 1: + default: + break; + } + switch(u8OverSmpl) + { + case 2: + reg |= 1; + i32kPkT = 1572864; + break; + case 4: + reg |= 2; + i32kPkT = 3670016; + break; + case 8: + reg |= 3; + i32kPkT = 7864320; + break; + case 16: + i32kPkT = 253952; + reg |= 4; + break; + case 32: + i32kPkT = 516096; + reg |= 5; + break; + case 64: + i32kPkT = 1040384; + reg |= 6; + break; + case 128: + i32kPkT = 2088960; + reg |= 7; + break; + case 1: + default: + i32kPkT = 524288; + break; + } + + if(iSensor == PRESSURE_SENSOR) + { + p_LQBP002TA->i32kP = i32kPkT; + //printf("i32kP : %d \n",p_LQBP002TA->i32kP); + LQBP002TA_write(HW_ADR, 0x06, reg); + if(u8OverSmpl > 8) + { + reg = LQBP002TA_read(HW_ADR, 0x09); + //printf("0x09 : %x \n",reg); + LQBP002TA_write(HW_ADR, 0x09, reg | 0x04); + } + else + { + reg = LQBP002TA_read(HW_ADR, 0x09); + //printf("0x09 : %x \n",reg); + LQBP002TA_write(HW_ADR, 0x09, reg & (~0x04)); + } + } + if(iSensor == TEMPERATURE_SENSOR) + { + p_LQBP002TA->i32kT = i32kPkT; + //printf("i32kT : %d \n",p_LQBP002TA->i32kT); + if(is_LQBP002TA()) + LQBP002TA_write(HW_ADR, 0x07, reg); + else + LQBP002TA_write(HW_ADR, 0x07, reg|0x80); //Using mems temperature + if(u8OverSmpl > 8) + { + reg = LQBP002TA_read(HW_ADR, 0x09); + //printf("0x09 : %x \n",reg); + LQBP002TA_write(HW_ADR, 0x09, reg | 0x08); + } + else + { + reg = LQBP002TA_read(HW_ADR, 0x09); + //printf("0x09 : %x \n",reg); + LQBP002TA_write(HW_ADR, 0x09, reg & (~0x08)); + } + } +} + +/***************************************************************************** + Function: LQBP002TA_get_calib_param + Description: obtain the calibrated coefficient + Input: void + Output: + Return: void + Calls: + Called By: +*****************************************************************************/ +void LQBP002TA_get_calib_param(void) +{ + uint8_t h; + uint8_t m; + uint8_t l; + h = LQBP002TA_read(HW_ADR, 0x10); + l = LQBP002TA_read(HW_ADR, 0x11); + p_LQBP002TA->calib_param.c0 = (int16_t)h<<4 | l>>4; + p_LQBP002TA->calib_param.c0 = (p_LQBP002TA->calib_param.c0&0x0800)?(0xF000|p_LQBP002TA->calib_param.c0):p_LQBP002TA->calib_param.c0; + //printf("0x10 : %x \n",h); + //printf("0x11 : %x \n",l); + //printf("c0 : %d \n",p_LQBP002TA->calib_param.c0); + + h = LQBP002TA_read(HW_ADR, 0x11); + l = LQBP002TA_read(HW_ADR, 0x12); + + p_LQBP002TA->calib_param.c1 = (int16_t)(h&0x0F)<<8 | l; + p_LQBP002TA->calib_param.c1 = (p_LQBP002TA->calib_param.c1&0x0800)?(0xF000|p_LQBP002TA->calib_param.c1):p_LQBP002TA->calib_param.c1; + //printf("0x11 : %x \n",h); + //printf("0x12 : %x \n",l); + //printf("c1 : %d \n",p_LQBP002TA->calib_param.c1); + + h = LQBP002TA_read(HW_ADR, 0x13); + m = LQBP002TA_read(HW_ADR, 0x14); + l = LQBP002TA_read(HW_ADR, 0x15); + p_LQBP002TA->calib_param.c00 = (int32_t)h<<12 | (int32_t)m<<4 | (int32_t)l>>4; + p_LQBP002TA->calib_param.c00 = (p_LQBP002TA->calib_param.c00&0x080000)?(0xFFF00000|p_LQBP002TA->calib_param.c00):p_LQBP002TA->calib_param.c00; + //printf("0x13 : %x \n",h); + //printf("0x14 : %x \n",m); + //printf("0x15 : %x \n",l); + //printf("c00 : %d \n",p_LQBP002TA->calib_param.c00); + + h = LQBP002TA_read(HW_ADR, 0x15); + m = LQBP002TA_read(HW_ADR, 0x16); + l = LQBP002TA_read(HW_ADR, 0x17); + p_LQBP002TA->calib_param.c10 = (int32_t)(h&0x0F)<<16 | (int32_t)m<<8 | l; + p_LQBP002TA->calib_param.c10 = (p_LQBP002TA->calib_param.c10&0x080000)?(0xFFF00000|p_LQBP002TA->calib_param.c10):p_LQBP002TA->calib_param.c10; + //printf("0x15 : %x \n",h); + //printf("0x16 : %x \n",m); + //printf("0x17 : %x \n",l); + //printf("c10 : %d \n",p_LQBP002TA->calib_param.c10); + + h = LQBP002TA_read(HW_ADR, 0x18); + l = LQBP002TA_read(HW_ADR, 0x19); + p_LQBP002TA->calib_param.c01 = (int16_t)h<<8 | l; + //printf("0x18 : %x \n",h); + //printf("0x19 : %x \n",l); + //printf("c01 : %d \n",p_LQBP002TA->calib_param.c01); + + h = LQBP002TA_read(HW_ADR, 0x1A); + l = LQBP002TA_read(HW_ADR, 0x1B); + p_LQBP002TA->calib_param.c11 = (int16_t)h<<8 | l; + //printf("0x1A : %x \n",h); + //printf("0x1B : %x \n",l); + //printf("c11 : %d \n",p_LQBP002TA->calib_param.c11); + + h = LQBP002TA_read(HW_ADR, 0x1C); + l = LQBP002TA_read(HW_ADR, 0x1D); + p_LQBP002TA->calib_param.c20 = (int16_t)h<<8 | l; + //printf("0x1C : %x \n",h); + //printf("0x1D : %x \n",l); + //printf("c20 : %d \n",p_LQBP002TA->calib_param.c20); + + h = LQBP002TA_read(HW_ADR, 0x1E); + l = LQBP002TA_read(HW_ADR, 0x1F); + p_LQBP002TA->calib_param.c21 = (int16_t)h<<8 | l; + //printf("0x1E : %x \n",h); + //printf("0x1F : %x \n",l); + //printf("c21 : %d \n",p_LQBP002TA->calib_param.c21); + + h = LQBP002TA_read(HW_ADR, 0x20); + l = LQBP002TA_read(HW_ADR, 0x21); + p_LQBP002TA->calib_param.c30 = (int16_t)h<<8 | l; + //printf("0x20 : %x \n",h); + //printf("0x21 : %x \n",l); + //printf("c30 : %d \n",p_LQBP002TA->calib_param.c30); + + if(is_LQBP002TA()) + { + h = LQBP002TA_read(HW_ADR, 0x22); + l = LQBP002TA_read(HW_ADR, 0x23); + p_LQBP002TA->calib_param.c31 = (int16_t)h << 4 | l >> 4; + p_LQBP002TA->calib_param.c31 = (p_LQBP002TA->calib_param.c31 & 0x0800) ? (0xF000 | p_LQBP002TA->calib_param.c31) : p_LQBP002TA->calib_param.c31; + //printf("0x22 : %x \n",h); + //printf("0x23 : %x \n",l); + //printf("c31 : %d \n",p_LQBP002TA->calib_param.c31); + h = LQBP002TA_read(HW_ADR, 0x23); + l = LQBP002TA_read(HW_ADR, 0x24); + p_LQBP002TA->calib_param.c40 = (int16_t)(h & 0x0F) << 8 | l; + p_LQBP002TA->calib_param.c40 = (p_LQBP002TA->calib_param.c40 & 0x0800) ? (0xF000 | p_LQBP002TA->calib_param.c40) : p_LQBP002TA->calib_param.c40; + //printf("0x23 : %x \n",h); + //printf("0x24 : %x \n",l); + //printf("c40 : %d \n",p_LQBP002TA->calib_param.c40); + } +} + +/***************************************************************************** + Function: LQBP002TA_start_temperature + Description: start one measurement for temperature + Input: void + Output: + Return: void + Calls: + Called By: +*****************************************************************************/ +void LQBP002TA_start_temperature(void) +{ + LQBP002TA_write(HW_ADR, 0x08, 0x02); +} + +/***************************************************************************** + Function: LQBP002TA_start_pressure + Description: start one measurement for pressure + Input: void + Output: + Return: void + Calls: + Called By: +*****************************************************************************/ + +void LQBP002TA_start_pressure(void) +{ + LQBP002TA_write(HW_ADR, 0x08, 0x01); +} +/***************************************************************************** + Function: LQBP002TA_start_continuous + Description: Select mode for the continuously measurement + Input: uint8_t mode 1: pressure; 2: temperature; 3: pressure and temperature + Output: + Return: void + Calls: + Called By: +*****************************************************************************/ +void LQBP002TA_start_continuous(uint8_t mode) +{ + LQBP002TA_write(HW_ADR, 0x08, mode+4); +} + +void LQBP002TA_stop(void) +{ + LQBP002TA_write(HW_ADR, 0x08, 0); +} + +/***************************************************************************** + Function: LQBP002TA_get_raw_temp + Description:obtain the original temperature value and turn them into 32bits-integer + Input: void + Output: + Return: void + Calls: + Called By: +*****************************************************************************/ +void LQBP002TA_get_raw_temp(void) +{ + uint8_t h,m,l; + + h = LQBP002TA_read(HW_ADR, 0x03); + m = LQBP002TA_read(HW_ADR, 0x04); + l = LQBP002TA_read(HW_ADR, 0x05); + //printf("0x03 : %x \n",h); + //printf("0x04 : %x \n",m); + //printf("0x05 : %x \n",l); + + p_LQBP002TA->i32rawTemperature = (int32_t)h<<16 | (int32_t)m<<8 | (int32_t)l; + p_LQBP002TA->i32rawTemperature= (p_LQBP002TA->i32rawTemperature&0x800000) ? (0xFF000000|p_LQBP002TA->i32rawTemperature) : p_LQBP002TA->i32rawTemperature; + //printf("i32rawTemperature : %d \n",p_LQBP002TA->i32rawTemperature); +} + +/***************************************************************************** + Function: LQBP002TA_get_raw_pressure + Description: obtain the original pressure value and turn them into 32bits-integer + Input: void + Output: + Return: void + Calls: + Called By: +*****************************************************************************/ +void LQBP002TA_get_raw_pressure(void) +{ + uint8_t h,m,l; + + h = LQBP002TA_read(HW_ADR, 0x00); + m = LQBP002TA_read(HW_ADR, 0x01); + l = LQBP002TA_read(HW_ADR, 0x02); + //printf("0x00 : %x \n",h); + //printf("0x01 : %x \n",m); + //printf("0x02 : %x \n",l); + p_LQBP002TA->i32rawPressure = (int32_t)h<<16 | (int32_t)m<<8 | (int32_t)l; + p_LQBP002TA->i32rawPressure= (p_LQBP002TA->i32rawPressure&0x800000) ? (0xFF000000|p_LQBP002TA->i32rawPressure) : p_LQBP002TA->i32rawPressure; + //printf("i32rawPressure : %d \n",p_LQBP002TA->i32rawPressure); +} + +/***************************************************************************** + Function: LQBP002TA_get_temperature + Description: return calibrated temperature value base on original value. + Input: void + Output: + Return: void + Calls: + Called By: +*****************************************************************************/ +float LQBP002TA_get_temperature(void) +{ + float fTCompensate; + volatile float fTsc = (float)p_LQBP002TA->i32rawTemperature / (float)p_LQBP002TA->i32kT; + //printf("i32rawTemperature : %d , i32kT : %f ,fTsc : %f\n",p_LQBP002TA->i32rawTemperature,(float)p_LQBP002TA->i32kT,fTsc); + fTCompensate = (float)p_LQBP002TA->calib_param.c0 * 0.5f + ((float)p_LQBP002TA->calib_param.c1) * (float)fTsc; + //printf("(float)p_LQBP002TA->calib_param.c0 * 0.5f : %f\n",(float)p_LQBP002TA->calib_param.c0 * 0.5f); + //printf("((float)p_LQBP002TA->calib_param.c1) * fTsc : %f\n",((float)p_LQBP002TA->calib_param.c1) * fTsc); + //printf("fTsc : %f\n",fTsc); + printf("fTCompensate : %f\n",fTCompensate); + return fTCompensate; +} + +/***************************************************************************** + Function: LQBP002TA_get_pressure + Description: return calibrated pressure value base on original value. + Input: void + Output: + Return: void + Calls: + Called By: +*****************************************************************************/ + +float LQBP002TA_get_pressure(void) +{ + float fTsc, fPsc; + float qua2, qua3; + float fPCompensate; + + fTsc = p_LQBP002TA->i32rawTemperature / (float)p_LQBP002TA->i32kT; + fPsc = p_LQBP002TA->i32rawPressure / (float)p_LQBP002TA->i32kP; + qua2 = p_LQBP002TA->calib_param.c10 + fPsc * (p_LQBP002TA->calib_param.c20 + fPsc* (p_LQBP002TA->calib_param.c30+ fPsc * p_LQBP002TA->calib_param.c40)); + + if(is_LQBP002TA()) + qua3 = fTsc * fPsc * (p_LQBP002TA->calib_param.c11 + fPsc * (p_LQBP002TA->calib_param.c21+ fPsc * p_LQBP002TA->calib_param.c31)); + else + qua3 = fTsc * fPsc * (p_LQBP002TA->calib_param.c11 + fPsc * p_LQBP002TA->calib_param.c21); + fPCompensate = p_LQBP002TA->calib_param.c00 + fPsc * qua2 + fTsc * p_LQBP002TA->calib_param.c01 + qua3; + printf("fPCompensate : %f\n",fPCompensate); + return fPCompensate; +} + +uint8_t lqbp002ta_check_exist(void) +{ + read_chip_id(); + return is_LQBP002TA(); +} + +void lqbp002ta_start(void) +{ + LQBP002TA_init(); + LQBP002TA_start_continuous(CONTINUOUS_P_AND_T); +} + +void lqbp002ta_check(void) +{ + float fSplTemp = 0.0f; + float fSplPressure = 0.0f; + LQBP002TA_get_raw_temp(); + LQBP002TA_get_raw_pressure(); + fSplTemp = LQBP002TA_get_temperature(); + fSplPressure = LQBP002TA_get_pressure(); +} + + +void lqbp002ta_stop(void) +{ + LQBP002TA_stop(); +} diff --git a/code/sdk/zhihui/drivers/barometer/driver/lqbp002ta.h b/code/sdk/zhihui/drivers/barometer/driver/lqbp002ta.h new file mode 100644 index 000000000..989afd32a --- /dev/null +++ b/code/sdk/zhihui/drivers/barometer/driver/lqbp002ta.h @@ -0,0 +1,62 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-16 20:03:01 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-17 21:05:56 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/LQBP002TA/driver/lqbp002ta.h + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE; + */ +#ifndef LQBP002TA_H +#define LQBP002TA_H + +#include "stdint.h" + +#define HW_ADR 0x77 +#define CONTINUOUS_PRESSURE 1 +#define CONTINUOUS_TEMPERATURE 2 +#define CONTINUOUS_P_AND_T 3 +#define PRESSURE_SENSOR 0 +#define TEMPERATURE_SENSOR 1 + + +struct LQBP002TA_calib_param_t { + int16_t c0; + int16_t c1; + int32_t c00; + int32_t c10; + int16_t c01; + int16_t c11; + int16_t c20; + int16_t c21; + int16_t c30; + int16_t c31; + int16_t c40; +}; + +struct LQBP002TA_t { + struct LQBP002TA_calib_param_t calib_param;/**= LQBP002TA_MAX_ARGC) { + return LQBP002TA_FAILED; + } + } else { + argv[*argc][pos] = para[i]; + pos++; + if (pos >= LQBP002TA_MAX_ARGV_LEN) { + return LQBP002TA_FAILED; + } + } + } + if (pos != 0) { + *argc += 1; + } + + return LQBP002TA_SUCCESS; +} + +static uint32_t lqbp002ta_start_test(uint8_t *para, uint32_t paraLen, char *strCommonCmd) +{ + UNUSED(strCommonCmd); + UNUSED(paraLen); + UNUSED(para); + do_lqbp_service_check_msg(LQBP_SERVICE_CMD_START_CHECK); + return ERRCODE_SUCC; +} +static uint32_t lqbp002ta_stop_test(uint8_t *para, uint32_t paraLen, char *strCommonCmd) +{ + UNUSED(strCommonCmd); + UNUSED(paraLen); + UNUSED(para); + do_lqbp_service_check_msg(LQBP_SERVICE_CMD_STOP_CHECK); + return ERRCODE_SUCC; +} +static const at_cmd_table_t g_lqbp002ta_cmd[] = { + { (char *)"AT^LQBPTESTSTART", lqbp002ta_start_test, (char *)"1" }, + { (char *)"AT^LQBPTESTSTOP", lqbp002ta_stop_test, (char *)"1" }, +}; + +at_cmd_table_t *get_lqbp002ta_cmd_table(uint32_t *cmd_count) +{ + *cmd_count = ARRAY_COUNT(g_lqbp002ta_cmd); + return (at_cmd_table_t *)g_lqbp002ta_cmd; +} diff --git a/code/sdk/zhihui/drivers/barometer/service/lqbp_at_cmd.h b/code/sdk/zhihui/drivers/barometer/service/lqbp_at_cmd.h new file mode 100644 index 000000000..2c06b3b51 --- /dev/null +++ b/code/sdk/zhihui/drivers/barometer/service/lqbp_at_cmd.h @@ -0,0 +1,40 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-16 20:49:19 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-17 14:25:16 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/LQBP002TA/service/lqbp002ta_at_cmd.h + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +/** + * Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved. \n + * + * Description: Provides at cmd register func \n + * Author: @CompanyNameTag \n + * History: \n + * 2022-07-20, Create file. \n + */ +#ifndef LQBP002TA_AT_PROCESS_H +#define LQBP002TA_AT_PROCESS_H + +#include +#include +#include "errcode.h" +#include "soc_osal.h" +#include "at_cmd_api.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +at_cmd_table_t *get_lqbp002ta_cmd_table(uint32_t *cmd_count); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif diff --git a/code/sdk/zhihui/drivers/barometer/service/lqbp_service.c b/code/sdk/zhihui/drivers/barometer/service/lqbp_service.c new file mode 100644 index 000000000..82cea70b3 --- /dev/null +++ b/code/sdk/zhihui/drivers/barometer/service/lqbp_service.c @@ -0,0 +1,173 @@ + +#include "debug_print.h" +#include "soc_osal.h" +#include "tcxo.h" +#include "irmalloc.h" +#include "errcode.h" +#include "common_def.h" +#include "tcxo.h" +#include "input_app.h" +#include "cmsis_os2.h" +#include "custom_utils.h" +#include "lqbp002ta.h" +#include "lqbp_service.h" +// #include "custom_runtime.h" +/*** + * 其他cmd在Header里定义,对外开放 +*/ +#define LQBP_SERVICE_CMD_TIMER_CHECK 0x11 + +#define LQBP_CHECK_MS 30 + +static bool g_lqbp_service_initialled = false; + +osTimerId_t g_lqbp_service_timer = NULL; + +static bool g_lqbp_sensor_exist = false; + +static bool g_bat_timer_is_running = false; + +static unsigned long g_app_queue= 0xFFFFFFFF; + +static bool lqbp_service_running_stat = false; + +static void lqbp_service_check_timer_callback(void* data); + +static void lqbp_service_timer_start_check(uint32_t rtc_ms) +{ + + if (g_bat_timer_is_running ) + osTimerStop(g_lqbp_service_timer); + + if (lqbp_service_running_stat == true) + osTimerStart(g_lqbp_service_timer, rtc_ms); + + g_bat_timer_is_running = true; + +} +void do_lqbp_service_check_msg(int cmd); + +static void lqbp_service_check_timer_callback(void * data) +{ + unused(data); + + g_bat_timer_is_running = false; + + if(lqbp_service_running_stat == true){ + // mmc5603_check(); //此处只能直接加到timer callback中,采用queue的话,新能跟不上,系统资源会挂掉。 + do_lqbp_service_check_msg(LQBP_SERVICE_CMD_TIMER_CHECK); + // lqbp_service_timer_start_check(lqbp_CHECK_MS); + } + + + +} +void uapi_lqbp_service_deinit(void) +{ + /* Stop the time and clear the pin map */ + osTimerStop(g_lqbp_service_timer); + osTimerDelete(g_lqbp_service_timer); + + g_bat_timer_is_running = false; + g_lqbp_service_timer = NULL; +} + + + +void do_lqbp_service_check_msg(int cmd) +{ + int ret; + if (g_lqbp_sensor_exist == false) + return ; + common_notify_mail_t mail = {0}; + mail.comm.app = cmd; + ret = osal_msg_queue_write_copy(g_app_queue, (void *)&mail, + sizeof(common_notify_mail_t), 0); + if (ret != OSAL_SUCCESS) { + PRINT("Error lqbp Check MSG 0x%02X send failed! err=0x%x! \r\n",cmd, ret); + } + +} + +static void lqbp_service_check_task(void const *arg) +{ + unused(arg); + int32_t ret; + common_notify_mail_t mail; + uint32_t msgsize = (uint32_t)sizeof(common_notify_mail_t); + while (1) { + ret = osal_msg_queue_read_copy(g_app_queue, (void *)&mail, &msgsize, + OSAL_MSGQ_WAIT_FOREVER); + if (ret != OSAL_SUCCESS) { + continue; + } + + if (mail.comm.app == LQBP_SERVICE_CMD_TIMER_CHECK) { + lqbp002ta_check(); + }else if (mail.comm.app == LQBP_SERVICE_CMD_START_CHECK){ + lqbp002ta_start(); + LOS_Mdelay(30); + lqbp_service_running_stat = true; + lqbp_service_timer_start_check(LQBP_CHECK_MS); + }else if (mail.comm.app == LQBP_SERVICE_CMD_STOP_CHECK){ + lqbp_service_running_stat = false; + osTimerStop(g_lqbp_service_timer); + lqbp002ta_stop(); + LOS_Mdelay(20); + } + + + } +} +void create_lqbp_queue(void) +{ + + if (osal_msg_queue_create("lqbp_service_queue", APP_BOARD_MSG_COUNT, &g_app_queue, + 0, sizeof(common_notify_mail_t)) != ERRCODE_SUCC) { + PRINT("[lqbp][service] app create mail fail\n"); + } +} + +#define LQBP_BOARD_TASK_SIZE 0x1000 +errcode_t uapi_lqbp_service_init(void) +{ + PRINT("uapi_lqbp_service_init !\r\n"); + + if(!lqbp002ta_check_exist()) + { + //TODO 设置气压计检测是否存在标识 + PRINT("lqbp002ta not found!\r\n"); + return ERRCODE_FAIL; + } + g_lqbp_sensor_exist = true; + + app_task_definition_t taskcfg={0}; + taskcfg.create_queue_handle = create_lqbp_queue; + taskcfg.attr.name="lqbp_service"; + taskcfg.attr.priority = osPriorityBelowNormal4; + taskcfg.attr.stack_size = LQBP_BOARD_TASK_SIZE; + taskcfg.func = lqbp_service_check_task; + taskcfg.use_mem = USE_EXTEND_MEM; + + errcode_t ret=uapi_create_osThread_custom(&taskcfg); + if(ret != ERRCODE_SUCC) + { + PRINT("create lqbp_service failed %d!\r\n",ret); + return ERRCODE_FAIL; + } + + g_lqbp_service_timer = osTimerNew((osTimerFunc_t)lqbp_service_check_timer_callback, osTimerPeriodic, NULL, NULL); + + + return ERRCODE_SUCC; +} + +// void lqbp_service_service_resume(void) +// { +// MMC_LOG("lqbp_service_service resume!\r\n"); +// lqbp_service_check_timer_callback(0); +// } +// void lqbp_service_service_suspend(void) +// { +// MMC_LOG("lqbp_service_service suspend!\r\n"); +// } \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/barometer/service/lqbp_service.h b/code/sdk/zhihui/drivers/barometer/service/lqbp_service.h new file mode 100644 index 000000000..85ea7f697 --- /dev/null +++ b/code/sdk/zhihui/drivers/barometer/service/lqbp_service.h @@ -0,0 +1,45 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-16 20:48:55 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-17 14:27:18 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/LQBP002TA/service/lqbp002ta_service.h + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +/** + * Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved. \n + * + * Description: Test adc header \n + * Author: @CompanyNameTag \n + * History: \n + * 2022-08-16, Create file. \n + */ + +#ifndef LQBP_SERVICE_H__ +#define LQBP_SERVICE_H__ + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#include "stdint.h" +#include "errcode.h" + + +#define LQBP_SERVICE_CMD_START_CHECK 0x12 +#define LQBP_SERVICE_CMD_STOP_CHECK 0x13 + +errcode_t uapi_lqbp_service_init(void); +void uapi_lqbp_service_deinit(void); +void do_lqbp_service_check_msg(int cmd); + + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/charge/CMakeLists.txt b/code/sdk/zhihui/drivers/charge/CMakeLists.txt new file mode 100644 index 000000000..b100dae71 --- /dev/null +++ b/code/sdk/zhihui/drivers/charge/CMakeLists.txt @@ -0,0 +1,46 @@ +#=============================================================================== +# @brief cmake file +# Copyright (c) @CompanyNameMagicTag 2022-2023. All rights reserved. +#=============================================================================== +set(COMPONENT_NAME "charge_ic") + +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/hp4570/hp4570.c + ${CMAKE_CURRENT_SOURCE_DIR}/service/charge_at_cmd.c + ${CMAKE_CURRENT_SOURCE_DIR}/service/charge_service.c +) + +set(PUBLIC_HEADER + ${CMAKE_CURRENT_SOURCE_DIR}/hp4570/ + ${CMAKE_CURRENT_SOURCE_DIR}/service/ + +) + +set(PRIVATE_HEADER +) + +set(PRIVATE_DEFINES + TARGET_NAME="${TARGET_COMMAND}" + BUILD_TIME="${BUILD_TIME}" +) + +set(PUBLIC_DEFINES +SUPPORT_AT_CHARGE +) + +# use this when you want to add ccflags like -include xxx +set(COMPONENT_PUBLIC_CCFLAGS +) + +set(COMPONENT_CCFLAGS +) + +set(WHOLE_LINK + true +) + +set(MAIN_COMPONENT + false +) + +build_component() diff --git a/code/sdk/zhihui/drivers/charge/hp4570/hp4570.c b/code/sdk/zhihui/drivers/charge/hp4570/hp4570.c new file mode 100644 index 000000000..be4a803c8 --- /dev/null +++ b/code/sdk/zhihui/drivers/charge/hp4570/hp4570.c @@ -0,0 +1,766 @@ +#include "hp4570.h" +#include "custom_utils.h" +#include "platform_core.h" + + +#define INPUT_PARA_FAIL_RETURN(value, min, max) \ + do { \ + if (value < min || value > max) \ + return INVALID_INPUT; \ + } while (0) + +#define INVALID_INPUT_RETURN(value, index, array, array_size) \ + do { \ + for(index = 0 ; index < array_size; index++){ \ + if(value == array[index]) \ + break; \ + } \ + if(index == array_size) \ + return INVALID_INPUT; \ + } while (0) + +#define SET_BIT(value, mask, code, shift) \ + do { \ + value &= ~mask; \ + value |= (code << shift); \ + } while (0) + +#define SET_ENABLE(en, value, mask, en_code, shift) \ + do { \ + if (en) \ + SET_BIT(value, mask, en_code, shift); \ + else \ + SET_BIT(value, mask, !en_code, shift); \ + } while (0) + + +struct CHIP_STATE{ + bool vin_fault; + bool tshut; + bool vbat_ovp; + bool safety_mode; + bool ntc_hot; + bool ntc_cold; +}hp45xxx_chip_state; + +uint8_t hp45xxx_i2c_addr = 0; + +#define SUCCESS 0 +#define FAIL 1 +#define I2C_ERROR 1 +#define INVALID_INPUT 1 + +#ifdef BOARD_L22 +#define HP4570_I2C_BUS I2C_BUS_0 +#elif defined BOARD_U8 +#define HP4570_I2C_BUS I2C_BUS_3 +#endif + +bool hp45xxx_write_reg(uint8_t reg, uint8_t value) +{ + return custom_i2c_write_reg(hp45xxx_i2c_addr, reg, 1, &value, 1, HP4570_I2C_BUS); + +} + +bool hp45xxx_read_reg(uint8_t reg, uint8_t *value) +{ + return custom_i2c_read_regs(hp45xxx_i2c_addr, reg, 1, value, 1, HP4570_I2C_BUS); +} + +// Initialize HP4570 chip +bool hp45xxx_init(uint8_t chip_ver){ + switch (chip_ver){ + case HP4570K: + hp45xxx_i2c_addr = HP4570K_I2C_ADDR; + break; + default: + hp45xxx_i2c_addr = HP45XXX_I2C_ADDR; + break; + } + if(hp45xxx_identify(chip_ver) != SUCCESS) + { + printf("hp45xxx_identify(chip_ver) failed !!!! \n"); + return FAIL; + } + printf("hp45xxx_identify(chip_ver) succed !!!! \n"); + // hp45xxx_reg_rst(); + // Add initialization code here, such as configuring registers + hp45xxx_set_ceb_en(true); + if( hp45xxx_set_chrg_vbat_mV(4012.5) + // && + // hp45xxx_set_chrg_ipre_mA(34) && + // hp45xxx_set_chrg_ichg_mA(200) && + // hp45xxx_set_chrg_iterm_mA(34) && + // hp45xxx_set_iindpm_mA(1000) && + // hp45xxx_set_vin_min_mV(3800) + ) + return SUCCESS; + return FAIL; +} + +bool hp45xxx_identify(uint8_t chip_ver){ + uint8_t vid_value[2]; + uint8_t pid_value[2]; + uint8_t chip_ver_value; + hp45xxx_read_reg(HP45XXX_REG_VID_LSB, &vid_value[0]); + hp45xxx_read_reg(HP45XXX_REG_VID_MSB, &vid_value[1]); + hp45xxx_read_reg(HP45XXX_REG_PID_LSB, &pid_value[0]); + hp45xxx_read_reg(HP45XXX_REG_PID_MSB, &pid_value[1]); + hp45xxx_read_reg(HP45XXX_REG_CHIP_VER, &chip_ver_value); + // printf("vid_value[0] : 0x%x , vid_value[1] : 0x%x , pid_value[0] : 0x%x,pid_value[1] : 0x%x chip_ver_value : 0x%x\n",vid_value[0],vid_value[1],pid_value[0],pid_value[1],chip_ver_value); + if((vid_value[0]!=HP45XXX_REG_VID_LSB_DEF)|| + (vid_value[1]!=HP45XXX_REG_VID_MSB_DEF)|| + (pid_value[0]!=HP45XXX_REG_PID_LSB_DEF)|| + (pid_value[1]!=HP45XXX_REG_PID_MSB_DEF)|| + (chip_ver_value != chip_ver)) + return FAIL; + return SUCCESS; +} + +void hp45xxx_entry_shipmode(void){ + hp45xxx_set_batfet_dis(true); +} + +void hp45xxx_reg_rst(void){ + // Write 1 will auto be 0 + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL1, HP45XXX_REG_RST_RESET << HP45XXX_REG_RST_BIT_SHIFT); +} + +void hp45xxx_wdt_rst(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL1, &value); + // Write 1 will auto be 0 and timer will reset,after watchdog fault,Only write 1 will restart timer + SET_BIT(value, HP45XXX_WDT_RST_MASK, HP45XXX_WDT_RST_RESET, HP45XXX_WDT_RST_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL1,value); +} + +void hp45xxx_vsys_rst(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL5, &value); + SET_BIT(value, HP45XXX_VSYS_RST_MASK, HP45XXX_VSYS_RST_RESET, HP45XXX_VSYS_RST_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL5,value); +} + +bool hp45xxx_set_vindpm_mV(uint16_t voltage){ + uint8_t value; + uint8_t vin_min_code; + INPUT_PARA_FAIL_RETURN(voltage,HP45XXX_VIN_MIN_MIN_mV,HP45XXX_VIN_MIN_MAX_mV); + hp45xxx_read_reg(HP45XXX_REG_DPM_SET, &value); + vin_min_code = (uint8_t)((voltage - HP45XXX_VIN_MIN_OFFSET_mV)/HP45XXX_VIN_MIN_STEP_mV); + SET_BIT(value, HP45XXX_VIN_MIN_MASK, vin_min_code, HP45XXX_VIN_MIN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_DPM_SET, value); + return SUCCESS; +} + +bool hp45xxx_set_iindpm_mA(uint16_t current){ + uint8_t value; + uint8_t iindpm_code; + INPUT_PARA_FAIL_RETURN(current,HP45XXX_IINDPM_MIN_mA,HP45XXX_IINDPM_MAX_mA); + hp45xxx_read_reg(HP45XXX_REG_DPM_SET, &value); + iindpm_code = (uint8_t)((current - HP45XXX_IINDPM_OFFSET_mA)/HP45XXX_IINDPM_STEP_mA); + SET_BIT(value, HP45XXX_IINDPM_MASK, iindpm_code, HP45XXX_IINDPM_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_DPM_SET, value); + return SUCCESS; +} + +bool hp45xxx_set_trst_dgl_mS(uint32_t time){ + uint8_t value; + uint8_t trst_dgl_code; + INVALID_INPUT_RETURN(time, trst_dgl_code, hp45xxx_trst_dgl_time, HP45XXX_NUM_TRST_DGL_VAL); + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL0, &value); + SET_BIT(value, HP45XXX_TRST_DGL_MASK, trst_dgl_code, HP45XXX_TRST_DGL_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL0, value); + return SUCCESS; +} + +bool hp45xxx_set_trst_dur_mS(uint16_t time){ + uint8_t value; + uint8_t trst_dur_code; + INVALID_INPUT_RETURN(time, trst_dur_code, hp45xxx_trst_dur_time, HP45XXX_NUM_TRST_DUR_VAL); + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL0, &value); + SET_BIT(value, HP45XXX_TRST_DUR_MASK, trst_dur_code, HP45XXX_TRST_DUR_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL0,value); + return SUCCESS; +} + +void hp45xxx_set_hiz_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL0, &value); + SET_ENABLE(en, value, HP45XXX_EN_HIZ_MASK, HP45XXX_EN_HIZ_ENABLE, HP45XXX_EN_HIZ_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL0,value); +} + +void hp45xxx_set_ceb_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL0, &value); + SET_ENABLE(en, value, HP45XXX_CEB_MASK, HP45XXX_CEB_ENABLE, HP45XXX_CEB_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL0, value); +} + +bool hp45xxx_set_vbat_uvlo_mV(uint16_t voltage){ + uint8_t value; + uint8_t vbat_uvlo_code; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL0, &value); + INPUT_PARA_FAIL_RETURN(voltage,HP45XXX_VBAT_UVLO_MIN_mV,HP45XXX_VBAT_UVLO_MAX_mV); + vbat_uvlo_code = (uint8_t)((voltage - HP45XXX_VBAT_UVLO_OFFSET_mV)/HP45XXX_VBAT_UVLO_STEP_mV); + SET_BIT(value, HP45XXX_VBAT_UVLO_MASK, vbat_uvlo_code, HP45XXX_VBAT_UVLO_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL0, value); + return SUCCESS; +} + +bool hp45xxx_set_chrg_ichg_mA(uint16_t current){ + uint8_t value; + uint8_t ichg_code; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL1, &value); + INPUT_PARA_FAIL_RETURN(current,HP45XXX_ICHG_MIN_mA,HP45XXX_ICHG_MAX_mA); + ichg_code = (uint8_t)((current - HP45XXX_ICHG_OFFSET_mA)/HP45XXX_ICHG_STEP_mA); + SET_BIT(value, HP45XXX_ICHG_MASK, ichg_code, HP45XXX_ICHG_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL1, value); + return SUCCESS; +} + +bool hp4570i_set_chrg_ichg_mA(uint16_t current){ + uint8_t value; + uint8_t ichg_code; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL1, &value); + INPUT_PARA_FAIL_RETURN(current,HP4570I_ICHG_MIN_mA,HP4570I_ICHG_MAX_mA); + ichg_code = (uint8_t)((current - HP4570I_ICHG_OFFSET_mA)/HP4570I_ICHG_STEP_mA); + SET_BIT(value, HP45XXX_ICHG_MASK, ichg_code, HP45XXX_ICHG_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL1, value); + return SUCCESS; +} + +bool hp45xxx_set_ibatfet_ocp_mA(uint16_t current){ + uint8_t value; + uint8_t ibatfet_ocp_code; + hp45xxx_read_reg(HP45XXX_REG_IDSG_ITERM_SET, &value); + INPUT_PARA_FAIL_RETURN(current,HP45XXX_IBATFET_OCP_MIN_mA,HP45XXX_IBATFET_OCP_MAX_mA); + ibatfet_ocp_code = (uint8_t)((current - HP45XXX_IBATFET_OCP_OFFSET_mA)/HP45XXX_IBATFET_OCP_STEP_mA); + SET_BIT(value, HP45XXX_IBATFET_OCP_MASK, ibatfet_ocp_code, HP45XXX_IBATFET_OCP_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_IDSG_ITERM_SET,value); + return SUCCESS; +} + +bool hp45xxx_set_chrg_iterm_mA(uint8_t current){ + uint8_t value; + uint8_t iterm_code; + hp45xxx_read_reg(HP45XXX_REG_IDSG_ITERM_SET, &value); + INPUT_PARA_FAIL_RETURN(current,HP45XXX_ITERM_MIN_mA,HP45XXX_ITERM_MAX_mA); + iterm_code = (uint8_t)((current - HP45XXX_ITERM_OFFSET_mA)/HP45XXX_ITERM_STEP_mA); + SET_BIT(value, HP45XXX_ITERM_MASK, iterm_code, HP45XXX_ITERM_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_IDSG_ITERM_SET,value); + return SUCCESS; +} + +bool hp45xxx_set_chrg_vbat_mV(float voltage){ + uint8_t value; + uint8_t vbatreg_code; + hp45xxx_read_reg(HP45XXX_REG_BAT_VOL_SET, &value); + INPUT_PARA_FAIL_RETURN(voltage,HP45XXX_VBATREG_MIN_mV,HP45XXX_VBATREG_MAX_mV); + vbatreg_code = (uint8_t)((voltage - HP45XXX_VBATREG_OFFSET_mV)/HP45XXX_VBATREG_STEP_mV); + SET_BIT(value, HP45XXX_VBATREG_MASK, vbatreg_code, HP45XXX_VBATREG_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_BAT_VOL_SET,value); + return SUCCESS; +} + +bool hp45xxx_set_chrg_vpre_mV(uint8_t voltage){ + uint8_t value0; + uint8_t value1; + uint8_t vbat_pre_code; + INVALID_INPUT_RETURN(voltage, vbat_pre_code, hp45xxx_vbat_pre_voltage, HP45XXX_NUM_VBAT_PRE_VAL); + hp45xxx_read_reg(HP45XXX_REG_BAT_VOL_SET, value0); + hp45xxx_read_reg(HP45XXX_REG_VIPRE, value1); + SET_BIT(value0, HP45XXX_VBAT_PRE_0_MASK, (vbat_pre_code & 0x1), HP45XXX_VBAT_PRE_0_BIT_SHIFT); + SET_BIT(value1, HP45XXX_VBAT_PRE_1_MASK, (vbat_pre_code & 0x2)>>1, HP45XXX_VBAT_PRE_1_BIT_SHIFT); + (hp45xxx_write_reg(HP45XXX_REG_BAT_VOL_SET,value0) & hp45xxx_write_reg(HP45XXX_REG_VIPRE,value1)); + return SUCCESS; +} + +bool hp45xxx_set_chrg_vre_mV(uint8_t voltage){ + uint8_t value; + uint8_t vre_chg_code; + INVALID_INPUT_RETURN(voltage, vre_chg_code, hp45xxx_vre_chg_voltage, HP45XXX_NUM_VRE_CHG_VAL); + hp45xxx_read_reg(HP45XXX_REG_BAT_VOL_SET, &value); + SET_BIT(value, HP45XXX_VRE_CHG_MASK, vre_chg_code, HP45XXX_VRE_CHG_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_BAT_VOL_SET,value); + return SUCCESS; +} + +void hp45xxx_set_wdt_dsg_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL2, &value); + SET_ENABLE(en, value, HP45XXX_EN_WDT_DSG_MASK, HP45XXX_EN_WDT_DSG_ENABLE, HP45XXX_EN_WDT_DSG_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL2, value); +} + +void hp45xxx_set_wdt_timer_en(bool en){ + uint8_t value; + uint32_t wdt_timer_code; + if(!en) + hp45xxx_set_wdt_timer_mS(HP45XXX_WDT_TIMER_DIS); + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL2, &value); + value &= HP45XXX_WDT_TIMER_MASK; + if(value >> HP45XXX_WDT_TIMER_BIT_SHIFT == HP45XXX_WDT_TIMER_DIS) + // do nothing + return; + wdt_timer_code = (HP45XXX_REG_SYS_CTRL2_DEF & HP45XXX_WDT_TIMER_MASK) >> HP45XXX_WDT_TIMER_BIT_SHIFT; + SET_BIT(value, HP45XXX_WDT_TIMER_MASK, wdt_timer_code, HP45XXX_WDT_TIMER_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL2,value); +} + +bool hp45xxx_set_wdt_timer_mS(uint32_t time){ + uint8_t value; + uint8_t wdt_timer_code; + INVALID_INPUT_RETURN(time, wdt_timer_code, hp45xxx_watchdog_time, HP45XXX_NUM_WDT_TIMER_VAL); + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL2, &value); + SET_BIT(value, HP45XXX_WDT_TIMER_MASK, wdt_timer_code, HP45XXX_WDT_TIMER_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL2,value); + return SUCCESS; +} + +void hp45xxx_set_chrg_term_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL2, &value); + SET_ENABLE(en, value, HP45XXX_EN_TERM_MASK, HP45XXX_EN_TERM_ENABLE, HP45XXX_EN_TERM_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL2, value); +} + +void hp45xxx_set_chrg_timer_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL2, &value); + SET_ENABLE(en, value, HP45XXX_EN_TIMER_MASK, HP45XXX_EN_TIMER_ENABLE, HP45XXX_EN_TIMER_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL2, value); +} + +bool hp45xxx_set_chrg_timer_hour(uint8_t time){ + uint8_t value; + uint8_t chg_timer_code; + INVALID_INPUT_RETURN(time, chg_timer_code, hp45xxx_fast_chg_time, HP45XXX_NUM_CHG_TIMER_VAL); + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL2, &value); + SET_BIT(value, HP45XXX_CHG_TIMER_MASK, chg_timer_code, HP45XXX_CHG_TIMER_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL2,value); + return SUCCESS; +} + +// when TERM_TMR is enabled, the IC will not suspend the charge current after the charge termination +void hp45xxx_set_chrg_term_tmr(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL2, &value); + SET_ENABLE(en, value, HP45XXX_TERM_TMR_MASK, HP45XXX_TERM_TMR_ENABLE, HP45XXX_TERM_TMR_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL2, value); +} + +void hp45xxx_set_ntc_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL3, &value); + SET_ENABLE(en, value, HP45XXX_NTC_EN_MASK, HP45XXX_NTC_EN_ENABLE, HP45XXX_NTC_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL3, value); +} + +void hp45xxx_set_chrg_tmr2x_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL3, &value); + SET_ENABLE(en, value, HP45XXX_TMR2X_EN_MASK, HP45XXX_TMR2X_EN_ENABLE, HP45XXX_TMR2X_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL3, value); +} + +// when set 1, enter ship mode w/o vbus, the bit will be set to 0 after exiting ship mode +bool hp45xxx_set_batfet_dis(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL3, &value); + SET_ENABLE(en, value, HP45XXX_BATFET_DIS_MASK, HP45XXX_BATFET_DIS_ENABLE, HP45XXX_BATFET_DIS_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL3, value); + return SUCCESS; +} + +void hp45xxx_set_int_pg_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL3, &value); + SET_ENABLE(en, value, HP45XXX_PG_INT_CONTROL_MASK, HP45XXX_PG_INT_CONTROL_ENABLE, HP45XXX_PG_INT_CONTROL_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL3, value); +} + +void hp45xxx_set_int_chrg_done_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL3, &value); + SET_ENABLE(en, value, HP45XXX_CHG_DONE_INT_EN_MASK, HP45XXX_CHG_DONE_INT_EN_ENABLE, HP45XXX_CHG_DONE_INT_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL3, value); +} + +// Include: not charge to CC, Pre into CC ,CC into CV, not charge into CC(Auto recharge) +void hp45xxx_set_int_chrg_stat_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL3, &value); + SET_ENABLE(en, value, HP45XXX_CHG_STAT_INT_EN_MASK, HP45XXX_CHG_STAT_INT_EN_ENABLE, HP45XXX_CHG_STAT_INT_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL3, value); +} + +void hp45xxx_set_int_ntc_transition_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL3, &value); + SET_ENABLE(en, value, HP45XXX_NTC_TRANSITION_EN_MASK, HP45XXX_NTC_TRANSITION_EN_ENABLE, HP45XXX_NTC_TRANSITION_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL3, value); +} + +void hp45xxx_set_int_batovp_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL3, &value); + SET_ENABLE(en, value, HP45XXX_BATOVP_INT_EN_MASK, HP45XXX_BATOVP_INT_EN_ENABLE, HP45XXX_BATOVP_INT_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL3, value); +} + +void hp45xxx_set_pcb_otp_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL4, &value); + SET_ENABLE(en, value, HP45XXX_EN_PCB_OTP_MASK, HP45XXX_EN_PCB_OTP_ENABLE, HP45XXX_EN_PCB_OTP_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL4, value); +} + +void hp45xxx_set_vindpm_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL4, &value); + SET_ENABLE(en, value, HP45XXX_VINDPM_EN_MASK, HP45XXX_VINDPM_EN_ENABLE, HP45XXX_VINDPM_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL4, value); +} + +bool hp45xxx_set_thermal_reg_degree(uint8_t degree){ + uint8_t value; + uint8_t thermal_reg_code; + INVALID_INPUT_RETURN(degree, thermal_reg_code, hp45xxx_thermal_reg_degree, HP45XXX_NUM_THERMAL_REG_VAL); + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL4, &value); + SET_BIT(value, HP45XXX_THERMAL_REG_MASK, thermal_reg_code, HP45XXX_THERMAL_REG_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL4, value); + return SUCCESS; +} + +bool hp45xxx_set_vsys_reg_mV(float voltage){ + uint8_t value; + uint8_t vsys_reg_code; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL4, &value); + INPUT_PARA_FAIL_RETURN(voltage,HP45XXX_VSYS_REG_MIN_mV,HP45XXX_VSYS_REG_MAX_mV); + vsys_reg_code = (uint8_t)((voltage - HP45XXX_VSYS_REG_OFFSET_mV)/HP45XXX_VSYS_REG_STEP_mV); + SET_BIT(value, HP45XXX_VSYS_REG_MASK, vsys_reg_code, HP45XXX_VSYS_REG_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL4,value); + return SUCCESS; +} + +bool hp45xxx_set_ship_dgl_mS(uint16_t time){ + uint8_t value; + uint8_t ship_dgl_code; + INVALID_INPUT_RETURN(time, ship_dgl_code, hp45xxx_ship_dgl_time, HP45XXX_NUM_SHIP_DGL_VAL); + SET_BIT(value, HP45XXX_SHIP_DGL_MASK, ship_dgl_code, HP45XXX_SHIP_DGL_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_CHIP_STAT1, value); + return SUCCESS; +} + +bool hp45xxx_set_vin_exit_ship_tset_mS(uint16_t time){ + uint8_t value; + uint8_t in_exit_ship_tset_code; + INVALID_INPUT_RETURN(time, in_exit_ship_tset_code, hp45xxx_ship_dgl_time, HP45XXX_NUM_IN_EXIT_SHIP_TSET_VAL); + hp45xxx_read_reg(HP45XXX_REG_VIPRE, &value); + SET_BIT(value, HP45XXX_IN_EXIT_SHIP_TSET_MASK, in_exit_ship_tset_code, HP45XXX_IN_EXIT_SHIP_TSET_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_VIPRE, value); + return SUCCESS; +} + +bool hp45xxx_set_chrg_ipre_mA(uint16_t current){ + uint8_t value; + uint8_t ipre_code; + hp45xxx_read_reg(HP45XXX_REG_VIPRE, &value); + INPUT_PARA_FAIL_RETURN(current,HP45XXX_IPRE_MIN_mA,HP45XXX_IPRE_MAX_mA); + ipre_code = (uint8_t)((current - HP45XXX_IPRE_OFFSET_mA)/HP45XXX_IPRE_STEP_mA); + SET_BIT(value, HP45XXX_IPRE_MASK, ipre_code, HP45XXX_IPRE_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_VIPRE,value); + return SUCCESS; +} + +void hp45xxx_set_rst_vin_entry_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_EXT_CTRL, &value); + SET_ENABLE(en, value, HP45XXX_VINDPM_EN_MASK, HP45XXX_VIN_ENTRY_RST_EN_ENABLE, HP45XXX_VIN_ENTRY_RST_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_EXT_CTRL, value); +} + +bool hp45xxx_set_chrg_idead_mA(uint16_t current){ + uint8_t value; + uint8_t ideadbat_code; + hp45xxx_read_reg(HP45XXX_REG_EXT_CTRL, &value); + INPUT_PARA_FAIL_RETURN(current,HP45XXX_IDEADBAT_MIN_mA,HP45XXX_IDEADBAT_MAX_mA); + ideadbat_code = (uint8_t)((current - HP45XXX_IDEADBAT_OFFSET_mA)/HP45XXX_IDEADBAT_STEP_mA); + SET_BIT(value, HP45XXX_IDEADBAT_MASK, ideadbat_code, HP45XXX_IDEADBAT_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_EXT_CTRL,value); + return SUCCESS; +} + +bool hp45xxx_set_en_iindpm_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_EXT_CTRL, &value); + SET_ENABLE(en, value, HP45XXX_EN_IINDPM_MASK, HP45XXX_EN_IINDPM_ENABLE, HP45XXX_EN_IINDPM_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL5, value); + return SUCCESS; +} + +bool hp45xxx_set_key_wk_time_mS(uint16_t time){ + uint8_t value; + uint8_t key_wk_set_code; + INVALID_INPUT_RETURN(time, key_wk_set_code, hp45xxx_key_wk_set_time, HP45XXX_NUM_KEY_WK_SET_VAL); + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL5, &value); + SET_BIT(value, HP45XXX_KEY_WK_SET_MASK, key_wk_set_code, HP45XXX_KEY_WK_SET_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL5, value); + return SUCCESS; +} + +bool hp45xxx_set_t_hiccup_mS(uint16_t time){ + uint8_t value; + uint8_t t_hiccup_code; + INVALID_INPUT_RETURN(time, t_hiccup_code, hp45xxx_key_wk_set_time, HP45XXX_NUM_T_HICCUP_VAL); + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL5, &value); + SET_BIT(value, HP45XXX_T_HICCUP_MASK, t_hiccup_code, HP45XXX_T_HICCUP_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL5, value); + return SUCCESS; +} + +void hp45xxx_set_wdt_vsys_rst_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL6, &value); + SET_ENABLE(en, value, HP45XXX_WDT_VSYS_RST_MASK, HP45XXX_WDT_VSYS_RST_ENABLE, HP45XXX_WDT_VSYS_RST_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL6, value); +} + +void hp45xxx_set_ship_dgl_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL6, &value); + SET_ENABLE(en, value, HP45XXX_EN_SHIP_DGL_MASK, HP45XXX_EN_SHIP_DGL_ENABLE, HP45XXX_EN_SHIP_DGL_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL6, value); +} + +void hp45xxx_set_wdt_chg_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL7, &value); + SET_ENABLE(en, value, HP45XXX_EN_WDT_CHG_MASK, HP45XXX_EN_WDT_CHG_ENABLE, HP45XXX_EN_WDT_CHG_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL7, value); +} + +void hp45xxx_set_rst_pwrkey_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL7, &value); + SET_ENABLE(en, value, HP45XXX_EN_PWRKEY_RST_MASK, HP45XXX_EN_PWRKEY_RST_ENABLE, HP45XXX_EN_PWRKEY_RST_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL7, value); +} + +void hp45xxx_set_rst_vsys_wvbus_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL7, &value); + SET_ENABLE(en, value, HP45XXX_VSYS_RST_WVBUS_MASK, HP45XXX_VSYS_RST_WVBUS_ENABLE, HP45XXX_VSYS_RST_WVBUS_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL7, value); +} + +void hp45xxx_set_pwrkey_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL7, &value); + SET_ENABLE(en, value, HP45XXX_PWRKEY_EN_MASK, HP45XXX_PWRKEY_EN_ENABLE, HP45XXX_PWRKEY_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL7, value); +} + +void hp45xxx_set_vbus_wk_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL7, &value); + SET_ENABLE(en, value, HP45XXX_VBUS_WK_EN_MASK, HP45XXX_VBUS_WK_EN_ENABLE, HP45XXX_VBUS_WK_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL7, value); +} + +void hp45xxx_set_pwrkey_wk_en(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_CTRL7, &value); + if(en){ + SET_BIT(value, HP45XXX_PWRKEY_WK_EN_MASK, HP45XXX_PWRKEY_WK_EN_ENABLE, HP45XXX_PWRKEY_WK_EN_BIT_SHIFT); + // Note: bit 2 must be 1 + value |= (HP45XXX_PWRKEY_EN_ENABLE << HP45XXX_PWRKEY_EN_BIT_SHIFT); + }else + SET_BIT(value, HP45XXX_PWRKEY_WK_EN_MASK, !HP45XXX_PWRKEY_WK_EN_ENABLE, HP45XXX_PWRKEY_WK_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_SYS_CTRL7, value); +} + +bool hp45xxx_set_watchdog_int_enable(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_INT0_ENABLE, &value); + SET_ENABLE(en, value, HP45XXX_WATCHDOG_INT_EN_MASK, HP45XXX_WATCHDOG_INT_EN_ENABLE, HP45XXX_WATCHDOG_INT_EN_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_INT0_ENABLE, value); + return SUCCESS; +} + +bool hp45xxx_set_chg_safety_expired_enable(bool en){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_INT0_ENABLE, &value); + SET_ENABLE(en, value, HP45XXX_CHG_SAFETY_EXPIRED_MASK, HP45XXX_CHG_SAFETY_EXPIRED_ENABLE, HP45XXX_CHG_SAFETY_EXPIRED_BIT_SHIFT); + hp45xxx_write_reg(HP45XXX_REG_INT0_ENABLE, value); + return SUCCESS; +} + +bool hp45xxx_read_status_wdt_fault(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_CHIP_STAT0, &value); + return (value & HP45XXX_WDT_FAULT_MASK) ? true : false; +} + +bool hp45xxx_read_chrg_stat_not_chrg(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_CHIP_STAT0, &value); + return ((value & HP45XXX_CHRG_STAT_MASK) == HP45XXX_CHRG_STAT_NOT_CHRG) ? true : false; +} + +bool hp45xxx_read_chrg_stat_dead_chrg(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_CHIP_STAT0, &value); + return ((value & HP45XXX_CHRG_STAT_MASK) == HP45XXX_CHRG_STAT_DEAD_CHRG) ? true : false; +} + +bool hp45xxx_read_chrg_stat_pre_chrg(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_CHIP_STAT0, &value); + return ((value & HP45XXX_CHRG_STAT_MASK) == HP45XXX_CHRG_STAT_PRE_CHRG) ? true : false; +} + +bool hp45xxx_read_chrg_stat_fast_chrg(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_CHIP_STAT0, &value); + return ((value & HP45XXX_CHRG_STAT_MASK) == HP45XXX_CHRG_STAT_FAST_CHRG) ? true : false; +} + +bool hp45xxx_read_chrg_stat_chrg_done(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_CHIP_STAT0, &value); + return ((value & HP45XXX_CHRG_STAT_MASK) == HP45XXX_CHRG_STAT_CHRG_DONE) ? true : false; +} + +bool hp45xxx_read_status_vindpm(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_CHIP_STAT0, &value); + return (value & HP45XXX_VIN_DPM_STAT_MASK) ? true : false; +} + +bool hp45xxx_read_status_vbus_pg(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_CHIP_STAT0, &value); + return (value & HP45XXX_VBUS_PG_STAT_MASK) ? true : false; +} + +bool hp45xxx_read_status_thermal(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_CHIP_STAT0, &value); + return (value & HP45XXX_THERM_STAT_MASK) ? true : false; +} + +void hp45xxx_update_status_chip_stat1(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_CHIP_STAT1, &value); + if(value & HP45XXX_VIN_FAULT_MASK) + hp45xxx_chip_state.vin_fault |= true; + if(value & HP45XXX_TSHUT_MASK) + hp45xxx_chip_state.tshut |= true; + if(value & HP45XXX_VBAT_OVP_MASK) + hp45xxx_chip_state.vbat_ovp |= true; + if(value & HP45XXX_SAFETY_MODE_MASK) + hp45xxx_chip_state.safety_mode |= true; + hp45xxx_chip_state.ntc_hot = (value & HP45XXX_NTC_HOT_MASK) ? true : false; + hp45xxx_chip_state.ntc_cold = (value & HP45XXX_NTC_COLD_MASK) ? true : false; +} + +void hp45xxx_clear_status_chip_stat1(void){ + hp45xxx_chip_state.vin_fault = false; + hp45xxx_chip_state.tshut = false; + hp45xxx_chip_state.vbat_ovp = false; + hp45xxx_chip_state.safety_mode = false; +} + +bool hp45xxx_read_status_vin_fault(void){ + hp45xxx_update_status_chip_stat1(); + return hp45xxx_chip_state.vin_fault; +} + +bool hp45xxx_read_status_tshut(void){ + hp45xxx_update_status_chip_stat1(); + return hp45xxx_chip_state.tshut; +} + +bool hp45xxx_read_status_vbat_ovp(void){ + hp45xxx_update_status_chip_stat1(); + return hp45xxx_chip_state.vbat_ovp; +} + +bool hp45xxx_read_status_safety_mode(void){ + hp45xxx_update_status_chip_stat1(); + return hp45xxx_chip_state.safety_mode; +} + +bool hp45xxx_read_status_ntc_hot(void){ + hp45xxx_update_status_chip_stat1(); + return hp45xxx_chip_state.ntc_hot; +} + +bool hp45xxx_read_status_ntc_cold(void){ + hp45xxx_update_status_chip_stat1(); + return hp45xxx_chip_state.ntc_cold; +} + +bool hp45xxx_read_status_iindpm(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_CHIP_STAT2, &value); + return (value & HP45XXX_IIN_DPM_STAT_MASK) ? true : false; +} + +bool hp45xxx_read_status_vin_entry_rst(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_WAKE_UP_SRC, &value); + if(value & HP45XXX_ENTRY_RST_STAT_MASK){ + hp45xxx_write_reg(HP45XXX_REG_SYS_WAKE_UP_SRC,HP45XXX_ENTRY_RST_STAT_MASK); + return true; + } + return false; +} + +bool hp45xxx_read_status_pwrkey_rst(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_WAKE_UP_SRC, &value); + if(value & HP45XXX_PWRKEY_RST_STAT_MASK){ + hp45xxx_write_reg(HP45XXX_REG_SYS_WAKE_UP_SRC,HP45XXX_PWRKEY_RST_STAT_MASK); + return true; + } + return false; +} + +bool hp45xxx_read_status_hiccup_latch(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_WAKE_UP_SRC, &value); + if(value & HP45XXX_HICCUP_LATCH_STAT_MASK){ + hp45xxx_write_reg(HP45XXX_REG_SYS_WAKE_UP_SRC,HP45XXX_HICCUP_LATCH_STAT_MASK); + return true; + } + return false; +} + +bool hp45xxx_read_status_pwrkey_wk(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_WAKE_UP_SRC, &value); + if(value & HP45XXX_PWRKEY_WK_MASK){ + hp45xxx_write_reg(HP45XXX_REG_SYS_WAKE_UP_SRC,HP45XXX_PWRKEY_WK_MASK); + return true; + } + return false; +} + +bool hp45xxx_read_status_vbus_wk(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_SYS_WAKE_UP_SRC, &value); + if(value & HP45XXX_VBUS_WK_MASK){ + hp45xxx_write_reg(HP45XXX_REG_SYS_WAKE_UP_SRC,HP45XXX_VBUS_WK_MASK); + return true; + } + return false; +} + +bool hp45xxx_read_status_watchdog_int(void){ + uint8_t value; + hp45xxx_read_reg(HP45XXX_REG_INT0_STAT, &value); + return (value & HP45XXX_WATCHDOG_INT_MASK) ? true : false; +} \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/charge/hp4570/hp4570.h b/code/sdk/zhihui/drivers/charge/hp4570/hp4570.h new file mode 100644 index 000000000..57d9318c4 --- /dev/null +++ b/code/sdk/zhihui/drivers/charge/hp4570/hp4570.h @@ -0,0 +1,505 @@ +/** + * @file HP4570.h + * @brief This is the header file containing the HP4570 I2C Settings and Control. + Includes API for computations related to configuring HP4570 Registers + and Functions to Control the HP4570 Family IC. + * @author Hypower Applications + * @date June 26, 2023 + * + * Copyright (C) 2023 Hypower Microelectronics (Wuxi) Co., Ltd. All rights reserved. + * + * THIS SOFTWARE IS PROVIDED BY HYPOWER MICROELECTRONICS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL HYPOWER MICROELECTRONICS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA, + * OR PROFITS;OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef HP4570_CHARGER_H +#define HP4570_CHARGER_H + +#include +#include +#include "chip_io.h" + +// for example +// Avoid using "X", x stands for General Condition +enum hp45xxx_chip_ver { + HP4570A,// all default, package typeA + HP4570B,// all default, package typeB + HP4570I = 0x0A,// different ichg range and step + HP4570J,// different default charge enable + HP4570K,// different i2c address + HP4580A = 0x10,// just different name + HP4570C9 = 0x02,// just different name +}; + + +// 7-bit Slave Address +#define HP45XXX_I2C_ADDR 0x07 +#define HP4570K_I2C_ADDR 0x3F + +// Register address definitions +#define HP45XXX_REG_DPM_SET 0x00 +#define HP45XXX_REG_SYS_CTRL0 0x01 +#define HP45XXX_REG_SYS_CTRL1 0x02 +#define HP45XXX_REG_IDSG_ITERM_SET 0x03 +#define HP45XXX_REG_BAT_VOL_SET 0x04 +#define HP45XXX_REG_SYS_CTRL2 0x05 +#define HP45XXX_REG_SYS_CTRL3 0x06 +#define HP45XXX_REG_SYS_CTRL4 0x07 +#define HP45XXX_REG_CHIP_STAT0 0x08 +#define HP45XXX_REG_CHIP_STAT1 0x09 +#define HP45XXX_REG_CHIP_STAT2 0x0B +#define HP45XXX_REG_VIPRE 0x0C +#define HP45XXX_REG_EXT_CTRL 0x0D +#define HP45XXX_REG_SYS_CTRL5 0x15 +#define HP45XXX_REG_SYS_CTRL6 0x19 +#define HP45XXX_REG_SYS_CTRL7 0x1B +#define HP45XXX_REG_SYS_WAKE_UP_SRC 0x1E +#define HP45XXX_REG_INT0_ENABLE 0x50 +#define HP45XXX_REG_INT0_STAT 0x58 +#define HP45XXX_REG_VID_LSB 0x60 +#define HP45XXX_REG_VID_MSB 0x61 +#define HP45XXX_REG_PID_LSB 0x62 +#define HP45XXX_REG_PID_MSB 0x63 +#define HP45XXX_REG_CHIP_VER 0x64 + +#ifndef GENMASK +#define GENMASK(h, l) (((uint8_t) -1 >> (7 - (h))) & ~((1U << (l)) - 1)) +#endif + +#define HP45XXX_VIN_MIN_MASK GENMASK(7, 4) +#define HP45XXX_VIN_MIN_STEP_mV 100 +#define HP45XXX_VIN_MIN_OFFSET_mV 3800 +#define HP45XXX_VIN_MIN_MIN_mV 3800 +#define HP45XXX_VIN_MIN_MAX_mV 4700 +#define HP45XXX_VIN_MIN_BIT_SHIFT 4 + +#define HP45XXX_IINDPM_MASK GENMASK(2, 0) +#define HP45XXX_IINDPM_STEP_mA 100 +#define HP45XXX_IINDPM_OFFSET_mA 100 +#define HP45XXX_IINDPM_MIN_mA 100 +#define HP45XXX_IINDPM_MAX_mA 900 +#define HP45XXX_IINDPM_BIT_SHIFT 0 + +#define HP45XXX_NUM_TRST_DGL_VAL 4 +#define HP45XXX_TRST_DGL_MASK GENMASK(7, 6) +#define HP45XXX_TRST_DGL_BIT_SHIFT 6 + +#define HP45XXX_NUM_TRST_DUR_VAL 2 +#define HP45XXX_TRST_DUR_MASK BIT(5) +#define HP45XXX_TRST_DUR_BIT_SHIFT 5 + +#define HP45XXX_EN_HIZ_MASK BIT(4) +#define HP45XXX_EN_HIZ_ENABLE 1 +#define HP45XXX_EN_HIZ_BIT_SHIFT 4 + +#define HP45XXX_CEB_MASK BIT(3) +#define HP45XXX_CEB_ENABLE 0 +#define HP45XXX_CEB_BIT_SHIFT 3 + +#define HP45XXX_VBAT_UVLO_MASK GENMASK(2, 0) +#define HP45XXX_VBAT_UVLO_STEP_mV 100 +#define HP45XXX_VBAT_UVLO_OFFSET_mV 2400 +#define HP45XXX_VBAT_UVLO_MIN_mV 2400 +#define HP45XXX_VBAT_UVLO_MAX_mV 3100 +#define HP45XXX_VBAT_UVLO_DEF_mV 2800 +#define HP45XXX_VBAT_UVLO_BIT_SHIFT 0 + +#define HP45XXX_REG_RST_MASK BIT(7) +#define HP45XXX_REG_RST_RESET 1 +#define HP45XXX_REG_RST_BIT_SHIFT 7 + +#define HP45XXX_WDT_RST_MASK BIT(6) +#define HP45XXX_WDT_RST_RESET 1 +#define HP45XXX_WDT_RST_BIT_SHIFT 6 + +#define HP45XXX_ICHG_MASK GENMASK(5, 0) +#define HP45XXX_ICHG_STEP_mA 8 +#define HP45XXX_ICHG_OFFSET_mA 8 +#define HP45XXX_ICHG_MIN_mA 8 +#define HP45XXX_ICHG_MAX_mA 512 +#define HP4570I_ICHG_STEP_mA 8 +#define HP4570I_ICHG_OFFSET_mA 8 +#define HP4570I_ICHG_MIN_mA 8 +#define HP4570I_ICHG_MAX_mA 512 +#define HP45XXX_ICHG_BIT_SHIFT 0 + +#define HP45XXX_IBATFET_OCP_MASK GENMASK(7, 4) +#define HP45XXX_IBATFET_OCP_STEP_mA 200 +#define HP45XXX_IBATFET_OCP_OFFSET_mA 200 +#define HP45XXX_IBATFET_OCP_MIN_mA 200 +#define HP45XXX_IBATFET_OCP_MAX_mA 3200 +#define HP45XXX_IBATFET_OCP_BIT_SHIFT 4 + +#define HP45XXX_ITERM_MASK GENMASK(3, 0) +#define HP45XXX_ITERM_STEP_mA 2 +#define HP45XXX_ITERM_OFFSET_mA 1 +#define HP45XXX_ITERM_MIN_mA 1 +#define HP45XXX_ITERM_MAX_mA 31 +#define HP45XXX_ITERM_BIT_SHIFT 0 + +#define HP45XXX_VBATREG_MASK GENMASK(7, 2) +#define HP45XXX_VBATREG_STEP_mV 12.5 +#define HP45XXX_VBATREG_OFFSET_mV 4000 +#define HP45XXX_VBATREG_MIN_mV 4000 +#define HP45XXX_VBATREG_MAX_mV 4500 +#define HP45XXX_VBATREG_BIT_SHIFT 2 + +#define HP45XXX_NUM_VBAT_PRE_VAL 4 +#define HP45XXX_VBAT_PRE_0_MASK BIT(1) +#define HP45XXX_VBAT_PRE_1_MASK BIT(4) +#define HP45XXX_VBAT_PRE_0_BIT_SHIFT 1 +#define HP45XXX_VBAT_PRE_1_BIT_SHIFT 4 + +#define HP45XXX_NUM_VRE_CHG_VAL 2 +#define HP45XXX_VRE_CHG_MASK BIT(0) +#define HP45XXX_VRE_CHG_BIT_SHIFT 0 + +#define HP45XXX_EN_WDT_DSG_MASK BIT(7) +#define HP45XXX_EN_WDT_DSG_ENABLE 1 +#define HP45XXX_EN_WDT_DSG_BIT_SHIFT 7 + +#define HP45XXX_NUM_WDT_TIMER_VAL 4 +#define HP45XXX_WDT_TIMER_MASK GENMASK(6, 5) +#define HP45XXX_WDT_TIMER_DIS 0 +#define HP45XXX_WDT_TIMER_BIT_SHIFT 5 + +#define HP45XXX_EN_TERM_MASK BIT(4) +#define HP45XXX_EN_TERM_ENABLE 1 +#define HP45XXX_EN_TERM_BIT_SHIFT 4 + +#define HP45XXX_EN_TIMER_MASK BIT(3) +#define HP45XXX_EN_TIMER_ENABLE 1 +#define HP45XXX_EN_TIMER_BIT_SHIFT 3 + +#define HP45XXX_NUM_CHG_TIMER_VAL 4 +#define HP45XXX_CHG_TIMER_MASK GENMASK(2, 1) +#define HP45XXX_CHG_TIMER_BIT_SHIFT 1 + +#define HP45XXX_TERM_TMR_MASK BIT(0) +#define HP45XXX_TERM_TMR_ENABLE 1 +#define HP45XXX_TERM_TMR_BIT_SHIFT 0 + +#define HP45XXX_NTC_EN_MASK BIT(7) +#define HP45XXX_NTC_EN_ENABLE 1 +#define HP45XXX_NTC_EN_BIT_SHIFT 7 + +#define HP45XXX_TMR2X_EN_MASK BIT(6) +#define HP45XXX_TMR2X_EN_ENABLE 1 +#define HP45XXX_TMR2X_EN_BIT_SHIFT 6 + +#define HP45XXX_BATFET_DIS_MASK BIT(5) +#define HP45XXX_BATFET_DIS_ENABLE 1 +#define HP45XXX_BATFET_DIS_BIT_SHIFT 5 + +#define HP45XXX_PG_INT_CONTROL_MASK BIT(4) +#define HP45XXX_PG_INT_CONTROL_ENABLE 0 +#define HP45XXX_PG_INT_CONTROL_BIT_SHIFT 4 + +#define HP45XXX_CHG_DONE_INT_EN_MASK BIT(3) +#define HP45XXX_CHG_DONE_INT_EN_ENABLE 0 +#define HP45XXX_CHG_DONE_INT_EN_BIT_SHIFT 3 + +#define HP45XXX_CHG_STAT_INT_EN_MASK BIT(2) +#define HP45XXX_CHG_STAT_INT_EN_ENABLE 0 +#define HP45XXX_CHG_STAT_INT_EN_BIT_SHIFT 2 + +#define HP45XXX_NTC_TRANSITION_EN_MASK BIT(1) +#define HP45XXX_NTC_TRANSITION_EN_ENABLE 0 +#define HP45XXX_NTC_TRANSITION_EN_BIT_SHIFT 1 + +#define HP45XXX_BATOVP_INT_EN_MASK BIT(0) +#define HP45XXX_BATOVP_INT_EN_ENABLE 0 +#define HP45XXX_BATOVP_INT_EN_BIT_SHIFT 0 + +#define HP45XXX_EN_PCB_OTP_MASK BIT(7) +#define HP45XXX_EN_PCB_OTP_ENABLE 0 +#define HP45XXX_EN_PCB_OTP_BIT_SHIFT 7 + +#define HP45XXX_VINDPM_EN_MASK BIT(6) +#define HP45XXX_VINDPM_EN_ENABLE 1 +#define HP45XXX_VINDPM_EN_BIT_SHIFT 6 + +#define HP45XXX_NUM_THERMAL_REG_VAL 4 +#define HP45XXX_THERMAL_REG_MASK GENMASK(5, 4) +#define HP45XXX_THERMAL_REG_BIT_SHIFT 4 + +#define HP45XXX_VSYS_REG_MASK GENMASK(2, 0) +#define HP45XXX_VSYS_REG_STEP_mV 100 +#define HP45XXX_VSYS_REG_OFFSET_mV 4250 +#define HP45XXX_VSYS_REG_MIN_mV 4250 +#define HP45XXX_VSYS_REG_MAX_mV 4950 +#define HP45XXX_VSYS_REG_BIT_SHIFT 0 + +#define HP45XXX_CHRG_STAT_MASK GENMASK(5, 3) +#define HP45XXX_CHRG_STAT_BIT_SHIFT 3 +#define HP45XXX_CHRG_STAT_NOT_CHRG 0 +#define HP45XXX_CHRG_STAT_DEAD_CHRG (BIT(5) | BIT(3)) +#define HP45XXX_CHRG_STAT_PRE_CHRG BIT(3) +#define HP45XXX_CHRG_STAT_FAST_CHRG BIT(4) +#define HP45XXX_CHRG_STAT_CHRG_DONE (BIT(4) | BIT(3)) + +#define HP45XXX_WDT_FAULT_MASK BIT(7) +#define HP45XXX_VIN_DPM_STAT_MASK BIT(2) +#define HP45XXX_VBUS_PG_STAT_MASK BIT(1) +#define HP45XXX_THERM_STAT_MASK BIT(0) + +#define HP45XXX_NUM_SHIP_DGL_VAL 4 +#define HP45XXX_SHIP_DGL_MASK GENMASK(7, 6) +#define HP45XXX_SHIP_DGL_BIT_SHIFT 6 + +#define HP45XXX_VIN_FAULT_MASK BIT(5) +#define HP45XXX_TSHUT_MASK BIT(4) +#define HP45XXX_VBAT_OVP_MASK BIT(3) +#define HP45XXX_SAFETY_MODE_MASK BIT(2) +#define HP45XXX_NTC_HOT_MASK BIT(1) +#define HP45XXX_NTC_COLD_MASK BIT(0) + +#define HP45XXX_IIN_DPM_STAT_MASK BIT(6) + +#define HP45XXX_NUM_IN_EXIT_SHIP_TSET_VAL 2 +#define HP45XXX_IN_EXIT_SHIP_TSET_MASK BIT(5) +#define HP45XXX_IN_EXIT_SHIP_TSET_BIT_SHIFT 5 + +#define HP45XXX_IPRE_MASK GENMASK(3, 0) +#define HP45XXX_IPRE_STEP_mA 2 +#define HP45XXX_IPRE_OFFSET_mA 1 +#define HP45XXX_IPRE_MIN_mA 1 +#define HP45XXX_IPRE_MAX_mA 31 +#define HP45XXX_IPRE_BIT_SHIFT 0 + +#define HP45XXX_VIN_ENTRY_RST_EN_MASK BIT(4) +#define HP45XXX_VIN_ENTRY_RST_EN_ENABLE 1 +#define HP45XXX_VIN_ENTRY_RST_EN_BIT_SHIFT 4 + +#define HP45XXX_IDEADBAT_MASK GENMASK(3, 0) +#define HP45XXX_IDEADBAT_STEP_mA 2 +#define HP45XXX_IDEADBAT_OFFSET_mA 1 +#define HP45XXX_IDEADBAT_MIN_mA 1 +#define HP45XXX_IDEADBAT_MAX_mA 31 +#define HP45XXX_IDEADBAT_BIT_SHIFT 0 + +#define HP45XXX_VSYS_RST_MASK BIT(7) +#define HP45XXX_VSYS_RST_RESET 1 +#define HP45XXX_VSYS_RST_BIT_SHIFT 7 + +#define HP45XXX_EN_IINDPM_MASK BIT(5) +#define HP45XXX_EN_IINDPM_ENABLE 1 +#define HP45XXX_EN_IINDPM_BIT_SHIFT 5 + +#define HP45XXX_NUM_KEY_WK_SET_VAL 2 +#define HP45XXX_KEY_WK_SET_MASK BIT(2) +#define HP45XXX_KEY_WK_SET_BIT_SHIFT 2 + +#define HP45XXX_NUM_T_HICCUP_VAL 2 +#define HP45XXX_T_HICCUP_MASK BIT(0) +#define HP45XXX_T_HICCUP_BIT_SHIFT 0 + +#define HP45XXX_WDT_VSYS_RST_MASK BIT(3) +#define HP45XXX_WDT_VSYS_RST_ENABLE 1 +#define HP45XXX_WDT_VSYS_RST_BIT_SHIFT 3 + +#define HP45XXX_EN_SHIP_DGL_MASK BIT(2) +#define HP45XXX_EN_SHIP_DGL_ENABLE 1 +#define HP45XXX_EN_SHIP_DGL_BIT_SHIFT 2 + +#define HP45XXX_EN_WDT_CHG_MASK BIT(7) +#define HP45XXX_EN_WDT_CHG_ENABLE 1 +#define HP45XXX_EN_WDT_CHG_BIT_SHIFT 7 + +#define HP45XXX_EN_PWRKEY_RST_MASK BIT(5) +#define HP45XXX_EN_PWRKEY_RST_ENABLE 1 +#define HP45XXX_EN_PWRKEY_RST_BIT_SHIFT 5 + +#define HP45XXX_VSYS_RST_WVBUS_MASK BIT(4) +#define HP45XXX_VSYS_RST_WVBUS_ENABLE 1 +#define HP45XXX_VSYS_RST_WVBUS_BIT_SHIFT 4 + +#define HP45XXX_PWRKEY_EN_MASK BIT(2) +#define HP45XXX_PWRKEY_EN_ENABLE 1 +#define HP45XXX_PWRKEY_EN_BIT_SHIFT 2 + +#define HP45XXX_VBUS_WK_EN_MASK BIT(1) +#define HP45XXX_VBUS_WK_EN_ENABLE 1 +#define HP45XXX_VBUS_WK_EN_BIT_SHIFT 1 + +#define HP45XXX_PWRKEY_WK_EN_MASK BIT(0) +#define HP45XXX_PWRKEY_WK_EN_ENABLE 1 +#define HP45XXX_PWRKEY_WK_EN_BIT_SHIFT 0 + +#define HP45XXX_ENTRY_RST_STAT_MASK BIT(7) +#define HP45XXX_PWRKEY_RST_STAT_MASK BIT(5) +#define HP45XXX_HICCUP_LATCH_STAT_MASK BIT(4) +#define HP45XXX_PWRKEY_WK_MASK BIT(1) +#define HP45XXX_VBUS_WK_MASK BIT(0) + +#define HP45XXX_WATCHDOG_INT_EN_MASK BIT(7) +#define HP45XXX_WATCHDOG_INT_EN_ENABLE 0 +#define HP45XXX_WATCHDOG_INT_EN_BIT_SHIFT 7 + +#define HP45XXX_CHG_SAFETY_EXPIRED_MASK BIT(1) +#define HP45XXX_CHG_SAFETY_EXPIRED_ENABLE 0 +#define HP45XXX_CHG_SAFETY_EXPIRED_BIT_SHIFT 1 + +#define HP45XXX_WATCHDOG_INT_MASK BIT(7) + +static const uint32_t hp45xxx_trst_dgl_time[HP45XXX_NUM_TRST_DGL_VAL] = { + 80000, 120000, 160000, 200000 +}; +static const uint16_t hp45xxx_trst_dur_time[HP45XXX_NUM_TRST_DUR_VAL] = { + 2000, 4000 +}; +static const uint16_t hp45xxx_vbat_pre_voltage[HP45XXX_NUM_VBAT_PRE_VAL] = { + 2800, 3000, 3200, 3300 +}; +static const uint8_t hp45xxx_vre_chg_voltage[HP45XXX_NUM_VRE_CHG_VAL] = { + 100, 200 +}; +static const uint32_t hp45xxx_watchdog_time[HP45XXX_NUM_WDT_TIMER_VAL] = { + 0, 40000, 80000, 160000 +}; +static const uint8_t hp45xxx_fast_chg_time[HP45XXX_NUM_CHG_TIMER_VAL] = { + 3, 5, 8, 12 +}; +static const uint8_t hp45xxx_thermal_reg_degree[HP45XXX_NUM_THERMAL_REG_VAL] = { + 80, 100, 120, 130 +}; +static const uint16_t hp45xxx_ship_dgl_time[HP45XXX_NUM_SHIP_DGL_VAL] = { + 1000, 2000, 4000, 8000 +}; +static const uint16_t hp45xxx_in_exit_ship_tset_time[HP45XXX_NUM_IN_EXIT_SHIP_TSET_VAL] = { + 50, 2000 +}; +static const uint16_t hp45xxx_key_wk_set_time[HP45XXX_NUM_KEY_WK_SET_VAL] = { + 2000, 200 +}; +static const uint16_t hp45xxx_t_hiccup_time[HP45XXX_NUM_T_HICCUP_VAL] = { + 256, 512 +}; + +//Default value for a register. +#define HP45XXX_REG_DPM_SET_DEF 0x84 +#define HP45XXX_REG_SYS_CTRL0_DEF 0xA4 +#define HP45XXX_REG_SYS_CTRL1_DEF 0x00 +#define HP45XXX_REG_IDSG_ITERM_SET_DEF 0x91 +#define HP45XXX_REG_BAT_VOL_SET_DEF 0x43 +#define HP45XXX_REG_SYS_CTRL2_DEF 0x3A +#define HP45XXX_REG_SYS_CTRL3_DEF 0x40 +#define HP45XXX_REG_SYS_CTRL4_DEF 0x24 +#define HP45XXX_REG_CHIP_STAT1_DEF 0x00 +#define HP45XXX_REG_VIPRE_DEF 0x01 +#define HP45XXX_REG_EXT_CTRL_DEF 0x11 +#define HP45XXX_REG_SYS_CTRL5_DEF 0x31 +#define HP45XXX_REG_SYS_CTRL6_DEF 0x0C +#define HP45XXX_REG_SYS_CTRL7_DEF 0xB6 +#define HP45XXX_REG_INT0_ENABLE_DEF 0x4C +#define HP45XXX_REG_INT0_STAT_DEF 0x00 +#define HP45XXX_REG_VID_LSB_DEF 0xC6 +#define HP45XXX_REG_VID_MSB_DEF 0x34 +#define HP45XXX_REG_PID_LSB_DEF 0x81 +#define HP45XXX_REG_PID_MSB_DEF 0x00 + +#define HP4570J_REG_SYS_CTRL0_DEF 0xAC + +// Function Declarations +bool hp45xxx_init(uint8_t chip_ver); +bool hp45xxx_identify(uint8_t chip_ver); + +void hp45xxx_set_pwrkey_en(bool en);// PWRKEY function enable control +bool hp45xxx_set_trst_dgl_mS(uint32_t time);// Pull INT low to disconnect the battery then auto on + +void hp45xxx_reg_rst(void);// Register reset +void hp45xxx_wdt_rst(void);// Watchdog timer reset +void hp45xxx_vsys_rst(void);// VSYS reset +void hp45xxx_set_rst_vin_entry_en(bool en);// VIN 4 pulse Reset Enable +void hp45xxx_set_rst_pwrkey_en(bool en);// VSYS reset enable PWRKEY operation Enable +void hp45xxx_set_rst_vsys_wvbus_en(bool en);// VSYS reset operation when VBUS is present + +void hp45xxx_entry_shipmode(void); +bool hp45xxx_set_batfet_dis(bool en);// BATFET force off +void hp45xxx_set_vbus_wk_en(bool en);// VBUS plug in to exit ship mode +void hp45xxx_set_pwrkey_wk_en(bool en);// PWRKEY be low to exit ship mode +bool hp45xxx_set_vin_exit_ship_tset_mS(uint16_t time);// IN Port Power Good Time Setting for Exit Shipmode +bool hp45xxx_set_key_wk_time_mS(uint16_t time);// KEY wake up time set +void hp45xxx_set_ship_dgl_en(bool en);// Enter ship mode deglitch enable control +bool hp45xxx_set_ship_dgl_mS(uint16_t time);// Enter ship mode deglitch time + + +void hp45xxx_set_hiz_en(bool en);// VBUS power path Hi-Z mode enable +void hp45xxx_set_ceb_en(bool en);// Enable or disable charging function + +bool hp45xxx_set_vsys_reg_mV(float voltage);// VSYS regulation +bool hp45xxx_set_trst_dur_mS(uint16_t time);// VSYS off time before auto on +void hp45xxx_set_vindpm_en(bool en); +void hp45xxx_set_iindpm_en(bool en); +bool hp45xxx_set_vindpm_mV(uint16_t voltage); +bool hp45xxx_set_iindpm_mA(uint16_t current); + +bool hp45xxx_set_chrg_vpre_mV(uint8_t voltage);// Battery pre-charge voltage set +bool hp45xxx_set_chrg_vbat_mV(float voltage); +bool hp45xxx_set_chrg_vre_mV(uint8_t voltage);// Battery re-charge voltage set +bool hp45xxx_set_chrg_idead_mA(uint16_t current); +bool hp45xxx_set_chrg_ipre_mA(uint16_t current);// Battery pre-charge current set +bool hp45xxx_set_chrg_ichg_mA(uint16_t current); +bool hp4570i_set_chrg_ichg_mA(uint16_t current); +bool hp45xxx_set_chrg_iterm_mA(uint8_t current); +void hp45xxx_set_chrg_term_en(bool en);// Termination is allowed or not +void hp45xxx_set_chrg_term_tmr(bool en);// Termination timer control +void hp45xxx_set_chrg_timer_en(bool en);// Safety timer set during charging +bool hp45xxx_set_chrg_timer_hour(uint8_t time);// Fast charging mode timer set +void hp45xxx_set_chrg_tmr2x_en(bool en);// Safety timer double during DPM/Thermal + +bool hp45xxx_set_ibatfet_ocp_mA(uint16_t current); +bool hp45xxx_set_vbat_uvlo_mV(uint16_t voltage);// Battery UVLO falling threshold,hys is about =150mV +bool hp45xxx_set_t_hiccup_mS(uint16_t time);// Hiccup timer set + +void hp45xxx_set_wdt_timer_en(bool en);// If watchdog timer already enable, function will do noting, otherwise will set as default +bool hp45xxx_set_wdt_timer_mS(uint32_t time); +void hp45xxx_set_wdt_chg_en(bool en);// Watchdog timer function enable control during charging +void hp45xxx_set_wdt_dsg_en(bool en);// Watchdog timer function enable control during discharging +void hp45xxx_set_wdt_vsys_rst_en(bool en);// Watch dog fault VSYS reset enable control + +void hp45xxx_set_ntc_en(bool en);// NTC enable during charging +void hp45xxx_set_pcb_otp_en(bool en);// PCB_OTP enable +bool hp45xxx_set_thermal_reg_degree(uint8_t degree);// Die temp control by Thermal loop + +void hp45xxx_set_int_pg_en(bool en);// Power good int enable control +void hp45xxx_set_int_chrg_done_en(bool en);// Charge complete int enable control +void hp45xxx_set_int_chrg_stat_en(bool en);// Charging status dynamic change int enable control +void hp45xxx_set_int_ntc_transition_en(bool en);// Battery temp region change int enable control +void hp45xxx_set_int_batovp_en(bool en);// Battery OVP int enable control +void hp45xxx_set_int_wdt_en(bool en);// Watch dog time expire int enable control +void hp45xxx_set_int_chg_safety_expired_en(bool en);// Charger enter safety int enable control + +bool hp45xxx_read_chrg_stat_not_chrg(void); +bool hp45xxx_read_chrg_stat_dead_chrg(void); +bool hp45xxx_read_chrg_stat_pre_chrg(void); +bool hp45xxx_read_chrg_stat_fast_chrg(void); +bool hp45xxx_read_chrg_stat_chrg_done(void); + +bool hp45xxx_read_status_wdt_fault(void);// Watchdog timer expired 1: Expired; 0: Not Expired +bool hp45xxx_read_status_vindpm(void);// 1: In DPM; 0: Not DPM +bool hp45xxx_read_status_vbus_pg(void);// 1: Power Good; 0: Not Good +bool hp45xxx_read_status_thermal(void);// 1: In thermal; 0: Not thermal +bool hp45xxx_read_status_vin_fault(void);// 1: Input fault ovp or bad source plug out; 0: normal +bool hp45xxx_read_status_tshut(void);// 1: Thermal shutdown; 0: Normal +bool hp45xxx_read_status_vbat_ovp(void);// 1: BAT_OVP; 0: Not BAT_OVP +bool hp45xxx_read_status_safety_mode(void);// Charger safety timer expired 1: Expired; 0: Not Expired +bool hp45xxx_read_status_ntc_hot(void);// 1: Hot; 0: Not hot +bool hp45xxx_read_status_ntc_cold(void);// 1: Cold; 0: Not cold +bool hp45xxx_read_status_iindpm(void);// 1: In DPM; 0: Not DPM +bool hp45xxx_read_status_vin_entry_rst(void);// VIN_ENTRY_RST once happenned +bool hp45xxx_read_status_pwrkey_rst(void);// PWRKEY reset once happened +bool hp45xxx_read_status_hiccup_latch(void);// Hiccup once happened +bool hp45xxx_read_status_pwrkey_wk(void);// PWRKEY 2S low time wake up from ship mode +bool hp45xxx_read_status_vbus_wk(void);// VBUS plug in wake up from ship mode +bool hp45xxx_read_status_watchdog_int(void);// Watch dog time expire IRQ + +#endif // HP4570_CHARGER_H \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/charge/service/charge_at_cmd.c b/code/sdk/zhihui/drivers/charge/service/charge_at_cmd.c new file mode 100644 index 000000000..41d45a67e --- /dev/null +++ b/code/sdk/zhihui/drivers/charge/service/charge_at_cmd.c @@ -0,0 +1,99 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-23 15:11:01 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-24 21:09:51 + * @FilePath: /sdk615/code/sdk/zhihui/drivers/charge/HP4570/service/charge_at_cmd.c + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +#include "at_cmd_api.h" +#include "pm_control.h" +#include "debug_print.h" +#include "hp4570.h" +#include "custom_utils.h" + +#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0])) +#define CHARGE_MAX_ARGC 8 +#define CHARGE_MAX_ARGV_LEN 64 + +#define CHARGE_SUCCESS 0 +#define CHARGE_FAILED (-1) + +static int ConvertCmdToParam(uint8_t *para, uint32_t paraLen, + uint8_t argv[CHARGE_MAX_ARGC][CHARGE_MAX_ARGV_LEN], uint32_t* argc) +{ + uint32_t pos = 0; + *argc = 0; + + for (uint32_t i = 0; i < paraLen; i++) { + if ((para[i] == ' ' || para[i] == '\n') && pos != 0) { + argv[*argc][pos] = '\0'; + *argc += 1; + pos = 0; + if (*argc >= CHARGE_MAX_ARGC) { + return CHARGE_FAILED; + } + } else { + argv[*argc][pos] = para[i]; + pos++; + if (pos >= CHARGE_MAX_ARGV_LEN) { + return CHARGE_FAILED; + } + } + } + if (pos != 0) { + *argc += 1; + } + + return CHARGE_SUCCESS; +} + +static uint8_t g_charge_test_started = 0; +static uint32_t hp4570_test(uint8_t *para, uint32_t paraLen, char *strCommonCmd) +{ + UNUSED(strCommonCmd); + uint8_t argv[CHARGE_MAX_ARGC][CHARGE_MAX_ARGV_LEN] = { 0 }; + uint32_t argc = 0; + + int ret = ConvertCmdToParam(para, paraLen, argv, &argc); + if (ret != CHARGE_SUCCESS) { + PRINT("CHARGETEST ATCMD Convert Command Failed\n"); + return ERRCODE_FAIL; + } + + if (argc != 1) { + PRINT("CHARGETEST ATCMD Set Parameter Failed argc %d\n",argc); + return ERRCODE_FAIL; + } + uint8_t start = (uint8_t)atoi((char*)argv[0]); + if (start == 1) + { + // if (g_CHARGE_test_started == 1) + // { + // PRINT("SKIP CHARGEtest start, already started\r\n"); + // return 0; + // } + hp45xxx_init(HP4570C9); + g_charge_test_started = 1; + }else if (start == 2){ + if (g_charge_test_started == 0) + { + PRINT("SKIP CHARGEtest stop, already stopped\r\n"); + return 0; + } + // ots_stop(); + g_charge_test_started = 0 ; + }else{ + custom_exit_lpm(); + } + return ERRCODE_SUCC; +} +static const at_cmd_table_t g_charge_cmd[] = { + { (char *)"AT^CHARGETEST", hp4570_test, (char *)"1" }, +}; + +at_cmd_table_t *get_charge_cmd_table(uint32_t *cmd_count) +{ + *cmd_count = ARRAY_COUNT(g_charge_cmd); + return (at_cmd_table_t *)g_charge_cmd; +} diff --git a/code/sdk/zhihui/drivers/charge/service/charge_at_cmd.h b/code/sdk/zhihui/drivers/charge/service/charge_at_cmd.h new file mode 100644 index 000000000..e54f7befc --- /dev/null +++ b/code/sdk/zhihui/drivers/charge/service/charge_at_cmd.h @@ -0,0 +1,40 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-18 10:23:22 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-24 17:58:19 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/rotary_encoder/hx3509/hx3509_at_cmd.h + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +/** + * Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved. \n + * + * Description: Provides at cmd register func \n + * Author: @CompanyNameTag \n + * History: \n + * 2022-07-20, Create file. \n + */ +#ifndef HX3509_AT_PROCESS_H +#define HX3509_AT_PROCESS_H + +#include +#include +#include "errcode.h" +#include "soc_osal.h" +#include "at_cmd_api.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +at_cmd_table_t *get_charge_cmd_table(uint32_t *cmd_count); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif diff --git a/code/sdk/zhihui/drivers/charge/service/charge_service.c b/code/sdk/zhihui/drivers/charge/service/charge_service.c new file mode 100644 index 000000000..3f9df3d83 --- /dev/null +++ b/code/sdk/zhihui/drivers/charge/service/charge_service.c @@ -0,0 +1,185 @@ +#include "pinctrl.h" +#include "debug_print.h" +#include "rtc.h" +#include "timer.h" +#include "systick.h" + +static bool g_batter_service_initialled = false; + +#include "tcxo.h" + +#include "cmsis_os2.h" +#include "input_app.h" +#include "charge_service.h" +#include "custom_utils.h" +// #include "custom_runtime.h" +/*** + * 其他cmd在Header里定义,对外开放 +*/ +#define CHARGE_SERVICE_CMD_TIMER_CHECK 0x11 + +osTimerId_t g_charge_service_timer = NULL; + +static bool g_charge_sensor_exist = false; + +static bool g_bat_timer_is_running = false; +#define CHARGE_CHECK_MS 40 +static unsigned long g_app_queue= 0xFFFFFFFF; + +static bool charge_service_running_stat = false; +static void charge_service_check_timer_callback(void* data); + +static void charge_service_timer_start_check(uint32_t rtc_ms) +{ + + if (g_bat_timer_is_running ) + osTimerStop(g_charge_service_timer); + + if (charge_service_running_stat == true) + osTimerStart(g_charge_service_timer, rtc_ms); + + g_bat_timer_is_running = true; + +} +void do_charge_service_check_msg(int cmd); + +static void charge_service_check_timer_callback(void * data) +{ + unused(data); + + g_bat_timer_is_running = false; + + if(charge_service_running_stat == true){ + // mmc5603_check(); //此处只能直接加到timer callback中,采用queue的话,新能跟不上,系统资源会挂掉。 + do_charge_service_check_msg(CHARGE_SERVICE_CMD_TIMER_CHECK); + // charge_service_timer_start_check(CHARGE_CHECK_MS); + } + + + +} +void uapi_charge_service_deinit(void) +{ + /* Stop the time and clear the pin map */ + osTimerStop(g_charge_service_timer); + osTimerDelete(g_charge_service_timer); + + g_bat_timer_is_running = false; + g_charge_service_timer = NULL; +} + + + +void do_charge_service_check_msg(int cmd) +{ + int ret; + if (g_charge_sensor_exist == false) + return ; + common_notify_mail_t mail = {0}; + mail.comm.app = cmd; + ret = osal_msg_queue_write_copy(g_app_queue, (void *)&mail, + sizeof(common_notify_mail_t), 0); + if (ret != OSAL_SUCCESS) { + MMC_ERROR("Error charge Check MSG 0x%02X send failed! err=0x%x! \r\n",cmd, ret); + } + +} + +static void charge_service_check_task(void const *arg) +{ + unused(arg); + int32_t ret; + common_notify_mail_t mail; + uint32_t msgsize = (uint32_t)sizeof(common_notify_mail_t); + while (1) { + ret = osal_msg_queue_read_copy(g_app_queue, (void *)&mail, &msgsize, + OSAL_MSGQ_WAIT_FOREVER); + if (ret != OSAL_SUCCESS) { + continue; + } + + if (mail.comm.app == CHARGE_SERVICE_CMD_TIMER_CHECK) { + // charge_service_check_and_enable_charge(); + MMC_LOG("CHARGE_SERVICE_CMD_TIMER_CHECK\r\n"); + mmc5603_check(); + }else if (mail.comm.app == CHARGE_SERVICE_CMD_START_CHECK){ + // uapi_dev_runtime_set_mmc_status(1); + // do_custom_fileoperate_service_check_msg(CUSTOM_FILEOPERATE_MSG_TYPE_MMCDATA_READ, NULL, 0); + + mmc5603_start(); + LOS_Mdelay(20); + charge_service_running_stat = true; + // uapi_pm_add_sleep_veto(PM_ID_SCREEN); + charge_service_timer_start_check(CHARGE_CHECK_MS); + }else if (mail.comm.app == CHARGE_SERVICE_CMD_STOP_CHECK){ + // uapi_dev_runtime_set_mmc_status(0); + // SavePara_infile(); + // do_custom_fileoperate_service_check_msg(CUSTOM_FILEOPERATE_MSG_TYPE_MMCDATA_SAVE, NULL, 0); + charge_service_running_stat = false; + osTimerStop(g_charge_service_timer); + mmc5603_stop(); + LOS_Mdelay(20); + + // uapi_pm_remove_sleep_veto(PM_ID_SCREEN); + } + + + } +} +void create_charge_queue(void) +{ + + if (osal_msg_queue_create("charge_service_queue", APP_BOARD_MSG_COUNT, &g_app_queue, + 0, sizeof(common_notify_mail_t)) != ERRCODE_SUCC) { + MMC_ERROR("[charge][service] app create mail fail\n"); + } +} + +#define CHARGE_BOARD_TASK_SIZE 0x1000 +errcode_t uapi_charge_service_init(void) +{ + + + PRINT("uapi_charge_service_init !\r\n"); + + + + // if(CUSTOM_MMC5603_CheckID()!=1) + // { + // //TODO 设置磁力计检测是否存在标识 + // PRINT("MMC5603 not found!\r\n"); + // return ERRCODE_FAIL; + // } + // mmc5603_stop(); + g_charge_sensor_exist = true; + + app_task_definition_t taskcfg={0}; + taskcfg.create_queue_handle = create_charge_queue; + taskcfg.attr.name="charge_service"; + taskcfg.attr.priority = osPriorityBelowNormal4; + taskcfg.attr.stack_size = CHARGE_BOARD_TASK_SIZE; + taskcfg.func = charge_service_check_task; + taskcfg.use_mem = USE_EXTEND_MEM; + + errcode_t ret=uapi_create_osThread_custom(&taskcfg); + if(ret != ERRCODE_SUCC) + { + PRINT("create charge_service failed %d!\r\n",ret); + return ERRCODE_FAIL; + } + + g_charge_service_timer = osTimerNew((osTimerFunc_t)charge_service_check_timer_callback, osTimerPeriodic, NULL, NULL); + + + return ERRCODE_SUCC; +} + +// void charge_service_service_resume(void) +// { +// MMC_LOG("charge_service_service resume!\r\n"); +// charge_service_check_timer_callback(0); +// } +// void charge_service_service_suspend(void) +// { +// MMC_LOG("charge_service_service suspend!\r\n"); +// } \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/charge/service/charge_service.h b/code/sdk/zhihui/drivers/charge/service/charge_service.h new file mode 100644 index 000000000..8e0553082 --- /dev/null +++ b/code/sdk/zhihui/drivers/charge/service/charge_service.h @@ -0,0 +1,54 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-23 15:11:01 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-24 17:57:55 + * @FilePath: /sdk615/code/sdk/zhihui/drivers/charge/HP4570/service/charge_service.h + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +/** + * Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved. \n + * + * Description: Test adc header \n + * Author: @CompanyNameTag \n + * History: \n + * 2022-08-16, Create file. \n + */ + +#ifndef CHARGE_SERVICE_H__ +#define CHARGE_SERVICE_H__ + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#include "stdint.h" +#include "errcode.h" + + +#define CHARGE_SERVICE_CMD_START_CHECK 0x12 +#define CHARGE_SERVICE_CMD_STOP_CHECK 0x13 + +errcode_t uapi_charge_service_init(void); +void uapi_charge_service_deinit(void); +/** + * 测试指令 参数cmd 选项: CHARGE_SERVICE_CMD_START_CHECK,CHARGE_SERVICE_CMD_STOP_CHECK +*/ +void do_charge_service_check_msg(int cmd); + +float charge_service_get_fAzimuth(void); +float charge_service_get_fPitch(void); +float charge_service_get_fRoll(void); + + +int charge_service_get_iAccuracy(void); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/mag/mmc5603/MMC5603.c b/code/sdk/zhihui/drivers/mag/mmc5603/MMC5603.c index 185d96aa4..dee38ea98 100644 --- a/code/sdk/zhihui/drivers/mag/mmc5603/MMC5603.c +++ b/code/sdk/zhihui/drivers/mag/mmc5603/MMC5603.c @@ -75,7 +75,8 @@ int CUSTOM_MMC5603_Factory_Test_Mode(void) /* Set Cmm_en bit '0', Disable continuous mode */ I2C_Write_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS, CUSTOM_MMC5603_REG_CTRL2, 0x00); - MMC5603_Delay_Ms(20); + // MMC5603_Delay_Ms(20); + LOS_Mdelay(20); /* Write reg 0x1B */ /* Set Auto_SR_en bit '0', Disable the function of automatic set/reset */ @@ -89,7 +90,8 @@ int CUSTOM_MMC5603_Factory_Test_Mode(void) /* Write 0x01 to register 0x1B, set Take_meas_M bit '1' */ I2C_Write_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS, CUSTOM_MMC5603_REG_CTRL0, CUSTOM_MMC5603_CMD_TMM); /* Delay 10 ms to finish the TM operation */ - MMC5603_Delay_Ms(10); + // MMC5603_Delay_Ms(10); + LOS_Mdelay(10); /* Read register data */ I2C_MultiRead_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS, CUSTOM_MMC5603_REG_DATA, 6, data_reg); /* Get high 16bits data */ @@ -102,7 +104,8 @@ int CUSTOM_MMC5603_Factory_Test_Mode(void) /* Write 0x01 to register 0x1B, set Take_meas_M bit '1' */ I2C_Write_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS,CUSTOM_MMC5603_REG_CTRL0,CUSTOM_MMC5603_CMD_TMM); /* Delay 10 ms to finish the TM operation */ - MMC5603_Delay_Ms(10); + // MMC5603_Delay_Ms(10); + LOS_Mdelay(10); /* Read register data */ I2C_MultiRead_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS, CUSTOM_MMC5603_REG_DATA, 6, data_reg); /* Get high 16bits data */ @@ -134,7 +137,8 @@ void CUSTOM_MMC5603_SET(void) I2C_Write_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS, CUSTOM_MMC5603_REG_CTRL0, CUSTOM_MMC5603_CMD_SET); /* Delay to finish the SET operation */ - MMC5603_Delay_Ms(1); + // MMC5603_Delay_Ms(1); + LOS_Mdelay(1); return; } @@ -148,7 +152,8 @@ void CUSTOM_MMC5603_RESET(void) I2C_Write_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS, CUSTOM_MMC5603_REG_CTRL0, CUSTOM_MMC5603_CMD_RESET); /* Delay to finish the RESET operation */ - MMC5603_Delay_Ms(1); + // MMC5603_Delay_Ms(1); + LOS_Mdelay(1); return; } @@ -220,7 +225,8 @@ int CUSTOM_MMC5603_Auto_SelfTest(void) I2C_Write_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS, CUSTOM_MMC5603_REG_CTRL0, CUSTOM_MMC5603_CMD_AUTO_ST_EN); /* Delay 15ms to finish the selftest process */ - MMC5603_Delay_Ms(15); + // MMC5603_Delay_Ms(15); + LOS_Mdelay(15); /* Read register 0x18, check Sat_sensor bit */ I2C_Read_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS, CUSTOM_MMC5603_REG_STATUS1, ®_status); @@ -278,7 +284,8 @@ int CUSTOM_MMC5603_Saturation_Checking(void) /* Do TM_M after selftest operation */ I2C_Write_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS,CUSTOM_MMC5603_REG_CTRL0,CUSTOM_MMC5603_CMD_TMM); - MMC5603_Delay_Ms(8); + // MMC5603_Delay_Ms(8); + LOS_Mdelay(8); } return ret; @@ -304,15 +311,18 @@ void CUSTOM_MMC5603_Auto_Switch(uint16_t *mag) /* Disable continuous mode */ I2C_Write_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS, CUSTOM_MMC5603_REG_CTRL2, 0x00); - MMC5603_Delay_Ms(15);//Delay 15ms to finish the last sampling + // MMC5603_Delay_Ms(15);//Delay 15ms to finish the last sampling + LOS_Mdelay(15); /* Do SET operation */ I2C_Write_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS, CUSTOM_MMC5603_REG_CTRL0, CUSTOM_MMC5603_CMD_SET); - MMC5603_Delay_Ms(1);//Delay 1ms to finish the SET operation + // MMC5603_Delay_Ms(1);//Delay 1ms to finish the SET operation + LOS_Mdelay(1); /* Do TM_M before next data reading */ I2C_Write_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS,CUSTOM_MMC5603_REG_CTRL0,CUSTOM_MMC5603_CMD_TMM); - MMC5603_Delay_Ms(8);//Delay 8ms to finish the TM_M operation + // MMC5603_Delay_Ms(8);//Delay 8ms to finish the TM_M operation + LOS_Mdelay(8); } } else if(sensor_state == 2) @@ -349,7 +359,8 @@ void CUSTOM_MMC5603_Disable(void) /* Set Cmm_en bit '0', Disable continuous mode */ I2C_Write_Reg(CUSTOM_MMC5603_7BITI2C_ADDRESS, CUSTOM_MMC5603_REG_CTRL2, 0x00); - MMC5603_Delay_Ms(20); + // MMC5603_Delay_Ms(20); + LOS_Mdelay(20); return; } @@ -378,7 +389,8 @@ void CUSTOM_MMC5603_Enable(void) /* Work mode setting */ CUSTOM_MMC5603_Continuous_Mode_With_Auto_SR(CUSTOM_MMC5603_CMD_BW00, 50); - MMC5603_Delay_Ms(20); + // MMC5603_Delay_Ms(20); + LOS_Mdelay(20); return; } diff --git a/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_at_cmd.c b/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_at_cmd.c index 7f368b42d..d821ed686 100644 --- a/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_at_cmd.c +++ b/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_at_cmd.c @@ -1,3 +1,11 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-16 09:56:44 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-16 20:50:13 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/mag/mmc5603/mmc5603_at_cmd.c + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ // #include "mmc5603.h" diff --git a/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_hal.h b/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_hal.h index 9f72a3ea4..069caef2a 100644 --- a/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_hal.h +++ b/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_hal.h @@ -28,6 +28,9 @@ int mmc5603_start(void); int mmc5603_savePara_infile(void); int mmc5603_readPara_infile(void); +void mmc5603_poweron(void); +void mmc5603_poweroff(void); + #ifdef __cplusplus #if __cplusplus } diff --git a/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_main.c b/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_main.c index e1fafe86e..4bed63fb1 100644 --- a/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_main.c +++ b/code/sdk/zhihui/drivers/mag/mmc5603/mmc5603_main.c @@ -11,6 +11,13 @@ #include "mmc_customer.h" #include "MMC5603.h" +#ifdef BOARD_U8 +#include "pm_control.h" +#include "platform_core.h" +#include "pinctrl_porting.h" +#include "hal_gpio.h" +#endif + #ifdef ACC_LIS2DOC #include "acc_service.h" #endif @@ -24,6 +31,9 @@ float magnetic_field_z; #define A_LAYOUT 7 #define M_LAYOUT 0 +// S_AGPIO_R0 PRG_LED_POWER ON 心率等电源控制脚 +#define GPIO_PIN_PRG_LED_ON S_AGPIO_R0 + /* These variables are used to save the magnetic sensor calibrated data. */ float calMagX, calMagY, calMagZ; @@ -71,8 +81,29 @@ static float g_MagPara[4] = {0.0f}; /* This variable is used to save calibrated mag para that need to be saved in system file */ static float saveData[4] = {0.0f}; +void mmc5603_poweron(void) +{ +#ifdef BOARD_U8 + uapi_pin_set_mode(GPIO_PIN_PRG_LED_ON, (pin_mode_t)HAL_PIO_FUNC_GPIO); + uapi_gpio_set_dir(GPIO_PIN_PRG_LED_ON, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_val(GPIO_PIN_PRG_LED_ON, GPIO_LEVEL_HIGH); + LOS_Mdelay(100); +#endif +} +void mmc5603_poweroff(void) +{ +#ifdef BOARD_U8 + uapi_pin_set_mode(GPIO_PIN_PRG_LED_ON, (pin_mode_t)HAL_PIO_FUNC_GPIO); + uapi_gpio_set_dir(GPIO_PIN_PRG_LED_ON, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_val(GPIO_PIN_PRG_LED_ON, GPIO_LEVEL_LOW); + LOS_Mdelay(1); +#endif +} int mmc5603_start(void) { + + mmc5603_poweron(); + int i; /* magnetic sensor soft iron calibration matrix, default is unit matrix */ float mag_smm[9] = { 1.0f, 0.0f, 0.0f, \ @@ -443,5 +474,8 @@ int mag_service_get_iAccuracy(void) int mmc5603_stop(void) { CUSTOM_MMC5603_Disable(); + + mmc5603_poweroff(); + return 0; } diff --git a/code/sdk/zhihui/drivers/mag/mmc5603/mmc_customer.h b/code/sdk/zhihui/drivers/mag/mmc5603/mmc_customer.h index a5f8bdedd..a08c83996 100644 --- a/code/sdk/zhihui/drivers/mag/mmc5603/mmc_customer.h +++ b/code/sdk/zhihui/drivers/mag/mmc5603/mmc_customer.h @@ -1,3 +1,11 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-16 09:56:44 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-16 10:33:24 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/mag/mmc5603/mmc_customer.h + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ #ifndef __MMC5603_PORTING_H__ #define __MMC5603_PORTING_H__ #include @@ -6,7 +14,7 @@ #ifndef XTS_SUPPORT -// #define MMC_DEBUG +#define MMC_DEBUG #endif #ifdef MMC_DEBUG diff --git a/code/sdk/zhihui/drivers/mag/service/mag_service.c b/code/sdk/zhihui/drivers/mag/service/mag_service.c index bab12f639..f0fce98f5 100644 --- a/code/sdk/zhihui/drivers/mag/service/mag_service.c +++ b/code/sdk/zhihui/drivers/mag/service/mag_service.c @@ -71,7 +71,13 @@ static bool g_batter_service_initialled = false; #include "mmc_customer.h" #include "custom_utils.h" -#include "custom_runtime.h" + +#ifdef BOARD_U8 +#include "vc9202_hal.h" +#endif + + +// #include "custom_runtime.h" /*** * 其他cmd在Header里定义,对外开放 */ @@ -162,17 +168,18 @@ static void mag_service_check_task(void const *arg) // MMC_LOG("MAG_SERVICE_CMD_TIMER_CHECK\r\n"); mmc5603_check(); }else if (mail.comm.app == MAG_SERVICE_CMD_START_CHECK){ - uapi_dev_runtime_set_mmc_status(1); - do_custom_fileoperate_service_check_msg(CUSTOM_FILEOPERATE_MSG_TYPE_MMCDATA_READ, NULL, 0); + // uapi_dev_runtime_set_mmc_status(1); + // do_custom_fileoperate_service_check_msg(CUSTOM_FILEOPERATE_MSG_TYPE_MMCDATA_READ, NULL, 0); + mmc5603_start(); LOS_Mdelay(20); mag_service_running_stat = true; // uapi_pm_add_sleep_veto(PM_ID_SCREEN); mag_service_timer_start_check(MAG_CHECK_MS); }else if (mail.comm.app == MAG_SERVICE_CMD_STOP_CHECK){ - uapi_dev_runtime_set_mmc_status(0); + // uapi_dev_runtime_set_mmc_status(0); // SavePara_infile(); - do_custom_fileoperate_service_check_msg(CUSTOM_FILEOPERATE_MSG_TYPE_MMCDATA_SAVE, NULL, 0); + // do_custom_fileoperate_service_check_msg(CUSTOM_FILEOPERATE_MSG_TYPE_MMCDATA_SAVE, NULL, 0); mag_service_running_stat = false; osTimerStop(g_mag_service_timer); mmc5603_stop(); @@ -200,21 +207,28 @@ errcode_t uapi_mag_service_init(void) PRINT("uapi_mag_service_init !\r\n"); - - +#ifdef BOARD_U8 + mmc5603_poweron(); +#endif if(CUSTOM_MMC5603_CheckID()!=1) { //TODO 设置磁力计检测是否存在标识 PRINT("MMC5603 not found!\r\n"); - return ERRCODE_FAIL; +#ifdef BOARD_U8 + mmc5603_poweroff(); +#endif + // return ERRCODE_FAIL; } mmc5603_stop(); +#ifdef BOARD_U8 + mmc5603_poweroff(); +#endif g_mag_sensor_exist = true; app_task_definition_t taskcfg={0}; taskcfg.create_queue_handle = create_mag_queue; taskcfg.attr.name="mag_service"; - taskcfg.attr.priority = TASK_PRIORITY_APP; + taskcfg.attr.priority = osPriorityBelowNormal4; taskcfg.attr.stack_size = MAG_BOARD_TASK_SIZE; taskcfg.func = mag_service_check_task; taskcfg.use_mem = USE_EXTEND_MEM; diff --git a/code/sdk/zhihui/drivers/mag/service/mag_service.h b/code/sdk/zhihui/drivers/mag/service/mag_service.h index 62bbe0e2d..951ffeef9 100644 --- a/code/sdk/zhihui/drivers/mag/service/mag_service.h +++ b/code/sdk/zhihui/drivers/mag/service/mag_service.h @@ -1,3 +1,11 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-16 09:56:44 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-18 17:23:06 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/mag/service/mag_service.h + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ /** * Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved. \n * diff --git a/code/sdk/zhihui/drivers/ppg/service/CMakeLists.txt b/code/sdk/zhihui/drivers/ppg/service/CMakeLists.txt index bed307cc1..8315e769d 100644 --- a/code/sdk/zhihui/drivers/ppg/service/CMakeLists.txt +++ b/code/sdk/zhihui/drivers/ppg/service/CMakeLists.txt @@ -17,6 +17,7 @@ set(PRIVATE_HEADER set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ppg_at_cmd.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ppg_service.cpp + ) set(COMPONENT_PUBLIC_CCFLAGS diff --git a/code/sdk/zhihui/drivers/ppg/service/ppg_at_cmd.cpp b/code/sdk/zhihui/drivers/ppg/service/ppg_at_cmd.cpp index afb40077b..d86729501 100644 --- a/code/sdk/zhihui/drivers/ppg/service/ppg_at_cmd.cpp +++ b/code/sdk/zhihui/drivers/ppg/service/ppg_at_cmd.cpp @@ -10,6 +10,11 @@ #include #include +#ifdef PPG_VC9202 +#include "vc9202_hal.h" +#include "vc9202_common.h" +#endif + #define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0])) #define PPG_MAX_ARGC 8 #define PPG_MAX_ARGV_LEN 64 @@ -77,7 +82,10 @@ static uint32_t ppg_do_test(uint8_t *para, uint32_t paraLen, char *strCommonCmd) md = PPG_SERVICE_CMD_TYPE_DO_LEAKLIGHT_TEST; } else if (strcmp(mode, "graycard") == 0) { md = PPG_SERVICE_CMD_TYPE_DO_GRAYCARD_TEST; + } else if (strcmp(mode, "fpc") == 0) { + md = PPG_SERVICE_CMD_TYPE_FPC_TEST; } + if(enabled == 0 ) { if (md == PPG_SERVICE_CMD_TYPE_DO_SPO2_TEST) diff --git a/code/sdk/zhihui/drivers/ppg/service/ppg_service.cpp b/code/sdk/zhihui/drivers/ppg/service/ppg_service.cpp index 2c801e8f2..5374055fe 100644 --- a/code/sdk/zhihui/drivers/ppg/service/ppg_service.cpp +++ b/code/sdk/zhihui/drivers/ppg/service/ppg_service.cpp @@ -42,18 +42,42 @@ extern "C" { #include "ppg_service.h" #include "los_printf.h" +#include "thread_init.h" +#include "custom_utils.h" + + +#ifdef PPG_VC9202 +#include "vc9202_hal.h" +#include "vc9202_user.h" +#endif +#ifdef PPG_HX3695 #include "hx3695_hal.h" -#include "custom_utils.h" -#include "cywee_hal.h" -#include "cywee_service.h" -#include "custom_runtime.h" -#include "custom_save_runtime.h" +#endif + +#ifdef PPG_VC921X +#include "vc921x_hal.h" +#include "vc921x_user_app.h" +#endif static bool g_ppg_service_initialled = false; uint8_t card_databuf[64] = {0}; uint8_t light_databuf[64] = {0}; + +osTimerId_t g_ppg_service_timer = NULL; + +typedef uint32_t PPG_CMD_TYPE_T ; + +typedef enum{ + HX3695_MSG_TYPE_HRS_UPDATE, + HX3695_MSG_TYPE_HRV_UPDATE, + HX3695_MSG_TYPE_SPO2_UPDATE, + HX3695_MSG_TYPE_LEAKLIGHT_UPDATE, + HX3695_MSG_TYPE_GRAYCARD_UPDATE, + HX3695_MSG_TYPE_WEARSTAT_UPDATE, + HX3695_MSG_TYPE_STATIC_HRS_UPDATE, +} hx3695_msg_type_t; /*** * 其他cmd在Header里定义,对外开放 */ @@ -70,7 +94,7 @@ static bool ppg_service_running_stat = false; static bool ppg_sensor_exist = false; static uint8_t wear_mode_state = 0; -static uint8_t keep_in_wear_det_mod = 1; //持续坚持 佩戴检测模式 +static uint8_t keep_in_wear_det_mod = 0; //持续坚持 佩戴检测模式 void set_keep_in_wear_det_mode(uint8_t mod) @@ -78,6 +102,7 @@ void set_keep_in_wear_det_mode(uint8_t mod) keep_in_wear_det_mod = mod; } +#if 0 //漏光测试 errcode_t update_leaklight_event_notify(uint8_t *data) { @@ -220,6 +245,8 @@ void hx3695_msg_callback(uint8_t msg_type, uint8_t *msg_data, int32_t msg_datale } } +#endif + void uapi_ppg_service_deinit(void) { ppg_service_running_stat = false; @@ -238,8 +265,8 @@ uint32_t ppg_resume(void) void send_ppg_cmd_msg(int cmd) { int ret; - if (ppg_sensor_exist == false) - return ; + // if (ppg_sensor_exist == false) + // return ; PPG_CMD_TYPE_T mail = 0; mail = cmd; @@ -266,21 +293,20 @@ static void ppg_service_check_task(void *arg) } if((wear_mode_state == 1) && (mail > 0x10) && mail != PPG_SERVICE_CMD_TYPE_STOP_WEAR_MODE_TEST) { - uapi_save_runtime_log(RUNTIME_LOG_WEAR_MIN_STOP); wear_mode_state = 0; - ppg_send_cmd_to_driver("wear",0); + // ppg_send_cmd_to_driver("wear",0); ppg_service_running_stat = false; } char* cmd = "hrs"; int enable = 1; if (mail == PPG_SERVICE_CMD_TYPE_DO_PPG_320TIMER_CALLBACK){ - heart_rate_meas_timeout_handler(NULL); + // heart_rate_meas_timeout_handler(NULL); continue; }else if (mail == PPG_SERVICE_CMD_TYPE_DO_PPG_40TIMER_CALLBACK){ - agc_timeout_handler(NULL); + // agc_timeout_handler(NULL); continue; }else if(mail == PPG_SERVICE_CMD_TYPE_DO_WEAR_MODE_5000TIMER_CALLBACK){ - wear_mode_timeout_handler(NULL); + // wear_mode_timeout_handler(NULL); continue; }else if (mail == PPG_SERVICE_CMD_TYPE_DO_PPG_TEST){ //do_cywee_service_check_msg(CYWEE_SERVICE_CMD_OPEN_GET_ACC,NULL); @@ -291,11 +317,16 @@ static void ppg_service_check_task(void *arg) { uapi_pm_add_sleep_veto(PM_ID_HEART); } - + // osTimerStart(g_ppg_service_timer,400); }else if (mail == PPG_SERVICE_CMD_TYPE_DO_HRV_TEST){ cmd = "hrv"; enable = 1; }else if (mail == PPG_SERVICE_CMD_TYPE_DO_SPO2_TEST){ + uint8_t cnt = pm_veto_get_id_cnt(PM_ID_HEART); + if (cnt == 0) + { + uapi_pm_add_sleep_veto(PM_ID_HEART); + } cmd = "spo2"; enable = 1; }else if (mail == PPG_SERVICE_CMD_TYPE_DO_LEAKLIGHT_TEST){ @@ -306,7 +337,6 @@ static void ppg_service_check_task(void *arg) enable = 1; }else if (mail == PPG_SERVICE_CMD_TYPE_DO_WEAR_MODE_TEST){ if (keep_in_wear_det_mod == 0) continue;; - uapi_save_runtime_log(RUNTIME_LOG_WEAR_MIN_START); cmd = "wear"; enable = 1; wear_mode_state = 1; @@ -329,50 +359,74 @@ static void ppg_service_check_task(void *arg) { uapi_pm_remove_sleep_veto(PM_ID_HEART); } + // osTimerStop(g_ppg_service_timer); }else if (mail == PPG_SERVICE_CMD_TYPE_STOP_SPO2_TEST){ - + uint8_t cnt = pm_veto_get_id_cnt(PM_ID_HEART); + if (cnt == 1) + { + uapi_pm_remove_sleep_veto(PM_ID_HEART); + } cmd = "spo2"; enable = 0; } else if (mail == PPG_SERVICE_CMD_TYPE_OPEN_HRS_BLE) { - hx3695_set_hrs_ble_swtich(1); + // hx3695_set_hrs_ble_swtich(1); continue ; } else if (mail == PPG_SERVICE_CMD_TYPE_CLOSE_HRS_BLE) { - hx3695_set_hrs_ble_swtich(0); + // hx3695_set_hrs_ble_swtich(0); continue ; + }else if (mail == PPG_SERVICE_CMD_TYPE_INT_HANDLER){ +#ifdef PPG_VC9202 + vc9202_usr_interrupt_handler(SPORT_TYPE_NORMAL,0); +#elif defined PPG_VC921X + cmd = "int"; + enable = 1; + ppg_send_cmd_to_vc921x_driver(cmd,enable); +#endif + continue; } - if (ppg_service_running_stat == true && enable ==1 ) - { - PRINT_ERR("ppg service already started! \r\n"); - continue ; - }else if ( ppg_service_running_stat == false && enable == 0 ) - { - PRINT_ERR("ppg service already stopped! \r\n"); - continue ; + else if (mail == PPG_SERVICE_CMD_TYPE_FPC_TEST){ +#ifdef PPG_VC9202 + cmd = "fpc"; + enable = 1; +#endif } -#ifdef PPG_HX3695 - ppg_send_cmd_to_driver(cmd,enable); + // if (ppg_service_running_stat == true && enable ==1 ) + // { + // PRINT_ERR("ppg service already started! \r\n"); + // continue ; + // }else if ( ppg_service_running_stat == false && enable == 0 ) + // { + // PRINT_ERR("ppg service already stopped! \r\n"); + // continue ; + // } + +#ifdef PPG_VC9202 + ppg_send_cmd_to_vc9202_driver(cmd,enable); +#elif defined PPG_VC921X + ppg_send_cmd_to_vc921x_driver(cmd,enable); +#endif + if(enable == 1) ppg_service_running_stat = true; else ppg_service_running_stat = false; + if((strcmp(cmd, "wear") != 0) && (enable == 0)) { if (keep_in_wear_det_mod == 1) { - uapi_save_runtime_log(RUNTIME_LOG_WEAR_MIN_START); - ppg_send_cmd_to_driver("wear",1); + //ppg_send_cmd_to_driver("wear",1); wear_mode_state = 1; ppg_service_running_stat = true; } } -#endif - } + } void create_ppg_queue(void) { @@ -387,10 +441,10 @@ errcode_t uapi_ppg_service_init(void) PRINT("uapi_ppg_service_init !\r\n"); - + tyhx_poweron(); #ifdef PPG_HX3695 - tyhx_poweron(); + if ( hx3695_chip_check() == false) { //TODO 没找到3695,退出线程 @@ -398,18 +452,26 @@ errcode_t uapi_ppg_service_init(void) tyhx_poweroff(); return ERRCODE_FAIL; } - tyhx_poweroff(); hx3695_register_msg_callback(hx3695_msg_callback);//注册驱动回调 - // ppg_send_cmd_to_driver("wear",0); - - ppg_sensor_exist = true; + // //ppg_send_cmd_to_driver("wear",0); +#elif defined PPG_VC9202 + if ( vc9202_drv_get_chip_id() != 0x21) + { + //TODO 没找到3695,退出线程 + PRINT("PPG_VC9202 not exist!\r\n"); + tyhx_poweroff(); + return ERRCODE_FAIL; + } #endif + tyhx_poweroff(); + + ppg_sensor_exist = true; app_task_definition_t taskcfg={0}; taskcfg.create_queue_handle = create_ppg_queue; taskcfg.attr.name="ppg_service"; - taskcfg.attr.priority = osPriorityBelowNormal4; + taskcfg.attr.priority = osPriorityHigh; taskcfg.attr.stack_size = PPG_SERVICE_TASK_SIZE; taskcfg.func = ppg_service_check_task; taskcfg.use_mem = USE_EXTEND_MEM; @@ -421,9 +483,20 @@ errcode_t uapi_ppg_service_init(void) return ERRCODE_FAIL; } + + // g_ppg_service_timer = osTimerNew((osTimerFunc_t)ppg_int_callback, osTimerPeriodic, NULL, NULL); + return ERRCODE_SUCC; } +void ppg_int_callback(pin_t pin, uintptr_t param) +{ + uapi_gpio_clear_interrupt(pin); + uint32_t tm; + uapi_get_timestampe_ms(&tm); + PRINT("ppg_int_callback %d 0x%X tm : %d \n",pin,param,tm); + send_ppg_cmd_msg(PPG_SERVICE_CMD_TYPE_INT_HANDLER); +} // void ppg_service_service_resume(void) // { // DEBUG_PRINTF("ppg_service_service resume!\r\n"); diff --git a/code/sdk/zhihui/drivers/ppg/service/ppg_service.h b/code/sdk/zhihui/drivers/ppg/service/ppg_service.h index 9e2f8ff80..67682fdf8 100644 --- a/code/sdk/zhihui/drivers/ppg/service/ppg_service.h +++ b/code/sdk/zhihui/drivers/ppg/service/ppg_service.h @@ -2,7 +2,7 @@ * @Author: penglei 1624952103@qq.com * @Date: 2024-03-27 09:07:00 * @LastEditors: penglei 1624952103@qq.com - * @LastEditTime: 2024-11-23 22:07:09 + * @LastEditTime: 2024-12-10 17:11:28 * @FilePath: /FD230/code/sdk/drivers/peripheral/ppg/service/ppg_service.h * @Description: * @@ -28,6 +28,7 @@ extern "C" { #include "stdint.h" #include "errcode.h" +#include "platform_core.h" #define PPG_SERVICE_CMD_TYPE_DO_WEAR_MODE_5000TIMER_CALLBACK 0x09 #define PPG_SERVICE_CMD_TYPE_DO_PPG_40TIMER_CALLBACK 0x0A @@ -45,6 +46,8 @@ extern "C" { #define PPG_SERVICE_CMD_TYPE_DO_SINGLE_CHECK_MODE_TEST 0x23 #define PPG_SERVICE_CMD_TYPE_OPEN_HRS_BLE 0x24 #define PPG_SERVICE_CMD_TYPE_CLOSE_HRS_BLE 0x25 +#define PPG_SERVICE_CMD_TYPE_INT_HANDLER 0x26 +#define PPG_SERVICE_CMD_TYPE_FPC_TEST 0x27 errcode_t uapi_ppg_service_init(void); void uapi_ppg_service_deinit(void); @@ -89,6 +92,8 @@ typedef struct { //设置佩戴检测模式 void set_keep_in_wear_det_mode(uint8_t mod); +void ppg_int_callback(pin_t pin, uintptr_t param); + #ifdef __cplusplus #if __cplusplus } diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/CMakeLists.txt b/code/sdk/zhihui/drivers/ppg/vc9202/CMakeLists.txt new file mode 100644 index 000000000..0bf14bb6e --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/CMakeLists.txt @@ -0,0 +1,63 @@ +#=============================================================================== +# @brief cmake file +# Copyright (c) @CompanyNameMagicTag 2022-2023. All rights reserved. +#=============================================================================== +set(COMPONENT_NAME "ppg_sensor_vc9202") + +if("PPG_VC9202" IN_LIST DEFINES) +set(SOURCES ${SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/src/vc9202_driver_testmode.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/vc9202_driver.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/vc9202_user_debug.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/vc9202_user.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/vc9202_hal.c +) +set(PUBLIC_HEADER + ${CMAKE_CURRENT_SOURCE_DIR}/inc + +) + +else() +set(SOURCES ${SOURCES} + # ${CMAKE_CURRENT_SOURCE_DIR}/ztw523_touch/ztw523_ctrl.c +) +set(PUBLIC_HEADER + ${CMAKE_CURRENT_SOURCE_DIR} +) +endif() + + +set(PRIVATE_HEADER +) + +set(PRIVATE_DEFINES +) + +set(PUBLIC_DEFINES +) + +# use this when you want to add ccflags like -include xxx +set(COMPONENT_PUBLIC_CCFLAGS +) + +set(COMPONENT_CCFLAGS +) + +set(WHOLE_LINK + true +) + +set(MAIN_COMPONENT + false +) + +if("PPG_VC9202" IN_LIST DEFINES) + find_library(LIB_PATH_VC9202_1 lib/lib_BIO_v151_int_miniSDK_log_02.a ${CMAKE_CURRENT_SOURCE_DIR}) + find_library(LIB_PATH_VC9202_2 lib/lib_hr_v640ed04_miniSDK_NA_01.a ${CMAKE_CURRENT_SOURCE_DIR}) + find_library(LIB_PATH_VC9202_3 lib/lib_Sp_Int_HaiSi_01.a ${CMAKE_CURRENT_SOURCE_DIR}) + find_library(LIB_PATH_VC9202_4 lib/lib_spo2_9202A4_limit_HaiSi_NA_01.a ${CMAKE_CURRENT_SOURCE_DIR}) + + target_link_libraries(${TARGET_NAME} PRIVATE -Wl,--whole-archive ${LIB_PATH_VC9202_1} ${LIB_PATH_VC9202_2} ${LIB_PATH_VC9202_3} ${LIB_PATH_VC9202_4} PRIVATE -Wl,--whole-archive) +endif() +build_component() + diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/inc/BIODetect_V15_1.h b/code/sdk/zhihui/drivers/ppg/vc9202/inc/BIODetect_V15_1.h new file mode 100644 index 000000000..d03b0b1dd --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/inc/BIODetect_V15_1.h @@ -0,0 +1,16 @@ + + +#ifndef BIODETECT +#define BIODETECT + +#include +#include +#include + +void f_BIOInit(int32_t gapDeltaBIO, int32_t gapDeltaPS); + +uint8_t f_BIOdetect(int32_t inputBIO, int32_t inputPS, int32_t PSth); + +char* bioVersion(void); + +#endif /* BIODETECT */ \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/inc/algo.h b/code/sdk/zhihui/drivers/ppg/vc9202/inc/algo.h new file mode 100644 index 000000000..a370e99ff --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/inc/algo.h @@ -0,0 +1,70 @@ + +#ifndef APP_ALGO_ALGO_H +#define APP_ALGO_ALGO_H + +#include +#include + +typedef enum +{ + SPORT_TYPE_NORMAL =0x00, // 日常 + SPORT_TYPE_RUNNING =0x01, // 跑步 + SPORT_TYPE_RIDE_BIKE =0X02, // 骑行 + SPORT_TYPE_JUMP_ROPE =0X03, // 跳绳 + SPORT_TYPE_SWIMMING =0X04, // 游泳 + SPORT_TYPE_BADMINTON =0X05, // 羽毛球 + SPORT_TYPE_TABLE_TENNIS=0X06, // 乒乓球 + SPORT_TYPE_TENNIS =0X07, // 网球 + SPORT_TYPE_CLIMBING =0X08, // 爬山 + SPORT_TYPE_WALKING =0X09, // 椭圆机 + SPORT_TYPE_BASKETBALL =0X0A, // 篮球 + SPORT_TYPE_FOOTBALL =0X0B, // 足球 + SPORT_TYPE_BASEBALL =0X0C, // 棒球 + SPORT_TYPE_VOLLEYBALL =0X0D, // 排球 + SPORT_TYPE_CRICKET = 0X0E, // 板球 + SPORT_TYPE_RUGBY = 0X0F, // 橄榄球 + SPORT_TYPE_HOCKEY =0X10, // 曲棍球 + SPORT_TYPE_DANCE = 0X11, // 跳舞 + SPORT_TYPE_SPINNING = 0X12, // 动感单车 + SPORT_TYPE_YOGA = 0X13, // 瑜伽 + SPORT_TYPE_SIT_UP =0X14, // 仰卧起坐 + SPORT_TYPE_TREADMILL =0X15, // 跑步机 + SPORT_TYPE_GYMNASTICS =0X16, // 体操 + SPORT_TYPE_BOATING = 0X17, // 划船 + SPORT_TYPE_JUMPING_JACK = 0X18, // 开合跳 + SPORT_TYPE_FREE_TRAINING = 0X19,// 自由训练 + SPORT_TYPE_PERIODIC_MONITORING = 0X1A,// 定时监测模式 +} AlgoSportMode; +//未出现在上表中的运动形式均先以跑步输入 + + +typedef struct +{ + int32_t x; + int32_t y; + int32_t z; +}AlgoAxesData_t; + +typedef struct +{ + int32_t hrData; + int32_t reliability; +}AlgoOutputData_t; + +typedef struct +{ + AlgoAxesData_t axes; + int32_t ppgSample; + int32_t envSample; +}AlgoInputData_t; + +void Algo_Init(void); +void Algo_Input(AlgoInputData_t *pInputData,int32_t gaptime,AlgoSportMode sportMode,int16_t surfaceRecogMode,int16_t opticalAidMode); +void Algo_Output(AlgoOutputData_t *pOutputData); + + + +#endif /* APP_ALGO_ALGO_H_ */ + + + diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/inc/spo2Algo.h b/code/sdk/zhihui/drivers/ppg/vc9202/inc/spo2Algo.h new file mode 100644 index 000000000..351643ae3 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/inc/spo2Algo.h @@ -0,0 +1,14 @@ +#ifndef __SPO2ALGO_H__ +#define __SPO2ALGO_H__ + +#include + +void spo2AlgoInit(void); +int32_t spo2Algo(int32_t r_ppg, int32_t ir_ppg, int32_t mode); + +void spo2_algo_content_init(void); + +char * +version_info_spo2(void); + +#endif diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc30x91x92x_common.h b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc30x91x92x_common.h new file mode 100644 index 000000000..36badd54a --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc30x91x92x_common.h @@ -0,0 +1,87 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-07 16:04:22 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-09 10:32:42 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/ppg/vc9202/vc30x91x92x_common.h + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +/********************************************************************************************************* + * Copyright(c) 2022, vcare Corporation. All rights reserved. + ********************************************************************************************************** + * @file vc30x91x92x_common.h + * @brief configuration file for vc30x,vc91x,vc92x. + * @details + * @author + * @date + * @version + *********************************************************************************************************/ +#ifndef __VC30x91x92x_COMMON_H__ +#define __VC30x91x92x_COMMON_H__ + +#include "algo.h" +#include "spo2Algo.h" +#include "vcSportMotionIntAlgo.h" + +#ifndef SUB +#define SUB(a, b) ((a) - (b)) +#endif +#ifndef ABS +#define ABS(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) +#endif + +#define FUNC_DISABLE (0) +#define FUNC_ENABLE (!FUNC_DISABLE) +#define WEAR_IS_DROP (0) +#define WEAR_IS_HOLD (1) + +typedef enum +{ + WEAR_STA_NONE = 0, + WEAR_STA_HOLD = 1, + WEAR_STA_DROP = 2, +}WearStatusTypeDef; + +typedef enum +{ + WORK_MODE_NONE = 0, + WORK_MODE_HR = 1, + WORK_MODE_SPO2 = 2, + WORK_MODE_HRV = 3, /* 100hz������ʹ��BIO������Ҫ��֤�ܹ��õ���ʱ��Ӧ��������Ȼ���ܻᵼ�¶��жϣ����������� */ + WORK_MODE_STRESS = 4, + WORK_MODE_WEAR = 5, + WORK_MODE_FACTORY = 6, + WORK_MODE_TEMPERATURE = 7, + WORK_MODE_FPC_TEST = 8, +}WorkModeTypeDef; + +typedef struct +{ + unsigned short sampling_gap_time; + WorkModeTypeDef work_mode; +}InitParamTypeDef; + +typedef struct +{ + unsigned char led_cur[3]; + unsigned char pd_res[3]; + unsigned char env_val[3]; + unsigned char pre_val[2]; + unsigned char ps_val; + unsigned char aenv_val[3]; + unsigned char apdbias_val[4]; + unsigned int pdbias; + unsigned char apre_val[2]; + unsigned char aps_val; + unsigned int ppg_data[128]; + unsigned short int bio_val_i; + unsigned short int bio_val_o; + signed short int temp_val_i; + signed short int temp_val_o; +}SamplingDataTypeDef; + +extern unsigned char usr_iic_write_bytes(unsigned char reg_addr, unsigned char *pBuffer, unsigned char buf_size); +extern unsigned char usr_iic_read_bytes(unsigned char reg_addr, unsigned char *pBuffer, unsigned char buf_size); +extern unsigned int usr_get_clock_ticks(void); + +#endif diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_common.h b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_common.h new file mode 100644 index 000000000..64d5ff998 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_common.h @@ -0,0 +1,77 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-07 16:04:22 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-10 18:40:24 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/ppg/vc9202/inc/vc9202_common.h + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +/********************************************************************************************************* + * Copyright(c) 2022, vcare Corporation. All rights reserved. + ********************************************************************************************************** + * @file vc9202_common.h + * @brief configuration file for the hardware driver layer + * @details + * @author + * @date + * @version + *********************************************************************************************************/ +#ifndef __VC9202_COMMON_H__ +#define __VC9202_COMMON_H__ + +#include "vc30x91x92x_common.h" + +#define VC9202_CFG_DRV_BY_TIMER (FUNC_DISABLE) +/* VC_COMMON_VC9202_CFG_SAMPLE_RATE: default 25hz */ +#define VC9202_CFG_SAMPLE_RATE (25) +/*VC_COMMON_CFG_GPIO_TIME: 0-50us, 1-100us, 2-200us, 3-400us */ +#define VC9202_CFG_INTERRUPT_TIME (3) +/* 0:rising edge 1:descending edge */ +#define VC9202_CFG_INTERRUPT_MODE (0) + +#define VC9202_CFG_CLK_CHECK_FREQUENCY (32000) //counter 32K +#define VC9202_CFG_CLK_CHECK_MAX (0xFFFFFF) //24bits counter +#define VC9202_CFG_CLK_CHECK_GAPTIME (10) //seconds +#define VC9202_CFG_CLK_CHECK_DEVIATION (3) + +/**************************************************************************** + * Configuration wear parameters + ***************************************************************************/ +#define VC9202_CFG_WEAR_DETECTION (FUNC_ENABLE) // FUNC_DISABLE FUNC_ENABLE +#define VC9202_CFG_WEAR_DEFAULT_STATUS (WEAR_IS_HOLD) //1 is default wear,0 is default not wear WEAR_IS_HOLD WEAR_IS_DROP +#define VC9202_CFG_WEAR_BIO_EN (FUNC_ENABLE) +#define VC9202_CFG_WEAR_ALG_EN (FUNC_DISABLE) //开启心率摘下功能,必须开启满足(ATC|BIO)为真 +#define VC9202_CFG_WEAR_ACT_EN (FUNC_DISABLE) +#define VC9202_CFG_WEAR_THRESHOLD_EN (FUNC_ENABLE) //绝对阈值宏开关 +#define VC9202_CFG_WEAR_HOLD_CNT (1) +#define VC9202_CFG_WEAR_DROP_CNT (3) + +/* sensor serial default wear param */ +#define VC9202_CFG_WEAR_PARAM_PS (128) +#define VC9202_CFG_WEAR_PARAM_PSIN (100) +#define VC9202_CFG_WEAR_PARAM_ENVMAX (128) //8*16 +#define VC9202_CFG_WEAR_PARAM_ENVMIN (64) //4*16 +#define VC9202_CFG_WEAR_PARAM_BIOIN (40) + +#define VC9202_CFG_PS_SLOT_CUR (0x3A) /* 0x5A */ +#define VC9202_CFG_PS_SLOT_RES (0x47) + +/* board cap halve,电容减半,需要提高一倍采样率的配置 */ +#define VC9202_CFG_IS_HALVE_CAP 0 + +/* VC9202 function */ +extern unsigned char vc9202_drv_reg_init(InitParamTypeDef *pInit_Param); +extern unsigned char vc9202_drv_get_chip_id(void); +extern void vc9202_drv_start_sampling(void); +extern void vc9202_drv_stop_sampling(void); +extern void vc9202_drv_soft_reset(void); +extern unsigned char vc9202_drv_channel_switch(unsigned char num_channel, unsigned char enable_flag); +extern unsigned char vc9202_drv_get_int_reason(void); +extern void vc9202_drv_set_algo_wear_status(WearStatusTypeDef status); +extern void vc9202_drv_set_gsensor_actuating_quantity(unsigned int axis_diff); +extern WearStatusTypeDef vc9202_drv_get_wear_status(void); +extern unsigned char vc9202_drv_get_sampling_data(SamplingDataTypeDef *pSamplingData, unsigned char *psize); +/* electrostatic patch */ +extern void vc9202_drv_electrostatic_death( void ); + +#endif diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_driver.h b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_driver.h new file mode 100644 index 000000000..047534aaa --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_driver.h @@ -0,0 +1,184 @@ +/********************************************************************************************************* + * Copyright(c) 2022, vcare Corporation. All rights reserved. + ********************************************************************************************************* + * @file vc9202_driver.h + * @brief vc9202 driver layer interface implementation. + * @details + * @author + * @date + * @version + *********************************************************************************************************/ + +#ifndef __VC9202_DRIVER_H__ +#define __VC9202_DRIVER_H__ + +#include "vc9202_common.h" + +#define VC9202_STATUS_CONFLICT (1<<4) +#define VC9202_STATUS_INSAMPLE (1<<3) +#define VC9202_STATUS_OVERLOAD2 (1<<2) +#define VC9202_STATUS_OVERLOAD1 (1<<1) +#define VC9202_STATUS_OVERLOAD0 (1<<0) + +#define VC9202_EVENT_TEMP_MARK (1<<6) //128 +#define VC9202_EVENT_BIO_MARK (1<<5) //64 +#define VC9202_EVENT_PS_MARK (1<<4) //32 +#define VC9202_EVENT_OV_MARK (1<<3) //8 +#define VC9202_EVENT_FIFO_MARK (1<<2) //4 +#define VC9202_EVENT_ENV_MARK (1<<1) //2 +#define VC9202_EVENT_PPG_MARK (1<<0) //1 + +#define VC9202_MAX_RES_LEVEL 17 //21 9202offset只有0-2,3和2一样、所以表中只有17个等效 +#define VC9202_MAX_CUR_LEVEL 0x6F + +#define VC9202_ENV_MAX_TH 8*16 + +/* adjust marco */ +#define VC9202_PPG_TH_CELL 10 // Causes of PPG interruption PPG_TH = 300 +#define VC9202_ADJUST_INCREASE 22 // 1.4 << 4 = 22.4//1.4f +#define VC9202_ADJUST_DECREASE 11 // 0.7 << 4 = 11.2//0.7f +#define VC9202_ADJUST_STEP_MAX 32 +#define VC9202_ADJUST_STEP_MIN 1 +#define VC9202_SLOTCHNU 3 + +/* bio算法运算结果宏定义 */ +#define VC9202_BIO_STA_NONE 0 +#define VC9202_BIO_STA_DROP 1 +#define VC9202_BIO_STA_HOLD 2 + +/**************************************************************************** + * driver ret enum + ***************************************************************************/ +typedef enum +{ + RET_OK = 0, + RET_STATUS_ERR = (1<<0), + RET_OVERLOAD_ERR = (1<<1), + RET_ENV_ERR = (1<<2), + RET_PPG_ERR = (1<<3), + RET_OSC_ERR = (1<<4), + RET_FIFO_ERR = (1<<5), +}VC9202DrvRetTypeDef; + +/**************************************************************************** + * clock calibration_struct + ***************************************************************************/ +typedef struct +{ + unsigned int last_clk_count; + unsigned int osc_gap_cnt; + unsigned char count_index; + unsigned int osc_count[6]; + unsigned int clk_count[6]; + unsigned int last_clk_check; /* 2023.06.05,check running gaptime */ +}VC9202OscClkTypeDef; + +/**************************************************************************** + * WearStatus_struct + ***************************************************************************/ +typedef struct +{ + unsigned char wear_ps; + unsigned char wear_env; + short int wear_bio; + unsigned char wear_hold_cnt; + unsigned char wear_drop_cnt; + WearStatusTypeDef wear_status; + WearStatusTypeDef last_status; +}VC9202WearInfoTypeDef; + +typedef enum +{ + WEAR_NO_CHANGE = 0, + DROP_TO_HOLD = 1, + HOLD_TO_DROP = 2, +}VC9202WearChangeTypeDef; + +/**************************************************************************** + * ADJUST_struct + ***************************************************************************/ +typedef struct +{ + unsigned char res; + unsigned char offset; + unsigned char low; +}VC9202ResTableTypeDef; + +typedef struct +{ + unsigned char res_val; + unsigned char low_res; + unsigned char offset; + unsigned char offset3; +}VC9202ResLevelTypeDef; + +typedef enum +{ + AGC_DIR_NULL = 0, + AGC_DIR_UP = 1, + AGC_DIR_DOWN = 2, +}VC9202AgcDirTypeDef; + +typedef struct +{ + VC9202AgcDirTypeDef dir; + VC9202AgcDirTypeDef last_dir; + unsigned char current_step; +}VC9202AgcInfoTypeDef; + +typedef struct +{ + unsigned char max_current; + unsigned char max_res_level; + unsigned char record_env; + unsigned char slot_env; + unsigned char current; + unsigned char res_level; + VC9202AgcInfoTypeDef info; +}VC9202AdjustSlotTypeDef; + +/**************************************************************************** + * Dev_struct + ***************************************************************************/ +typedef struct +{ + unsigned short fifo_time; + unsigned char work_frequency; + unsigned char enable_bio; + WorkModeTypeDef work_mode; + + unsigned char work_status; + unsigned int int_event; + + WearStatusTypeDef wear_status; + unsigned char osc_clk_status; + unsigned char enfifo_div; + unsigned char enslot[2]; + unsigned char slot_number; + unsigned char read_fifo_address; + +}VC9202DrvInfoTypeDef; + +/**************************************************************************** + * test_struct + ***************************************************************************/ +typedef struct +{ + unsigned char wait_cnt; + unsigned char step1_ret; // step1_ret/step2_ret/step3_ret:0-wait test,1-pass,2-failed + unsigned char icid; + unsigned char reason; + unsigned char leak_pre_value; + unsigned char max_current; + signed short temperature; + unsigned int frequency; + + unsigned char step2_ret; + unsigned char ps_value; + unsigned short biovalue_max; + unsigned short biovalue_now[2]; + unsigned short biovalue_gap[2]; +} VC9202TestInfoTypeDef; + + +#endif diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_hal.h b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_hal.h new file mode 100644 index 000000000..44f0a2f8f --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_hal.h @@ -0,0 +1,56 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-06-07 11:18:26 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-21 17:20:43 + * @FilePath: /FD230/code/sdk/drivers/peripheral/ppg/hx3695/hx3695_hal.h + * @Description: + * + * Copyright (c) 2024 by ${git_name_email}, All Rights Reserved. + */ +#ifndef VC_9202_H__ +#define VC_9202_H__ +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#include "stdint.h" + +#ifdef PPG_HX3695 + +#include "tyhx_hrs_custom.h" + + +typedef uint32_t PPG_CMD_TYPE_T ; + +typedef enum{ + HX3695_MSG_TYPE_HRS_UPDATE, + HX3695_MSG_TYPE_HRV_UPDATE, + HX3695_MSG_TYPE_SPO2_UPDATE, + HX3695_MSG_TYPE_LEAKLIGHT_UPDATE, + HX3695_MSG_TYPE_GRAYCARD_UPDATE, + HX3695_MSG_TYPE_WEARSTAT_UPDATE, + HX3695_MSG_TYPE_STATIC_HRS_UPDATE, +} hx3695_msg_type_t; + + +typedef void (*hx3695_msg_callback_t)(uint8_t msg_type, uint8_t *msg_data, int32_t msg_datalen); +uint32_t hx3695_register_msg_callback(hx3695_msg_callback_t func); +hx3695_msg_callback_t hx3695_get_send_msg_callback_func(void); + +#endif + +void ppg_send_cmd_to_vc9202_driver(char* mode, uint8_t enable); + +uint32_t tyhx_poweron(void); +uint32_t tyhx_poweroff(void); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_user.h b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_user.h new file mode 100644 index 000000000..f2f07eccd --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vc9202_user.h @@ -0,0 +1,58 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-07 16:04:22 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-21 17:20:35 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/ppg/vc9202/inc/vc9202_user.h + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +/********************************************************************************************************* + * Copyright(c) 2022, vcare Corporation. All rights reserved. + ********************************************************************************************************** + * @file vc9202_user.h + * @brief functional Module Layer + * @details The functional interface is implemented by calling the driver.contain init_func,irq_handler + * @author + * @date + * @version + *********************************************************************************************************/ + +#ifndef __VC9202_USER_H__ +#define __VC9202_USER_H__ + +#include "vc9202_common.h" + +/* gsensor struct */ +typedef struct +{ + unsigned char sampling_rate; /* 25hz,50hz */ + unsigned char sensing_range; /* ±4G, ±8G, ±16G*/ + unsigned char effective_bits; /* 11(12)bit, 15(16)bit */ + + int gsensor_x_axis[40]; + int gsensor_y_axis[40]; + int gsensor_z_axis[40]; +}GsensorTypeDef; + + +typedef struct +{ + short int hr_rate; + unsigned char spo2_val; + unsigned char hrv_results; + unsigned char stress_results; + signed short int temperature_value; +}AlgoCalcResultsTypeDef; + +void vc9202_usr_init( InitParamTypeDef *pInitParam ); +void vc9202_usr_get_chip_id( unsigned char *pchip_id, unsigned char *pvers_id ); +void vc9202_usr_start_sampling(void); +void vc9202_usr_stop_sampling(void); +void vc9202_usr_soft_reset(void); +unsigned char vc9202_usr_channel_switch(unsigned char num_channel, unsigned char enable_flag); +unsigned char vc9202_usr_get_int_reason(void); +unsigned char vc9202_usr_get_wear_status(void); +void vc9202_usr_interrupt_handler( AlgoSportMode hr_alg_mode, unsigned char spo2_alg_mode ); + +#endif + diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/inc/vcSportMotionIntAlgo.h b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vcSportMotionIntAlgo.h new file mode 100644 index 000000000..b137a7f06 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/inc/vcSportMotionIntAlgo.h @@ -0,0 +1,13 @@ +#ifndef __VCSPORTMOTIONALGOH__ +#define __VCSPORTMOTIONALGOH__ + +#include +#include +#include + + +void vcSportMotionAlgoInit(void); +int32_t vcSportMotionCalculate(int32_t x, int32_t y, int32_t z); +char* version_info_SportMotion(void); + +#endif diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/lib/.WeDrive b/code/sdk/zhihui/drivers/ppg/vc9202/lib/.WeDrive new file mode 100644 index 000000000..c285ea891 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/lib/.WeDrive @@ -0,0 +1 @@ +C:\Users\zwj\Documents\WXWork\1688852039096625\WeDrive\维客昕微\技术支持组和应用方案开发组公共文件\1.心率IC\1.心率+血氧资料\2.驱动文件\VCHR9202\drv_9202_sensor_v218\algo \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_BIO_V151_MiniSdk_NA_01.a b/code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_BIO_V151_MiniSdk_NA_01.a new file mode 100644 index 0000000000000000000000000000000000000000..cee79a175bf1325c78d317109a335926e0fb2f11 GIT binary patch literal 18232 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 literal 0 HcmV?d00001 diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_BIO_v151_int_miniSDK_log_01.a b/code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_BIO_v151_int_miniSDK_log_01.a new file mode 100644 index 0000000000000000000000000000000000000000..de6f1fb76c64371fe4dae4bdece43776ce91c956 GIT binary patch literal 20668 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 anRre-4f9HqmWOI`d^1{&dA|k`Q}{n}UtN9x literal 0 HcmV?d00001 diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_BIO_v151_int_miniSDK_log_02.a b/code/sdk/zhihui/drivers/ppg/vc9202/lib/lib_BIO_v151_int_miniSDK_log_02.a new file mode 100644 index 0000000000000000000000000000000000000000..0512470828b7a5888cfe407d772efe276d95ac47 GIT binary patch literal 20796 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
B+Qdc4vj(x2?WDn5)f^*K_+Et zH8{}@XbU0^P0QfWN~<5xLaU$ifb#$~I3V_W#lCPJsS{r!V6F`k$TPs$wU2&our&RNw^le<;Io=)Ygh)L9b;&#bN& zG<%MQBj?lu&mLb{TUR}6rlvDyO{uBuJ~5`E2yrwhG}S^;i%hjh)M8UD7PY&nc6X{K zPOB`htsh-pUwQ7F$~l!P)D}n0shd_>USEEadthZf^nUe&XHKb{_iMo;Yo=7zDbufo zj;OrsH$sP1&YW65?bqTBuPmSWYxu}C(az_>4575D5zKkcX1+4N2l*?PD;;^<18!-ITdR{X^(~+cX241z11P) zWGo3eSG1noc6!@}V8}Vp@<_|ct*=P0Z9Tp9k1a!$8KK3!2JNlwP}}y3wizKOEoJY= zSqHM-Nc%DEol9hCY9Of7YR_V_7G% zmZj})f2jSj_R+N=XL@>VdPZnhAR|0KX+ha7l*`ZNJI<=K8`A{;zx{npTJ~)d?i_ci z6L>V)aRT=xJJFm^e||KTYUa9BCve_wC)(xSBT+ASG@3*Ik>Jr5Uk5q`zfI}aEqUGc zZr$(O9u8eKuxQS{q-g8=oxsji=aV*}3p2{ryNd@svh0Ba8?rNYUg0(!^uTmN|5)QR zEWP5F2d00Row0P!FAZBVos6EDPPeKJ+0m{$jx6llD5+@Qr%s^r;H2pMc5cR!OedPX zJ1PH`Xyl8e<@bcnie~?iboP>lokN_4z?8L-XxG(8+_H+{-;=vhQZ&*i zX?an&O=Ve&mCO5u(g&9<82rHWP1z4sHY^UUje1)SZ|=U2)=o4Z z`b$*$Er$YL>9-A`suK-cDLiM(;fBz$Up9B&TAbgA4hf^ZdH{LKK6Yyd&l89a|wMAjqEtshP3e7X#S1^)ue?dMRRu? zxE!IUM!g*eKfQx;bLc6`?|18>xmylxozt+mRo`gWEr%WrEeifMM4<<-qtMFDq#bxP zG~c0;#i%3{9(3*wMZ0V{*wAEI(mL|AdYbfu$0>#Hnp6G{+yU)FavZq7lJn~-M3o~>@6)fY}f==$ar|5)dQLM_(1Wzo$0NeOVFCqtY(B<=BpH z;r(|ugq^gC(l?w8`X+q1V#hAv!wbFL!D#;OV7Db(w?EK*+xBRe`;#hmyiQyF@!*01 z%eS;lPkkk)ali|HvpICLv!WyVl#Ecw$-k-T(4@r+J6)F)=(OXz1^c2o_a|*!T(o3e z-v`3Gws+gMYr8v?)>Bw9g~Zcp|WW3erNgH z!K_eOQLCF!*N47XxnSpl0ZoUVwSm}IGpKeJ3>d#WMC{+MtIaiyhAtS8+CMy0))mct z?MO6x(RavQL;X+9rQQ}2kLKKaB;T*esCR!-Mz~QpgX(x;r_yi5Dc>8H#cQ2%^j$Rn z-Xn2k;|lS0T@hCoE^g;^T^Z;!g!Z)ilNX2nJpXS_MyH*Dj3uxAf_-T4s*Ib4I14*p z=RSYqRp&*$)knI0wjTX?G~K zC^RY1X>cGEI&H)9?xD*wzWbqhM)ykRrn1fJ*15x1&Ux7_qmq8pt#VVOAvC{9Md?=B zYV(^!yWUUREBNgozZc%PGBpD|es%!uEI|2A^+Ig|t#&(qo}Wtl`s%~WJB7}1%NlK1 zJY!+*qHh*be>lrIOl=cA-?SU3#twH|ye^p1Z(+{h@WP$loWQO#orT^W>dzXx3p)>S z16`K6(N_068H-y||8qap^P*!?XMwWooQBlC;ePvQ>n2}bcIkkM(rx5ySAp?6)tE!(^#+zlwba9#@4d;X z{Nv)}Zu>rqbX~f784S21W?aPP9K<}`%An|R{piVt5+X;>QC^u=0t*n4XczWBi2 zWmMk>H?KeDQv8EO_kHHPz3PP*8^W#n7OnrlX;Dcrn^UMWZq>MFE7$H_HcrLcyNvqF zgGJtVF6{vn`#uUk_u2Bb4N0N3n^#SwHoN1?ie1~1Dh|BrZcaYj?}4S?_3yZ$;^n)- zH?1vNdc@60P99aU?~@BDH`z2I2a%VB-l8vymcH)nUDgl|^{x19zf7`*^JP2 zZbRra>eYKY4dFdbM)DBXds1?)N#Y!YddbZzp`QohjnYUHH~`vwY2v+hn6nPqgJ~pBlix<+oHppzgyyddPkAB z)g8X7VP|Wn#r^=L)@|`aX-(H=gs=MLy#Ynrb~(dW1v-}=+U)J4+8%yLIYRfP^-Q6C zmD0=zfA>qrYZ}6n0`R`P@`f+qP-F9K>Q&O9!Cyom9d#>G(;J71?! z{a7Hjwjn&K&FY&T2yI(KW4OC{$3k^eUnlPXBQ=J>)VigxOv5EpMn1?BYgN&l$G*M zdjv|==2@hizl3_C5XOpiRn#`qHhZr-udk$efV1F>gUOU~#uD15Yuy10%X-o5 zZg+6;fCu(Jk)82aE2qcu1p|tb_d0z_ZU|5KVEG-#avp6$y>!mtQ8Jw^ChR~ z*gqY2S7<;qup7Nyn6}fm4L=@ho|@*lw3SmZ7fpSmZKKeDqSoWxjJfO6PTLje+$eea z5^{dCeyKa?{497RP9Jwve(e8Ivm*9$HAO=<#Xge^v%l6-_3Dxq#&c={kv0NAJPuw@Rm6l-hSFkUS4U&So~2&sBO|I17s~2tAk%h^Pg}Qmqnc?l9pfB zI4Sk|w&^nV74=x_G_2d_zPxhfzG(BtPP9W~C$u&pbp1Ye+^QD)5fbo9zqa?@v3YkM?Yuo>;<(d2@Q{@CWThR{nBHZR_m zgcgaBA!ESLzg)VK;)EN0L4DfSG=e5Yv*|y7Bh3;U1v3^O{bld6QtBaxl^_V9iHmt>06x? z)G{3@IID+WHNIl}8_wYiXwOgSxFqeF=LggLIpu*l+os&Tc-S**Z|yO$cw#~2l!BP^ zi(XF9H~w*~ZXT~o`r@<0MYrQ8QAGSpxWu!4qKi+yWxbM)?UQuGC*QVDxTIsJ4=!=> zo#YhUCEUce&vh;oTfmvO>1rV-`+oQDgCr}>JRUH1BYUavye%*XgJc(a3oSaEN z!pT7wOImi2nGvdk=lwcx9lv0|6~}d^|0hYi&b0r`RxLKy`8D`kS)uGd>1Y49jK*e9 z*k;Ct`o9Z*YxQQ8yN=(o3}`y+l#-n*owQ%vwtfS5oeQMSs0OzDM>Rmk9lkN4I{Y2N zUay%aivFAt-7ms##W9L!z~7GHx4{3fG5&UHe$f&N?6*rQ)YC?xeD}Z8e`{qFXM>;C zZy8;uLN?xS+x};(!tW_u=i>it{jgEAwW{v3&`f!msj^U6;gH|33&%UXNcJ zM#Z=-+)m9hLV;#!neCdjaK~lR08{J)nhnl4r_C9uXQn1)(QLzY2h(+@U`JuMnc4{iYFkoux*#wh0nnX&W^D-HMW}uGKD8q$~N#mv}eUM%hmYU&8NRxOHj?*+q zYn6~@T_{fb=8n@mpK_cY?wfT>|2CX@=QiiGah$YbCn=K!G>2D zE9=~*kFsWj609vLW>!3At74~p(>QI7EFm^h&CI7_m`P=YsF04+rn&lR8xJ0w!Ru;g zXE(&zZBKdNm4=Xj^D|XMP&I=f<^v8L0sP8>DT33J{@uzDE>Spv68Z=Zp%0qFLI}Hb z-o|lKlIcXe<4mD{_v~b4jV38Wk{b^>M~4KIbOX$NwbVXDAoYDr~xOSIG_=jQx*t?0puHYGIPRbaGlb_locv?KHn-M2B7}xVsxOm98TdG%` ztEyi~Wh!F|4$z?macQ8Iik0*#vOi8sO;X<1SxK70#ZEfe)c9nnnUd6&i#1VOZnYch zP!PFG={=|kxJ7yuZc0DZBB*93>aDL#D!0Yj#5Jp=?pn% zTT3}gQY9X*rL?$fke0f{lRYCII#`GHic3SZ)JI9}sod^REv2V)R@FeIyh@canJZEi zX_z+kjVCo+OQmsXgq8-yrIA`17MDh8X;fV5p{22L>0B+HAD2dJX+m5Yqoql4X{?r} z#HBu3niiMN(o&6*I#HkCo~NZ?N&!(UN%K@ir0~A1D&l-?njKH-0xi|Yr3?K~@)+F49tplAb`OF4j_?09LQNAQi0I#KdOY>4eS3t3oE}kQ5!#6oqnwDa};L z)TgUHlhkKGeFoKMNPULYXR`X_SdA2JtUgoKXA||=RDCv6pUu^0n)+;^KGW4_hWc!& zK3l2J*6K4;eP*f8HtMsjL&Fa*?)mt3r_sG6ytva*3oVAL&X>Sg0lyP4SwOXb*#i8a z%S5?az+wR_1kl+owX|3x;BEmMG;q@42(?pR;ZxODa&z^SlBT{I@mFIOQ<*hk)|6Q@ zX3hC4t%Zu$f?0ZVyC!yF3{#7{Ya5_Xc9&!Xv@G2FRAIJ`y9ptqMtK; zse$6gtNdl89H;-CNMB5(FC^0E6X`!eTXFvPBnItHq@N_xy@~XdMEZ6jeJhc^kw{-p zq^~B@kI)mu3;RK0==+KEy+rzMB7J8H6|^-9>wcIRV$qL(CDe$=aNiTOQ5f#uMX<$% zYCb^V_X1W3xI@5W0(J}dK)`nb!fK4BbTS1@6L7hJH3GH?ct^lM%8OcPLQ6YC`>abT zVg8tlRkXq6qRnT7+QQJZ1z;9FSV&8^gmAVJR5A*wWK`tjG|QX9sqIxO8bd2mwnlN) zSW#m;ax+6vnNMw@>{(|~*|PfM!(Sbz(*|3#%|HrKl}Fb!RV`ri1vTCQh3-DSFDsNQstUacWAiz~3yv!uVzh z7U{Iu`Rkt*HRUs>X3yoD`m$q%`33p0>@LHqXU@y+UtU+4J!V!_{bh7xTHf%IaoMr_ zA`0(cP|))rZQKO z`7(!YC&PbH;lz4pTJ_Xv&cwRel@;~1b7m+HsWzWNWv!k$m6MoVOKDv;1$P^k&#SC+ z<~f%;SEySj`w_+6hLpnW*;B`;3Zm7|)d)AX4W3CiRBF$tQ3Gesx~#HxbY*#sBV<7B ztQlh{I}WX;Uq!{tS+z5q^2v3xYUb2ej;XJ%sVSk{kDPb;)XJG-%O}@Vg4Ru&RjY~= z>p`pLzya0soboBt=hW4Yt)5ZoRM+*NQ+K&TcgKyXte7=(ii%b}b!O$1Y*n#TAH67U zZDmb$`Q+-F>iWx_L8EKR>!>8Pm4im3Jfo1;!tR5vK;>1|A_WCh`l{XsjjpU!I>n*# z^s6GwpRlxF!ih<>`hnG`LOh!eP*9@Y~J!mwtK4|pp^7@Kt z&gAkbV^mCtobPcZ&gAN;$Z>5=#hA;g$%epDbyNxbIf`!DG^%Pa)o47!sy>llRA_bG zn3~GU*|fUKnNu9f(3}}_YUoy0O0Zud?Vm_X5^1U49OJ2F)Ya3?qZI>c%BMP0s_W1Q z#>|*Ci|#`mGiv1MvAkQ17&&@)zhR~_dfbQ+gGUTZkOr5QP3%8-XqhPv88>{?#L**1 zC5U6r8$5it6}x}GQj*4w9AU;D+pqsH+B3^&U1JjTla!KSgNOIGl02{9u%VXJzu%al zWn=sI8;1C_KhDoY*^U^wpucJa0w-9=5O@4f&DKqg+U|9lAx6K8&Q&2 zDWe9TRHYY;9N5oHX3XHR6UU7*m9eACP;rUn8F<0)5&g!D9bA%7Y5hm`8*5buwhl=X zhx98MYNk41bQx9q=zfFkZ9J-M^x%=DgG(k3A30+1*pZ`=-ml%N9ZS5e*1V}W@qSsp z%~mIo7%Aay--O$^V+nU7C-@R?1x~!1IWa6DAO5Z7v6z&{xcNDLSFi-eZm)e6S=tZq(iRXL4&)Zb54!L!I;QC`!p zdWxdF5`ABMWqExG4GYz{E4^e+ZEYoWcvU10pJgl5g*!BA(|}FGDfNgd!_%p{C@%HC zSiS0+>SxTR@oNOeFPd-E)m2wrj$V{y$~zDZ5k#XY~9RR z^&^L>j(7}KTU}KpojBrBYR1T-)2F-+8m)14-N>2s=qzC^#7E2-SygpjZ8hEfUx{G{ zS`j{q%>fn@2^um+jxKX%^JY*bo>r^AFs8>*0Vr?yqcQ1 zGxEq(UVC}ol*-9-rk*U*NLoqL&b-U2XXeeAQ&*$X%+H@dTc%=eQDI(XP1S_znH4p2 zXwEpfy1vfQckJWBeL0Q(m(Q-O%b$kJ@0f#@R81wKg6Ot>h4daBsJ_z!uKI2l2;iHU`59`i9UV+ZsGaH24oNG+3=3uH&`g~JS#wLx z+v@Oy6zN`9&R2$ZNhNh3p)CZe>~IcUmhNjT*rS*0a) z*(8vvc!pkQn?RV?B{7O6=boW(OYoH*veu}0{u8B@#5zOgwX?)NLqbkc^geZx9y(b$ zMVI6(ow}b}+9UJe-&6Bk@4e-{~Z9&3wWV>YR`0WE%>AK_k=%Cj`3F)n_zhztN z?bpul8F0eQr9F`Q5ZUw6G}rrS=oGzevI3s+Q^j%mRA`PV?X5^cPSFmsvcuD9oDw(} zX^78nLGArKH2XhYM+aGlpD(Iv)yIWS3!JJ_qYB_0`*oZZ_=5_OUZ$-$GWFK+^TYW+ zN7tRillChm&(Yy+^tQ~PQM&2WIt%hbty4vBdYRg9_BS^#;N|gruL=; zUx+#iE?*~|zmB>+L^YRDqFCpypXB0HoquX$T-(wDPSIOj=Q`9Xz%99DU><5j;&+rS z?DtjF^ti1=b=#2?t@6`m<^7yCG;i@vl83W&%pN)=DOG2^m3m8zvvf@A-C-$7^j?N z+R=yiPVJMDN@$MWccsS{y-NV+q216HH?WIKzSnOK^e_ZVvR^C^;YI-xLfeeTjVvj( z{`UKdb97qy`umiy-mivjmVP-*#&&YYIyN&GoFw-LQ9Xy@fz)2_q0?4-Z_eTl{* z?oHc>m$nP{yZ%T^9YPS&&QG?TQ|i|_Bunon)D>Da(300CI_-OH?d< zzNAdsi|ef@aEXdX32>SHkS^NMD$rax{4z+5l9Ow}pJPfpmwiOiq=v(-)t>?OksiR0 zOBvui%P6ArL(P%nw$u4JRr5xHTdq(U%L;s?9I`fl^vO!xYNG30XK_s#u~ZAzxyaIM z(<6^&S%DwIey1S8={WtH%@1h*jlqV{>hMj;H>cdv=+?%!rLJl6r=}{6Urk?yI8MKzl%P7X zLigVh45o!@=^RUS;p~EmGw@M`j|zOu!`Yk}8IqR?esb4JGJ&lkl7YmEQ=( zi7tJ!nN|a&m`i|eV5I6WmS5-btDPLKZV^+ln5&x!3?A`QJsEWdvjurB{3 zD}1uWr&zqo;{HA6I=$&uc#XwpT70&}=URN8#jmjVl@?bw3n`83W4;xBjl~yPe2K-c zwfG8)ud?`Ri{E4Mdo8}+;tyMVo5iTg`S-h*o^DQ3lPdI)LEBtJW_qX@} zix08*2#d$t8?WzrD?Hx5IQ&*C{C114wRpTea{TpH_(qFAZSh?e-(&H3`{VTYTHyyR zo|7a3`@2}Ym&MPp_}LckYw=Qx53u+Uix0E-D2tD__;`z7Z1G7JpJMSUi%+-sOpDL9 zc)i8vTKo!&FS7Uwi?6cyjTXPz;%h8^kHsIf_;!mwYw=wcf6?NvSo{r(zi;smEq=h_ zM=XBA;xuj43%Ae47U%n!Vd3!BR(M;Bcd&Sl#Y-(d%;F;~KHB1AEq=bmCtG~F#phc5 zN{cVD_%e%cviM^bf5PJ1E&jB{cUb&+i@##=H!Qy2;)g7L%;G;<{Dj3*f{E=R&EnY> z?`rX4i}$j4ABzvN_-Knyu=pg4PquiK#iv_*w#Bcs__Y?_Xz_6tOTaDPFMf+g^5i|1Ip z)Z(KoKGx#rTYQ4WYb-w7;`J8iXHSr2&i^7Se1*kVS^Q3muebO%i$81e=Pmx0#Xqr4Z1J@g zzsKV1Exys>4_o{Zi*K{|V-|nX;!j)rS&Q$o_=^^Q#o}*R{E)?ev^YN>7;o?GlM~xZ z2a9_a=f?%(@q1X|{9s%>oF9RUbAGff&iNs>I3I7N$Iq9=!};N|IOiwJ;(VqRf04yk zS^Rp7-)ixDExys>k6HXli$8Di*DU_A#gAA#$v;Ts{wdAkSr%_^@obBG7SFYKFN>F2 ze6+>KTl_+cPqO$li`QFxk;QMd`0W;7Yw>$6zQN*~EWX9!$1MJ%#c`UM7v3JYfNOGG zx-~g2VVJzF#j`D*WAQE)?`!eV79Vf%2^O!nI4%I2`CDZ1n=QW9;`dto5sN=z@$DAh zWAPU){))xlu=u+c|J>q-Eq>JE{NxI1fVU?`lNR4$@#if5 zn#JF;_)&}hZ1EEoPt%VyN&eF<-rC|>7H@CyY>RiXc!9-xSRBug@xtw=)ZzmyKFH#u zEI!ua6D&T(;#C%(Zt+@+*IRt9#phZ4N{iErL=(z;v&C<>_y&t_vG@*)KWFjREdH*= z;s%hSZ-X%=s3@f?d6SiI2ULo7bs;`1%O!s2TzzR}{3SbUqs zpR@Q&7Jtp+Z(00(i|@DiA&Vce_%VzBXz>#kPimUDeS#KGv3Qonvn`%u@h%oGw0JLz zms)&~#Yb5DVvA3&gmF4d_)@|+d=`EqVQf?3YYC&)IL&6xM#9*h!XGD$S`og3FiI)>6~gHTe~)lW z`sX|}bG{&)W$?p<(HcEqv$g?AxdVDKX1#Re}W-pk#Le^NxE5xfFaLnJd4BwJ;^z7BZ-|@c!*MMv z%{U(#pt9B*yb19u4c>~ldH%Npar1m`XX3{Ue*y8ZasIA1@zw_KPux6TI+VD1esnDH z0fzr#;^z6$X~gFm;kCp!864NP%=2YSh;K2%uP1)c;A@Dd_?Q2FJCjRR({9cwd8~Et=;W4-oHTgrn`5^EF(Xnq-9I+SJtsPbY59-*D}z zwQ+vOBW|8=L)*#^Zdvm;^z62e-Ss&za&xH|I~=zgt%wquQhS=csrZ8c|N2oalC6&@?T7R zlEKd=ZXW;R+7iEm4>7{dXyWGa^#tPl>OT&zB0k=q#D*QbqpXjS{uRW{^PP)`&o{Q` zD&muk`n{F7dAy8kNf#UGKS=y(!;fo2=JD;*#LeUDJ;cr9)7OZb$CK|9H;)fLCvF~} z93dVt#;>1SZOIO(UrJ4|0*JGwwE)B`_nVppGt_E z_Zto+?opfKCG3nPUTW}*h@WrpD&pq-8MBC+_jh3IxY6)0AU?=%AYo@2@gCI2$od+H zoAV1?`+1uFC43!WbG&$%cpoFZM~P<}9C5oCd^ho~27iV4WZDLj{yW6Y@%B^VcN*be z5jV$ITstza|NTPT98Ynr=ztL)59gTUF~%zM`d^f|IX+|TGRNcY#Le;aY~mwmos$27 z#Le+>6mg945{}1I%=zPF;!hjlcr3)6PvY7U#&n5~M`q0adkNx8Ftw*u#CI4R*OJWn z_?^Vf`{ULVKf~A`A0}?jx3>{D`=7rP_l)%aLAMMU1JCzsLip={%ac$^BBfKqf^Zw8Xar1sIymWY&vA-7+ zH}BWNi{H%aTLXxz6A)@0VP_<9bAE9?ar1gqIdSv;g6YJ~`wQxboA(!7N&IS~y)Gth z&No*PH|LwT5IE}oFlIfg9JpNElrZb#)%m}}T z_}K=ZLHsgu{27CvMI7g(c%kwWf63q%5m#?>REA7v2Jz30@T-X9oRq|0N&H8H z-$6XXpATj_4-ro`_*2B&8vF&~p26QEo@el{h!+|BIPty)#~X=p4omWL3h^-p&m&%L z@Uw{58GJbL%M5-Iadpl}B}nBbewz_~74gjmUrGF4gWo~?NrOK`JU+f;I!_UQ-3Why z_~!}QsVm!K8*MwgO4SSYa+Y^oe9KG2*vRxar61E8sg^jUG>Dx=ew>X zZa&|&h&WC2^pfe^M7*iN*As7N@JESvG5B-DyBqv%;w1(@Kzx+Je*%jW5tsL?gO?Nki{Y;&zQEZ23yF7;;?k9?>xuu(2)~>7 zC?ot~;=7FSzZ1X22!EdVi$?f+#J@Ab4-nsPg#Sc5KHs39!=MWz-y7j=h_5ktSK@aY z{7mBA{P8i(8AKe%)VyRm7Z9IlgjW$yH^MI?zQhP$M7*;ReiQMBjPQGi_cp>e6F*>t zKSR8q5&jDC41fGdb3PI3LOecw6>K?z|k>;ckf7giLfjExQrF^FoZ{_#5 zY0g>1D~<3G@EhS1h|e~{XAqB%7irG#iC<}iuOR+}5q}NwB}VuI#19(bj}yP%2;W0I z#qUqkoVSR_k0&ynFNn`E!oMfJ)`(9(%H!N=gtsPszY*S<_)a67eiqRAyAeKs`1eNm zSmHP*AoW*CoPJ(MFKJF4@z;#-Ma0XD@EeKmH^To+{2C+tuf&fS;m;DsIRaiXomYu( zH2fbC57IHGgdZV}bGW?FU_>tn$4fYP$)uOd)Ad?^e@=5wC5~63NO%G9Qw-jZc*NkN ziRT-)Dw?yzp!%^@nXL|!w&sey)(ezehTxr8~MNsiH-N{C%y;Yc%Qwo7X2!B z{bv1Nd6_=HhTeRAzWh3X`l$d4{<7HV#T6} zCDuzTg)sp|0{j(-xv-}MNyMh%#x5SDSNS(##H=ubl#|k9dF?w;l)`}$67`;|rYcW|tEWX|28$RZ* zqtHi1eq6s%#R~nD3;nef`YponNhxAQ;w$vqK+JChF~6n6iv0B!ON>~tq#W}bNzC8g zv0|y9Sa&~0cRz;TQexfxlJ|)F{J6dS`txhP(9d(sZ!obQe#v{t=8X09OX)X^nBQn( zeyfT3jU?9F&tES;FMflG_40GtE1o~UUV8iM^_xbaU-z+Ie#*suuKeAv&~GJ$1rl25 zZ_7fzl@!LLl!XPd-a^0S6vn#+f9n?(`FV~PNE%LIp#&DjJBb2WMa6$g{W~SR!w4|uhI{eXic>t3RCTxzExYH?^ppU zRsqBUPz5DWq}mRE+JpdVYbk(+qkxW8tacqzidFkjKvu2x3@B=o0H`ia0c&Zs$9n5x$5dyb6us)0+R>p%nF|xNbxIMSE(ec_Erj4{V{#3{Rt_kZk2#vd1`+pLAI#c`K-V%1g=RSiW1iqyUbpmt0Jq_L@;k(5H! zcPpSvU8wqXD5@(3klHJhMq8-%TLh_&gMhRk)j5Mp9qKBJNxozL1{7rB30?tGplAztgEl3Up_+XHYoNOdSzebMD;5dPHwR zs-N$uvx3pT7zv{042tp@6vZ?s8ka$lXM8dMFqCbtq(+sX__*-UO_O|>C290fJG zl{?i-8R4quFt}=A1~0VMr*>^K+^RX%Sj=!M|Edj{;a2`s!#2aM`cpfV5w1EhlUw;w zov0aZl}EK*Gu+x9YO-O5+xf9Jw;GHLf3a15)dQK~R({ohV}@I8PYu*&xK)3udp5(Z z{HopC2v@tR$*ugT-P8=X>Q9Y8X1G;tcl}U1?`opwhuT$c_tkuCYZgRJWBvGcxmDlphuWJ<_3VDAT_ftuh#zY0 z#4$Bd^+QkEtZGn*2ix_jrn6=-)F2V}qlV+mu2FS#5D&JiR1GNcV7pG$_!1Act4tke z#DncRQ=?Bj*se5nydMv?YfX(!ey}}L;`0XaV7u1T$Q2K^o1q%G;=y*U^*~GD+tsMfa>Rq}YE)xnJlL*Ad&uNdDrS4K2hErqNT?N~ zJCuGfCLLf*4xwZIp_LrJ#pFPo&lw@Q9HGS2@I^|YIt@dh$R5!6n9a=HNgG}r!zDQE zjeAn}Y){K$_Pm_SlaQ#r73`@tpAblhVpqLA`{t@Kqo`xU1i@}<_Kcek(-Wc~r}5J> zs0{nq0Yi42nEIuR?euLQCI2z^kt#>>!AO?`Qgy`btL*3 zX*xQtxMX6*v`Z#V#jgd+4|x7R^{b-#7YJzrBFO(;P&yr0+>%gMpGGAMLV=J)e9SFD6Id#nSC z{vvdJ<0G)>Z$Q`S0E_-U^e&tZu;}}tpT;_{=!c-|2VH&>V9{~! zTzVesz@pGn#Q_tO}3BLfh8{866d!u zV9{F>mNoz^`Vd107JW5gX?wt;_n~}B+XEK8FJWnWz@iU;zbBUkSo9(AYaLj0++W`I z7=6$JEc$rD?X(Uo`Zh{By^!k%SajS++*ZF#99VQbKhsX@z@i@_oLqbVQfdiqTfz>y1scBSkl2W#p!+MgBD=X@tnJ)4=m|q6PEOWMIWpE*z&;8 zf$@wm(xEC=7&`Ekq^F<7>jj361<$)ndjl37_kl@!0~Q_ke~BMh^w$VWUk5BYo~cbo z#nS>TI_@(~KZkW-(Q*G;dSBLoMaOf{(zgSPK8SWs(Sb$BbG}j!z|c_-!w8EXSp0a7 zQvAT;$Md}D{Ww3s;>UBq;s+LgAHtG8u=r;hI8tc?#uox>y@qh!Ijk?E zuU!4`2<~Am>Bst=!2KEHS&qCC#&`~+q?GXq;4;Q-sS%Y7V4Op^bRgp{z=IeMBb=)r zOsRrj|0qH|;k=>jnG63g#&{=usn+KM>vWbGcn#s);p|^aSkl=5UHkDYvFLbDeC`PL zgiFRT#&ZXysCZhkfX`#x z1^9f%`GoT>V2pPiw7|xp1@E-Z{T<_*371S@ycQTQouTCk;ENgKJpv^Y8Gi^oiSa>T z{eTbN1yC}Xb-Z(*3cwN`Yq|O6IUCOhtkcA^hPm4RA~0TT-3=f`vPFuhphsKDKYZ$jAoVSoM?nlmB#JD{?8spjI z+{LV42+tD6wa}L`UJZOLG!Gh^{bA@gF@Az@ z?#+yM8TfhlZ(;pi;9D8vSyl1lous+9vEH6==^Dm4g!AxHC0YhSznwAO!C86-W*1@CW0*mJ|=+AOoV9~FJ{$H#Ei+&CC9jpV3 zex0EMi@p;2PWA(fzS_`%MZXpLF7^Y9ei!tAunsKxpP@g;IoLSoDXX?_nKS z^li}p$vUv;Pe6a3bzsq-F?3+jcS3)G{lKC>5B){ffkl4_`hT+yEc%;<4lMdR&|hLd zu;?E{f0=b)(f31tg>_)je;_Pv<2XFJZ2*hMrCl!iD#ryDy(M9Zn+1=K3oM@Y&|l-Y zz@nc{SmNRxmC@JP11z4N(DizOML!4n8|()by#)H3tOJWa%+P^F$9n>zZ?PX(bi98+ z@-`J7oi||dTmoI^4OsL#=x=kHz@p=w2U7pQqAw#X>skemUKg-<@J>xx7qIASpzq~% z0gHYo^mkYX7X7b;WnEk0(dz;h58lTq>jD-X?>vaU%V`3Oz7zU;tOJYwJoNWj2NoUg zMu>jEIPC`bWGjV9}FEl5GSm zdM066S37w0x`4%VDs%gMVgZ>Tcz@q;Fx^7p%qT|pddW1e`0T%sc!m_Q` zz@xV{uz2o-uD3O?=$IBqzoidafJMg?HhPqGV9_5jbYRi9LH~|EXaN=-Q;_H})`3OG zkRSb?bzspk9LiV;EINj<=)dTL7GTj)O;UC=L7BGziw8|d<_W-}BfHTb=z|tu(IG{D zWF1&^oH!I6SaiIbL-s*n(eX|W$s7G1xt}*+@!(yovR!~h$NM^>KXD#_MbCr&GwZ;j zad-OQ_fkp2J{RHd4qT{_E(O*~x7X5s}QkDtupaN(C7SAN;x{U#gJ`MUC zoF=g7HPBu9paod;Iff1_I^H27V;`{SD+o*8u7@XyK4<|J&uZv8Z@{A84n060v;d2~ z7P@}v3b5$+LD%&LEcye`b-e+LzMZh-?OAwq-hjok3%br5u;_TlS2RQ)v;d2a^LUa6 zVA0=(9;OdkfJJ`~dNS+4q8}zKc{>V^&Kt0J{sldSK4<|Jy$Kz(NZd5S5*JuJEuriE z2w3#?&>L}@z@pXe#@GMaN|-(Sb$B<)Ua4_5+KK%ORoz zi;mO%(WdMN7JZ1J1B*Tax}HA+i$2cKfknRnx}GNhi;i~}iViG#74%nlKLQs0a>BAL zu7XEzM_}vaK(zMHVD>jil9x`4&=GIU)oVA0=(ju&Cm0xbG_(9>847X4G`Em#K@{d4H) ztOJYw6Jg2QFYxHR0gERosuBIuc{1B*Td zItFA~fJL7MJ&XOoqR)b^=S9Gx*Fo3&5wPe>2uoS;j$pHmUTS>PdoMii)S13_N)Vo{uFdf3TOcq{Tb*TSO*pz*HvX*Z^3ga zdw|9BF7#~HfkpoW`ukkxz@mQ!y(9a9MLz^x&((oNKLS0%eqhmmfUdVGu;|C3N7)Z7 zdXP@W$=n-Q^c3j0utp28=*^+)xi_%r8PN6I8(8$V(DmFKSo99iJ8}BJqIZI>+ZeFu zr$Il$^#&|D-UA}r0`L5cdYmS(c<`>;XlK@eMIQ=XpSJ-PeI)cQ><1Q|pyM8=gFjku z4$##vb;JE9Zddl>o)fnl%Lj3dC9Fw)W)IG-`jN4lldY0**&d^+nxfeRRq297bl z7+9artOPD(y#^RR96-xFVD0$>a53x4f%UcQJAu2ij&oe1Zw1B#mX;@hdotbu+>0^J zHMzYRe*}z)EiIn|pUL=Z;699h0@mwIqUv+cVjcGgiQWeIY}Rvt&tY5u+?O%#!*MZL zre!#Af5ztnmoT0JT*`PhFecHoTm?LU@pZtuey#@|$oijv2Qj|S@H`AWnDr+N9p^OM zA*{b(=x+fJW&H!-VT|_!4`+;X4w8o-fJd+%q9!hS6X21oXBm19@F>;`4E;>tb6M{X zJeu)f;4zHH8U6{tV_BbO=(B*wvHp8QzXo_b>nnlrLld;zW_Z>DpU?V(hQ1m20@nX- z=(~U~WF1owDf`>NzhfQ8lI{e?=t{(c!&mnr)^P|SI;ODh#jImE6dm2GJCSwl-tHvE z*yY@E#%R9cNAq$gvyROpI;zI4U>(s!p9VaI^?F19J#Z!KOAUP$a24xo4E=84sjNR_ z=v#oX5?Y=%^nUFS}t}bTJ{0| zi81C*?(K}f2fl+by)Mad?_}Hv_%6ojz-t-fU5+kxN?LM&?_rz={Ab2JfbV5|4)8k0 zgMimF9sztG<8iYG9ryvpmjXY?cpfl123r0A{4is@^U?haP?cmMw(k7z$YCLQlZ6mHp2EZ)3a@_)*3$ z0YAq0HQ>h?e*pXh<4=J9#`qBMlZ=l5KgIYs@OH*7T_kq@&NvMi9U(2J0zbpJEAX?7 z^MU`1ac|%qjL!nz$r$fWb9XTw28<4smh*t0WBfbd-HfY%_b{Fg{7=SnfS+f4Iq(aN z7X!b@821sm|IK(cux`(H0l&ohpMhUyjC+dQR~X}-A{QMzEl&Zz#`qcF*BL(#{08Hf zfZt^N4)9xyKLCE4G43^TF;LKQ2>2bwKLEeW7{5p1zQ-89L*Ty8xC!tFj5C2hWZVw; zBgVJ~$^Doy?m==tVcZ?~Q^t5#rn`@EDKG{rS_T1s#&`_y=Zwz-{(|vj-~)`SfWKsX zDeytYxKGLbit&8lLyVULf6e%hz=s*H0se;Zoxn#J z<2Ql7XZ#`XzZic3{4wuecn_qjuj%{*`~!RNzDM^*#!Z2LVvOH4a(`xgD)6_QP8Z7FDkz@GLjfll`)VFC8EX9aY6pEwp^PkU~JjtZa!*wda{q3ici z1AE$o9xA;VGa8C=cr#(15 z7s+HD*wY@|CKAbF9oW+z9OFdv`yzoo?ZIvt!3hIefIaQ`5PCb-fj#ZPDV|7s)`30k z!SPY#6xM+~?fDn<4y*%v+T&2ujGW3ku%|sK(6d%gA&jDUU`>%gA& zjECNpbzo0>E{5KXbzo0>s-Wkx4(w^qOz3&61AE#t7kWPHz@GM84gGZ1fj#Y60=}k)<&A^a% zr#+b7MS8Lh>}e0CWszR21AE%@9Q59-1AE%@BJ?v@2lliFQ=G_|tOI-6gDFd-59`35 z_IwEaEY^WN?b#3gY}SE2?ZFfwat`ajp7vnKkMw07*wY>i;gNo<1AE$Y0(yVefj#ZP zkQpgq9oW+z41tkS)`30k!H^ayV;$Jj9(4VY0jvXi+Jj*xGLUs(PkVBp4`LnI)1F-D zgINdmw5I@iEJ+0bd)hMq`Uv&|d&3#y`dVaU9DCX`7WydGfj#ZH5c;{S1AE#t8Tx3} zfj#ZPd4R|m)`30kse?Y2bzo0>u7p00bzo0>7DFG;I}e0K!9^}+9oW;J zSD;U19oW;Jcc4#V9oW;JPobBy4(w^qLFkiN2llk*DD(=}fj#ZPb+^bA)`30k35H0o zWF6Sko+i+%SO@mBrzP~MtOI-6(;oUX)`30k$$?(YI}k)1&}Xp@>}e0qu|#IG4(w@94fIP{2lliF z=UXDRtOI-6b2aoj)`30k!MT@6J?p@p_FNBr4(q_4_S_16F6+Ra_S_BqGS-1T?ZJ7N z$UN48J?+^H{c_fUJ?(iC`W37Ld)kBF>y7-Lbzo0>{t5j`)`30k!8NtWRjdPh+VdXt zt62y3v}Zr`Kd=t$X%EixMCP*&>}k(2=nGf}_O$00=-03g>}e0Kc|{ho4(w@98uUf1 z1AE$&34Jl^z@GMGLtnx=u%|s;pf6<|*wdZ@=-09i>}gL==-06h>}gM5=*w6K_OxdZ z^yRDrd)hM!`U=*8J?+8oH%3;n4(w^qB(@gp83#kWF6Sko@=2uunz2L&-Kt(vkvTO&#lmJVjbAip0&_#W*ykmo(<4%VIA1h zo=wniWgXblp2wiy#yYU4Jx@bl!#c31J-ea*iFII4dtQNlJL|xn_Uwgz2kXF|_Tch5l#Ofj#YM3H@Hy zfj#ZPvv`qptOI-6lLLJ{>%gA&;5TI>_puJ_X%BuEJFqw5JyOUswnBwCDHGH?a=v zY0o0)e`Oum(;obeLgW$Ffj#ZPxxvV0)`30kxdZwZ)`30kSr2_H>%gA&JP3Uo>%gA& zY=QnL>%gA&JPG|V)`30k*#Z4=)`30kc^>)`tOI-6^BVNOu@3BM&%4l{WF6Sko=>4a z#X7L3JqMw0XC2tno}@}Tcx9oW;J9?<{6I20-7{tW#!)`30kc@X;RtOI-6gY&eJ zH&_SuwC73aZ?X>TY0rN_e~WcsPkWw+{x<8tp7y*3eJ|_4p7y*8{TX%EgbM?Pg8*wdZ@==)d)_Oz!b^!=;@d)m_%`e&>Id)kBFDvo^4IDPkZp&#*u@p1AE$24gD+Dfj#Y+1^p1~z@GNZ zh5j|`z@GNtw~!-;SqJvCXCd@&SO@mBX9e^ltOI-6gJ+y0-?9$uY0vG@kFpNzY0tgT zzhfQP)1C*RA7dTZ)1EERzh@oT)1IfG|BH2CPkVMk|ABR2PkUa3{v+$ap7y)}{U_Fe zJ?(iP`p>Kbd)l)f`f=8QJ?%LJ{RHd4p7tDr{tN5Cp7tDvuHFJezcC42d%}%LcU=_$ z*wdco&~Z}|Ex?}k;Mznaz&fy}J=xHMtOI-6(*=5nbzo0>3ZREs2llk5C-h|2fj#Z% z3q6H(U{8AnLvO@7u%|tvp*Ln7*wda1p{KGA>}k(r=uKD$_Ou7T0Uc?|I#Xw zPkSDK-imc#PkS~)Z_PTer#*jzp2<3}r#(BMXR!|KY0p2Q<0ds)fIaPb4SHMFfj#Yc z4|+S+fj#Zn4;>FR(gN&h&mriiunz2L&-c(funz2L4}POMaw_Y}d~vyE+nK9oW+zT=R)USqJw17kg&{UsZLs`+ah9!jJ$Nm<0?uuy7zT6Qfgr z1c9O=0(C$kgdounFa$*F076hf5s4sL2V@cfEedM2iiqGm*QvG+bwF#?Qmd$F?R}s1 zzVALOD`Ne6y}$dr-~ASxoagzkcdxzo+T&S!SQ2v>cn{{#Mq<#%bxu#_&_-egf*a=0 zMq&no_hJrhBxVHoA7Y<3>(O&_-gu20xlPw2_#kG~&lFhc*(E295`LDM1^FX$M}y9NI`s zSMWj1p^e1g`ee>v=FmoBjsPFR9NI`sKk!oK&_-fPz{{9J8;QaDE^>x4hc*&33j8?c z&_-fT1s}#7+DJ?}_;BXXMq;YKM=*yr5>pHQ3+B*9V(>nVoRQ3-jl|3cKb|?Xk(kBc zCoqRL5_2v1iOivm#NZtrIir|E8;My1eiCzNBQcx7M>B^u5_32B$;_dR#QY9?40C8B zF?gp|&RFKqMq-`;AIBWpNX#qXzhn+=BnExI=bXYE+DObE@Kc#X8;SV@{50m!Mq<7M zKb<+Wk(hsik7o{TBqpU5@iUl18;NNJekOBhBQbbaNzMf3&_-gqflp)(Z6u~AcsX-u zBQbg4lbAyriRlMk!5rF1%(38;nL`_i83ta-9NI|CDDWxFp^e0x3Vs%IXd^M@;8U4H z8;Pj`uVM~uBnIzV$vK-jw2_z#z|Ub0Z6pToW67yz4s9f65%@Ib&_-e!z-yR88;SWf z_;lvbMq<{2&tML1BxV!%xy+%B#B2kvWe#m5<{|Kz%%P3MJO*CJ9NI|CbKtX>LmP>C z6?`^xXd^LigP+G7+DOcY;B%Nm8;SW8{CwunMq>7XU%(vNNX+-(zhVw;BqpUb@e7$l z8;NNHei3tMBQfaHGUsCE&_-gi!7pJBZ6xMU@VU&Pjl>j!&tnd4B<4u)OPNC(i75r2 z&m7uF%<i0)82DXd^Mxz%OSGZ6szk_!Z2djl^6G zzKA)rk(he$#mu3N#4HBCk~y@Im}|kWVh(L2=0@-(%%P3MtOdWCIkb_O&EQL!LmP?N z27V25Xd^KXf-hqZZ6szFcms22BQei_U&|cYNX#qX%b7zPiFp%z1#@U4F?+zTV-9U3 z<}>h>%%P3Md~y-_(g zGlw=3b13*N%%P3M6oRi}4s9f6Ao#7!p^d~00bk7=+DObu@HNb#jl_%wU&|cYNX%*A z>zG3uiKzfz&m7uF%rx-Zm_r+hnFYRqIkb_Oi@|Sa4s9gna`27Jp^d~`1HOqlw2_$W z!8bF9HWITM{0`>OMq)OC-^m=>NX%C7-!O+Z67xIoEzF^f#OwmUi#fEBm}kK6W)5v6 z<|Xj0%%P3MyaB$AIkb_Ocfs#r4s9gnui*DGhc*)P1^9i;p^e0R2mV{;&_-fn>BR46 z4s9f+HTVO}p^e0}2mc*&Xd^M*z#n7|Z6u}__(RO0jl>)QzMVO=k(mDA4>N~05;F*V z2Xkm6F~h+hVGeC1W;FOt=FmoBP6dCIIkb_ON#MJfLmP=X2YfekXd^Lo;EyqfHWG6Y z_~Xo>jl?Vje}Xx*k(jH&pJWbgBxX7IQ_P`_#M})2G;?SpG3&vfVGeC1=1%ZunL`_i zxexp~=FmoBc7Q+69NI|CW9E{Mf059=ga>&u&!0veG}3onEq_l{RaZHuF37XJRp(8bIWx$k zKEuoNK(!C_D#~vHXUtMsI~@aSaDpd_kd7nlNb+WktE`<_HN8fm4F2zXUZ;u6Yuf$# z4^iraohLZ#$cARHa?-4`CX`Q_R9iW_Q7(EPX#}{FN*#i#DafmsK5behb-A8bGrg`d z@3<4j=GvY-8Vy!WonSTsQ) zpEUXmrH?xwl!x(ac?(ISJgw*T2bjKJ zOPrR!h2rdh^o4}-kap3>U2a?G7>4a$lg8n)vmZBEgXml1aG6i*2-zs_Or3cr50@b^ zObLCEj-vTJq%F@c4HC|85*6l_*SZ5eH|hYD?Yy|<*im8yk)(0yB;4^%ru!&LXnjFF zXVC|F7|i(P^$3D&;+nL)?p}Foe^1W{Q4q>&M;|S(Sjx+n0`8&m#?y60O(+kaW4*Wx zT3H5aki;Nvn8az;FWi+%;*fX755&Dp#}iE`3+WURnAQ>)N}tetY|G{O=uBScqsA*Q zyM*4)g#jcX4ynp3Z&f5><4`YM=%eNRisEQpo`GSIwvl^}R}c|JYze0a*b?T7bWzrEto$AZ2KubI-YaN%FK zro5Vz_3EA=t3&%>Q?u8`H>3pJvYG`gE8cr8D7>ug@}ja2lH#jAppyPYNv8$hk>E77 zk&H;s)>*M=>+}u>wa$o)rL8QfIEc0$o;5J_$kZg-n4;~2;aSnvuvFVUDDRlmRFbj0 zAS05QQqu9d!JRm}SEpk;(GD+<%Jlf~EaWtc1xXz^bT~FS>~665=&!9SXREc8|Ja4! zk91O-m|bwu!IB%PvO~vkyEv!FDfmvx2=_Yz+GF@;97Flms%!dPQC(hhR`zT@r^qfS z%*)R!$UcO+VV;vcxO`@1_Q}(y)SXveTbXsIvFuIBLn#ks1RKD~P8_(@eYG^5jIR8?2jj;GU|Irh(lf`0i{ z?J!O1VO-1!A zs?JGObu)jy>K`92Y!cq`4ubOP>bmo1RL;zsif!Ln{8*urlLEY`g(@>65^LQu8ll}+ zBo^Us?8+jML`b_xET$ouDN*d|h{$*6lqhb~ip08TPI^i-nM0Jc*O(qkWN03f-G5>q z5v4azL|P@a#{LwCQ8x{q7r8uXa2iepel%~;FDSs)J-up&WO90}c6L!=RpE^M3DfXr z3jS2!&m1i9Y2^i>oRp9iBr&Q+iRv*-$-&@qCHraZbaD$0t8E357dZn;o@hi4sa*cJ)kH@^s8_Qq-%BXh0`pPKw&KG6AAN zqGmy(bk>HtNl>nTclr`_hauY8^ADWUa7qbKSML1x)-3y-V$Z)yLY5L>eNLqGU=*1F zAxIA3%m`Q&pLfVubT&mK3w+@!k_r{Q=2e*>G-|<^W-{bg)Lk*D}JWGGw`Tqm? zN04Pxx_zIPTOVH#e@=37))Z~D$+QS?j{KUFA4(F@K(_{_QG1q~h=>Lqf`g?zOj9OJ zUvKf4hD@w4&R#HYWPY}L{K^|ScyPf4bO?&?V(lb!Qsr4yHRwB+I%D-Zmi7DErXc)a zoC5|1y9FLla+R*rqmke>91680NIjFonJ zBKYA?3FX8c-Agp4Cw>SbcsiA1uMxoy0ZNF89BQRqk0Xb%ZV!GqYizHP-mG(=>9+|I zMiY_WKA7H!%-2!SN6;TiL+BHc!wA;FNC7DKR~9c!=w8Cd5Q@mmpAIeag6%q9%<<4V zFO`Vt!+ZvGU)FWdIOUY2?4qSFV&_-fbfoE|! z(E7dvTvpTfB|z)@67B}a4`xcx`o08Q*3tJRKK*=2)2wD@hp<_@iAFo=^?+2-3bb5hvtV4E$W z4-(!hgWU<%9dLZG?GC4pTi#Ie*xzW<@=6J~<;|t?CK5w=m=-PX6w=r~MCpUHls?GQ zq`HED&J*$w=QMeQd1n%L$NLVA7wamzNFPm_H;;g7UR?&hQNM4i1Z6g4!=?z_=0yOVQ@~V-AIJsPCBQvZLxUSH?dVWjJ$bn$9{BNj`nI6 z#PfE29q+yE>&S?BkN3a(=d7&7A9Q}CvSD%TU$<9gT^W0AWLf>xtgC+8Eq(a1SUh{% z{@3C!vL%X5$HsWpjGh^{X*6z1$gY*J~hKJe*4N1xSv!W%Z zH$-P#o7H;8-iFpYPR?qReNsc4cOGmAUihq4WkZWsPRyvxYVpAZdlsdpcTS4-O#XI$ z&jtINFF$YjJ-c+;5sP8$uqXp%p-hxbsu`351zHj96 zwu6fiP{E7oL7wij7@5XzOiH2dQnFMQ(b! zX!h*L(nn&)9lh$B$642xUAZ%D(AJcDOSUdtAKg^{%FStyY>koQlAbJB6*(?#_{yEF zmZU#2W>93PJH|1OEWfI48ApzM(#>hR`;;=Bnym{z;IdO*yE*OIF}szIe730msi5uH zjFd4iF7CRo8uK^mr1@rd^NwSSn%^5dup+D3LG4$ri1w;)K7Q={9%aqbPyMR>)LqA1 z_f`Azb`>>WLCZJReEg}C&qzPxtM*Uq27hh0S&?iy;5hIfK4h@#XI7u}75+q;V{6h3M_e^T*C9yYQqQ!@pWlxtrtL z?LvH3%ClAdPr7jUqwvR{GrA3H;bNM4! zr=mS$0j(L|Rb<^m{WjHa8;dD_^otgMJmZ4jsZK6dZ)*Y3S>m~NWv!2v&T zXzj5(_UI5Y$?ND(EQp;T10=P#Cs@}e;XRvl^^QJCncNI-br$mm@V41DW;1jGwSHl#oTvoR638~^**YeLqiK-b=5q2G zR|DEPZI;Ae$%#3Q1(`TIis7dcQtZgAwlTctAP8_HP^>@KFG`OMNVDAPf$2ePj}DWU zl@>cRk$1Q=Oz-3br^n4R?Z5}y0{}NOhMmZR?W<+vuY|QXV08Ey|yl4am_~0dE9F? zg+WztcEHztXxec9%Z%ant9%aK?Qwo^L4Z*WB2!BD$2(d$HjNBem zUQuzv^xA1b)y(7RQc~U2@|uFe#57e*Ew2q`(UkS4V*m3CMR-L>G^nIC<$o>|o&O(P zC@Q4OMzq!G+Ci&+!WH!8%yL@5tb*jIUQaqC5{tH!OF|u@QQB2TqL<)rtX1@4``sa$ zWWS^0J4I8hZ_m-Ooaij;lk{RyBqiD!gg&?~6^`oYNUU8;9#}45Wy`2ibx^uG#Und# zPFESGvqYxKWObD?!?L=j_#{QIYaJ`Y9;)LVCRfLfmhU$5-AcY`NLw|rp3%6S*?b)` zPa;a?J6pc!|m1T0O4$&kstOd=X zvG9`4lVQUMt*3A-VU7%&!b8Zo^K=??+&a9DsiQ9S3|Uj!%b0bAK2}!f?ov`;E$L_( zQ><8Wx@k^#t=}-VDH)v-y~x&Mv5arH%&Gb&<7-;R@PAi^mf2m#@gvjIJ0*IaOiy$B zt-~I0dT8vt3+OIue^`34)=XF<6wAAf1D5t5%w1S|(_x#E5q@2b@TN$8^Kkjs+IncE zOO^H*rIv)dGFo?@?$$4&N{`W4Y-$qlht@&IH}z&{)&k9f)+J;g&aB(fqm=D_dTldPCZ;TiH_n zJ6?l-pN@Ze4IbBrIPo>#ZffbL3r@|A=MVFIqCPo3&&!ufG(0RUcLs;ZCEIYePa69_YX(H9&Npx*i%zjBVVFc%1g{2$0z2G{V*>+ z(bmoRiTP(g+KV6OdHa*iGLB#2<=^0W{XwYnzuL=R>-i0yf64P`XXV7>e@m^@$NQfD z*z=!x{&UZN?fLzl|K9Uq!>Np)e~KfBr>|}J1B`t~&v*8G56>IV>vei9Ki|tQ^n72> z5A=MA=Sw}0dssQ~^pEiTD9?}aJnr>&hsQmvt{?CDiJqVA`LjJ=?Rnh$>Xu*Y`8vo@bVw@{0`6W^!#I^MgG<)bk@ee}d;ndwz`P z$9et~&!6V`Gd(}a^HV%O)$`S!pW*pAp1;8J7kYky=h2UcyL^^-ewpW2c>V^@-|YES zo?q?xwVq$^`Hi06;rXW=uXBR$|6BJ+y62xbKX#`NK9Snl^XJ6*V>^34fqOn3r{K2X z0Lj7Oq+bnvioJI%+1cOVda-+a#<^t=Cx0aQ6C6L5ynFmVlYG9De-?T7_=4X@?)l;z z@;jaSpGQ8Cl7p+sWQHKw4(ra(O_bN($;Y{*TYsC$yY;b^d|xO20n(U9PRYSemG(O& zc!9p%^X1pccL`%sf_F&wcKk=A-R1QKd3S!lBOhJ|qy1N+?omj3kVSPj0Me#(dWWHE zd}jrH$xjSXdQjFVzjZK{{6;5#GWowd9_KzEIDP^7ZH{js|C-}*aV2rSogUmp{yiuE z5%Rxt{0rpob37hLe$??eR=Ue)KlwuXa7qtahU)@Ii=YGfVNQN8^1UP)`8XCN&PUUO zQu25*P;%{72+*KZp+hCHXCCY5n*od8{*>(&=Iqt?$n8UCH-!d@lLK`DS`R zEviA{d^$Zip8Omqp4ugYOC3LrJe~>D@m)k7mj%>cMgABulq@mZ3E1CeLnTxJOyKvJ3O23%JmyBwHDJw~5}~)2vs7=&@(I zl{sH-Y0jU3TY+(FGww3RH*PP+cYy-j-fV$J(q$PTxK-F@;U;7&^cw@&n~m*_(ukuQ zrR7U*n?@-F?Re-LMbOs(p@>ZsAcN2~D;`FI3oxY)gu4KuwNEAmI<%|KzJfo0_=^n&aP z=1gmWHwAVja`Fqk887r^r_fum3cW>H=q;>5Z+wOROnUi6Md7x~7hfx<&8$pZkPRl_ z8G?#)CR9v4XTn*t%4;VF2fo7V#!e}(s>UygAn0r_m$NRC-9}6L#(Uq9pp_p*I;$1$ z_@I@eZL)L+{h>61KBbRXX>ZavvJJ;$m!$D1`Y7Sh%5jdM(*v!1B59o-XyqG7XQp%c z(8{;a@k7T8t$Z749WS(UoQv7pP^b)OMimNYt&Wz*@W&TJPrIg z=FrM<&KQ55IkfVQ;4d(TR*v&z9oG?vkzt`V26YpEk^WGER*rM%xHMWqD?b+e_w@tPmHtqIR(=ubxSZ@kE1w6B z6J1Ks%CQdUxNab=Lpp|bYt>eOKr{jXw7_46LKhPga z(8_^y*f&YXvBFY<)|fw&)?uNQ|Ha|Z%0C5{>4a8}{YKURNx#h+=d_V`SYut%=VOLJV*)9iNT0~NR@(PkMBZav0scPg>Cke5 z|10P{%vVBx$odxOk65pV#*9+BpFXt4`bu|6F7Yt{pyzhONb8Y84M2Krmp_${pSRRJyYQVabZ$KZTA@=w-RKx1Ml zErtFUYy2jN&_AU6py`?;(xcGWiBftVS|0L4n`9)JIbMmR%keAd81rZn=@izTpqsHq zdt9VBYm}?Y)b2lJnPUH`%fha=vToz|p+3)q-)@l(D1r6^q;py%v_8*-`ORs`99o}e z!d-DWsm!7Ec_uu2l!Fmcg4TXyZvk(`92)OtM!F5WbwV47`3-m*=Fs|F6zW!=i-OkY zq8vK_fa$cW{g4XAv@O*&wX9KOzMSTL^k;{SB=b6y+?+b-K+h-LTmr8Sf<+lE40B5gA&?swf|RW?f$!R4s9d`ZKgRn%%P3M30BQd+d z3z+M^Ikfiw+8O*P=Fr;zYYzC)%%P3M90q<2b7<}V zwGjMR=Fr;z>yh9k%%Qda*CF78m_r+h`33l3=FmoB#()oD4s9d`?JPN^%%P3MRDzc= zhc*&}c9@)@%%P3M%mzP>IkfiwdI>mA7AQd*iMb4XICE$tF-yTmFo!l0b3OPkm_r+h zxfOgQb7&(mw}T(g9NI|C-QXuMhc*)P0QiZ_p^e1s1Rup5+DOdP;3qMMHWKrD@X^en zwIA6(fuGDATKkdx0DKH{XzfS#Q}D6Op|u~`ecw%^}hGPnKyN=&n4ia&)p zwDu#5?(4N5S!g3M=w4p?k%iWNWV?f(&gDRBKeC5{k7o|8{mABnpTQhj`;qMnekOBh z?ML<)@CnSJwIA8zz$Y??)_!D91TSX}Z6xNG;FFj`8;O|+UcnsNNX%65$;_dR#LNJ% zWDc$U$j$+u!W>%rk(~>E7ISFrNA_~?sm!6ZAK7cbtC&M;Ke9J~pUoWFNX%;RbC^SG zKeC&^tC>SCB;x#5@l^gE_R3nAgG2We#m5<{j`_ z=FmoBJ_4V~99sL4-3wmF99sL4{Th50b7&(mkr?sW%%P3Mv;;qoIkfg8n*~0HIkb@& z+rRJm%x(X^5`#W{b1q;GZ6u~QIDWWLg4TXyi@`5s4z2yj9tD08b7<{HwiNtg=Fr-Y z?D622Fo)KDWXFNeWe%i3trD0 zTKkc`9DE^jXzfS#8t}`QLu)^>H-KNx99sL4T@8K(b7<{Hb`$s_=FmoBwt+8Z4s9gn zA@D1iLmP>C4E!qQ&_-gO17E@%TKkcG75r-E(Atme+u%!?LmP?t2>cr6&_-hZ4!(>z zw2_$o;0?^7jl?9S5Wki=w2_#W;LDjq8;QvTU%?z&`;k2e{5s~)+K+4x@RiJ=wIA7B z@avgFYd^Am!Eay=t^LRz3;t{7(AtmeFz_3hLu)^>qrh)s4z2yjo&tU|b7<{Hb|UyK z%%Qa(*{R^Gm_utnvNOPMWe#m5W)Apj=FmoB=7Fza4s9f65%^l>&_-hDEqGx+ve4R( z>`mb7IUZX3kzEgd8*^ywNA@@18<<0DKeG3M-_9J`NX$dv8<|5JiFp)!6LV-IF;9bU zW)7|W$o?Mu4(8BCV*UVrCv#}+NA_*--!O;Peq=uY-@+W)NX%#8cQJ?7eq_G_zneL< z_9Oc(_(RO0wIA7zgd*FScY&rnq-^@c+d{F{_h*bEA9>i~U<`D7)~C={d}~7Y68^vU z{*1x{yg!4kbKvd1&NVvw7UJ-_$p4!!(l`k9Ngr7RK`~cn_%}&Zclt(LRJ_U=J|x~rP)Z!b zl+XuhFO^qH1EM_CftH7L%bkx=G(+#}L`fWd1=aM?d51^bcMy^^?=14}c#97Sf@`Tf zjJJj4VLouGqQj63M$$mvLP*j)912w<>o_{T^m3+8^Xe(j9q$u|1;HvAFUF_yi`~2q zLo%po#?uCkqAmA^67)Lkji^r3n34Q&o=qiIP{|`KgB605r^G~ zGk9<{D$Pln_YyJH$hL7bZcUo^ECF{uR?>XD-iGHnOY%@Z@6ktxAsH-9Z~R^pl!5dX zecbY99GzHC+eseE+e;r>MP|^KO?aRGNHz4WO+@YhAE4<(`rjXJs`zZ|i-{ zng?H9w`bR;`Y$)6?fhmo$5)JfC1|KG`}WWc6=&QNw7P0Jm2+=KKZ-rC!z&c~0mWv$ zQdaWc=H0d#F}ngp2ipZni}FD4w#~v0&L(-cZO4A(!i^)zT8a39um6(|Xw&-kGd`d_ zt76FXn#oml^j`l)&u8n(?LIJF6ilfsud6Mm9q)fUgQ>Ko{?Dsz^)=TDV&d&@7V4qkn?D2bq6Z>qnGQJ+3FZ6t0&+ASZXP20_QZIj)=TGqbXwRSN z`AMFi>iKHV*El|uB>r#RU+ykn)Q>xTiFE-}+Aioyf3qQNN(p+>cj8f9{irZ?-hsqcS8-4Y|ka#`or&zoncIRkV+?i zZhdwj|GblL^yJX87C{mD*PZ-OlK3h2%MO03|dF>HBp% zL*ulUQVxA0{jId^77^K)F&e$+=_qK~$qa!$lH*4}Ba;&5C61j7CFowlvG0f+0~!RU z(?{nKm%QS~f(G`z-#Raq~_1}bkUF_edC(eh3q?as#*$)hiu5QkJjADt(7#L4CZ8|L9$*&T0xnrN)67%zUQ z>3A{CZh23WzmPtfG;baO)yO-J%v=abnulTC@z&9J_Y%W+QExilWu$c&lJ?mp#AwpI zs|dK|O>UJ~9$0=_-YU`o6pP=CQ_Yq=-X$g+6Y1YpMNq zA~BSQ-Mg0eA!*t+X3&>SW4!WoT=;e-J6J_=bEy#Q?uXI`<)zaH>yB<~B!ldh?sKY% zY4_V)lWE{@Fn}Z-uQW>)|I{|u;-daP`Q61YtmkRveZnF18>&m#Usyaqo9ihz2O0O# ztj%AW91LE3%%TSt-+NWzRe4uzTomtd?RQzR1>aD+VYH#ef^QOUF1BwJj>Q7X-G|() zv<3UFqq+KUF3o*eJg4-lhKu*NX{cZFudLs;3+|(O%$gqyw#F)hD+g_@f1u>>h4*aV z)aTyqMGJ2aGGb{v7kBzHKXO831l_HX(vTJ;ZK{sN8=9AX(~wm9-KNW)jBScO6T4$* z`^z4yUofQkkc!in2TKPvUXM^NhGs?EcHq%so zwmc(tX+~<-FY^zNJ(}EnP*H5`pwzswNNPMKLhl-mthsvm_YJYVCl;++7u;U9b=~gZ z_^T=oie(m2Uh-XiR#Pp^Z53b)4SgzuqJ?(^u~^z_DlhG-Rezt=P#-IKb$P2P)V4hr zF%>oU26vSer92p<53X5pO)M!h^`=|jOzxexxd)m=7M?P(!WqA7fvbha?%YvE}u~^&H zi@SW8R?(tGz$I&aMc1{kZ~J{O{hq@9H`9`8Q!%){O{dDN*q66dj2PQ4tNx6j>(F^+ zi5jjiYezM{?!LveC{SmM3sO=S+?Ca{-Igpm%{u9-PJc!%M#>^1w$|SfT-p8l-Sc~< z1r(7~~Edbf4#%Lx@Hj_saRe{|3j zQ|!bq?oMrrYl>nIP&ZjumA!c@&1WZ?&s5B3tc1o9U9+H7P*Z<;a;(H;q-3m_*D8q9 zXALcFEb&;`noI8pKC3_d(sd;p?%Hr`)hrrr$GGK(&RcwXpBN%+Bn^>OKP~9gsJ_-O zp`qv95yYv4H#gWJCt}Fd@qJ=7^?yu??Py%Ti2tnqk8{_RWUQ9C8ay^b$ARfwbE!M6 zG}TEw)tJk_{7&bxXkpo)`m&3DNAudUV({h?DeD(wyZ_X(zFFV6tlb+bG2e5?E$==r z^|U^1upYQ;MpperK|7jvT~3&j`20I)dhZCX*lLH()?sxTP|G@8VjYt@n8&q!`CS=X z&&8Nmja%MxUh3(6+B6=zX^m4&eyr+#@E*Tfn3YZ+AcCy#Bk)EX~+tU*}I_g(Si z+B0S~&>HgUa@w{>-8wJz#j$NVd2_vfNn*Xp$*3<|)8?M<tZ4opH*dm#$~I^A?XQi>;}rwUIt3C9D4Uz#ksd{LQ_IVN!othBYi2+tE

*Ow* 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 X@2k>Ey*&IQa$~Qls>7u-`wIRS%@6Fe literal 0 HcmV?d00001 diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_driver.c b/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_driver.c new file mode 100644 index 000000000..5164e20fe --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_driver.c @@ -0,0 +1,1621 @@ +/********************************************************************************************************* + * Copyright(c) 2022, vcare Corporation. All rights reserved. + ********************************************************************************************************** + * @file vc9202_driver.c + * @brief vc9202 driver layer interface implementation. + * @details + * @author + * @date + * @version + *********************************************************************************************************/ +#include "vc9202_common.h" +#include "vc9202_driver.h" +#include "BIODetect_V15_1.h" + +/* static wear status declaration */ +static int bio_result = VC9202_BIO_STA_NONE; +static WearStatusTypeDef alg_wear_status = WEAR_STA_HOLD; +static unsigned int gsnesor_axis_diff = 0; +static VC9202WearInfoTypeDef dev9202_wear = {0}; +/* static adjust data declaration */ +static VC9202AdjustSlotTypeDef dev9202_adjust[3] = {0}; +static unsigned char ppg_mark[128 / 2] = {0}; +/* static device data declaration */ +static VC9202DrvInfoTypeDef dev9202_info = {0}; +/* static oscclk data declaration */ +static VC9202OscClkTypeDef dev9202_clk = {0}; + +static int Bio_Max_Value = 0; +static int Bio_Max_Diff_Value = 0; +static int Bio_Min_Diff_Value = 0; +static int Bio_Old_Value = 0; +static uint8_t Bio_cnt = 0; +static uint8_t Bio_HOLD_cnt = 0; +static uint8_t BIO_HOLD_DROP_FLAG = 2; +/* global test infomation declaration */ +extern VC9202TestInfoTypeDef vc9202_test_info; +extern void vc9202_test_handler(SamplingDataTypeDef *pResult, VC9202TestInfoTypeDef *ptest_info); +extern void vc9202_test_init(VC9202TestInfoTypeDef *ptest_info); +/* res-level tab */ +const static VC9202ResTableTypeDef res_table[22] = { + /* level: 0 */ {0, 0, 1}, + /* level: 1 */ {1, 0, 1}, + /* level: 2 */ {2, 0, 1}, + /* level: 3 */ {3, 0, 1}, + /* level: 4 */ {4, 0, 1}, + /* level: 5 */ {5, 0, 1}, + /* level: 6 */ {0, 0, 0}, + /* level: 7 */ {1, 0, 0}, + /* level: 8 */ {2, 0, 0}, + /* level: 9 */ {3, 0, 0}, + /* level:10 */ {4, 0, 0}, + /* level:11 */ {5, 0, 0}, + /* level:12 */ {6, 0, 0}, + /* level:13 */ {7, 0, 0}, + /* level:14 */ {6, 1, 0}, /* +2 */ + /* level:15 */ {7, 1, 0}, + /* level:16 */ {6, 2, 0}, /* +4 */ + /* level:17 */ {7, 2, 0}, + /* level:18 */ {6, 3, 0}, /* +6 */ + /* level:19 */ {7, 3, 0}, + /* level:20 */ {6, 4, 0}, /* +8 */ + /* level:21 */ {7, 4, 0}, +}; // 21-5 = 16 + +void vc9202_drv_set_gsensor_actuating_quantity(unsigned int axis_diff) +{ + gsnesor_axis_diff = axis_diff; +} + +void vc9202_drv_set_algo_wear_status(WearStatusTypeDef status) +{ + alg_wear_status = status; +} + +/******************************************************************************************************** + * 时钟校准部分 + ********************************************************************************************************/ +static void vc9202_drv_clear_clk(void) +{ + dev9202_clk.osc_gap_cnt = 0; + dev9202_clk.count_index = 0; + dev9202_clk.last_clk_count = 0; + dev9202_info.osc_clk_status = 0; + + dev9202_clk.last_clk_check = 0; +} +/** + * @brief 时钟校验,与原来的VC9201系列一致 + * @param 无 + * @retval + 0 校验正常通过 + 1 vc9202计数当次就溢出 + 2 mcu tick或vc9202后两次中断的时候有计数溢出 + 3 频率计算没有通过 + */ +static unsigned char vc9202_drv_calibration_osc_clk(void) +{ + unsigned char count_vaild = 1; + unsigned char i = 0; + unsigned char osc_buf[4] = {0}; + unsigned int osc_tick[2] = {0}; + unsigned int clock_tick[2] = {0}; + unsigned short int calc_div[2] = {0}; + unsigned short int mean_div = 0; + unsigned char reg_div1[2] = {0}; + /* 判断是否需要校准时钟,如不需则直接返回 */ + unsigned int sec_gap_cnt = VC9202_CFG_CLK_CHECK_GAPTIME * 1000 / dev9202_info.fifo_time; + if (dev9202_info.osc_clk_status && dev9202_clk.osc_gap_cnt++ <= sec_gap_cnt) + { + return 0; + } + /* 获取时钟计数 */ + clock_tick[0] = usr_get_clock_ticks(); + usr_iic_read_bytes(0x07, osc_buf, 4); + clock_tick[1] = usr_get_clock_ticks(); + osc_tick[0] = (osc_buf[0] << 8) + osc_buf[1]; + osc_tick[1] = (osc_buf[2] << 8) + osc_buf[3]; + if ((osc_tick[0] > osc_tick[1]) || (clock_tick[0] > clock_tick[1])) + { + dev9202_clk.count_index = 0; + return 1; + } + /* 存入校准缓存 */ + dev9202_clk.osc_count[dev9202_clk.count_index * 2] = osc_tick[0]; + dev9202_clk.osc_count[dev9202_clk.count_index * 2 + 1] = osc_tick[1]; + dev9202_clk.clk_count[dev9202_clk.count_index * 2] = clock_tick[0]; + dev9202_clk.clk_count[dev9202_clk.count_index * 2 + 1] = clock_tick[1]; + dev9202_clk.count_index++; + if (dev9202_clk.count_index >= 3) + { + dev9202_clk.count_index = 0; + for (i = 0; i < 2; i++) + { + count_vaild = count_vaild && (ABS((dev9202_clk.clk_count[2 * i + 2] - dev9202_clk.clk_count[2 * i]), (dev9202_clk.clk_count[2 * i + 3] - dev9202_clk.clk_count[2 * i + 1])) < VC9202_CFG_CLK_CHECK_DEVIATION); + count_vaild = count_vaild && (ABS((dev9202_clk.osc_count[2 * i + 2] - dev9202_clk.osc_count[2 * i]), (dev9202_clk.osc_count[2 * i + 3] - dev9202_clk.osc_count[2 * i + 1])) < VC9202_CFG_CLK_CHECK_DEVIATION); + count_vaild = count_vaild && (!(dev9202_clk.clk_count[2 * i + 2] == dev9202_clk.clk_count[2 * i])); /* avoid divisor==0, diffCountIsOk set false */ + } + if (count_vaild) + { + for (i = 0; i < 2; i++) + { + calc_div[i] = ((unsigned int)dev9202_clk.osc_count[2 * i + 2] - dev9202_clk.osc_count[2 * i]) * VC9202_CFG_CLK_CHECK_FREQUENCY / dev9202_info.work_frequency / ((unsigned int)dev9202_clk.clk_count[2 * i + 2] - dev9202_clk.clk_count[2 * i]) - 1; + if (calc_div[i] < (20000 / dev9202_info.work_frequency * 0.75f) || calc_div[i] > (20000 / dev9202_info.work_frequency * 1.25f)) + { + return 3; + } + } + /* 比较两次计算偏差是否合理 */ + if ((calc_div[1] - calc_div[0] <= 20) || (calc_div[0] - calc_div[1] <= 20)) + { + mean_div = (calc_div[1] + calc_div[0]) / 2; + reg_div1[0] = mean_div >> 8; + reg_div1[1] = mean_div; + usr_iic_write_bytes(0x14, reg_div1, 2); + dev9202_clk.count_index = 0; + dev9202_clk.osc_gap_cnt = 0; + dev9202_info.osc_clk_status = 1; + return 0; + } + } + } + return 2; +} +/******************************************************************************************************** + * 摘配带判断部分 + ********************************************************************************************************/ +/** + * @brief 佩戴检测,bio检测计算(内部阅读:f_BIOdetect,在BIO+PS均由佩戴产生的边缘时输出2佩戴;摘下时只依赖PS边缘) + * @param + bio_value bio采样值 + ps_value ps采样值 + * @retval bio结果 + */ +static int vc9202_drv_calc_biology_sense(unsigned short int bio_value, unsigned char ps_value, unsigned char env) +{ + static unsigned char calc_ps = 0; + static unsigned short int calc_bio = 0; + static int Bio_Diff_Value = 0; + int ret = 0; + if (bio_value) + { + calc_bio = bio_value; + Bio_Diff_Value = abs(Bio_Old_Value - calc_bio); + Bio_Old_Value = calc_bio; + } + if (ps_value) + { + calc_ps = ps_value; + } + if ((calc_ps < 100) && (env > 4)) + { + if (Bio_Diff_Value < VC9202_CFG_WEAR_PARAM_BIOIN * 0.2) + { + Bio_Max_Value = calc_bio; + Bio_Max_Diff_Value = Bio_Max_Value - (VC9202_CFG_WEAR_PARAM_BIOIN * 0.8); + Bio_Min_Diff_Value = Bio_Max_Value - (VC9202_CFG_WEAR_PARAM_BIOIN * 1.5); + } + } + if (Bio_Max_Diff_Value) + { + if ((calc_bio > Bio_Max_Diff_Value) && (Bio_Diff_Value < VC9202_CFG_WEAR_PARAM_BIOIN * 0.2)) + { + Bio_HOLD_cnt++; + if (Bio_HOLD_cnt > 4) + { + BIO_HOLD_DROP_FLAG = 1; + } + } + else if (calc_bio < Bio_Min_Diff_Value) + { + Bio_cnt++; + if (Bio_cnt > 2) + { + BIO_HOLD_DROP_FLAG = 2; + } + } + else + { + Bio_cnt = 0; + Bio_HOLD_cnt = 0; + BIO_HOLD_DROP_FLAG = 0; + } + } + uint32_t tm; + uapi_get_timestampe_ms(&tm); + printf("before f_BIOdetect bio_value :%d , ps_value : %d , env : %d , tm : %d\n",bio_value,ps_value,env,tm); + printf("before f_BIOdetect calc_bio :%d , calc_ps : %d , VC9202_CFG_WEAR_PARAM_PS : %d ,tm : %d , ret : %d\n",calc_bio,calc_ps,VC9202_CFG_WEAR_PARAM_PS,tm,ret); + ret = f_BIOdetect(calc_bio, calc_ps, VC9202_CFG_WEAR_PARAM_PS); + printf("after f_BIOdetect calc_bio :%d , calc_ps : %d , VC9202_CFG_WEAR_PARAM_PS : %d ,tm : %d , ret : %d\n",calc_bio,calc_ps,VC9202_CFG_WEAR_PARAM_PS,tm,ret); + printf("after f_BIOdetect bio_value :%d , ps_value : %d , env : %d , tm : %d\n",bio_value,ps_value,env,tm); + if (VC9202_BIO_STA_HOLD == ret) + { + vc9202_drv_set_algo_wear_status(WEAR_STA_HOLD); /* bio */ + bio_result = VC9202_BIO_STA_HOLD; + } + else if (VC9202_BIO_STA_DROP == ret) + { + bio_result = VC9202_BIO_STA_DROP; + } + return bio_result; +} +/** + * @brief 佩戴检测过程及状态 + * @param *pwear-佩戴结构体,env-环境光,ps-ps值,bio-活体值 + * @retval 识别改变结果 + */ +static VC9202WearChangeTypeDef vc9202_drv_wear_status_identify(VC9202WearInfoTypeDef *pwear, unsigned char env, unsigned char ps, unsigned short int bio) +{ + printf("vc9202_drv_wear_status_identify bio : %d \n",bio); + VC9202WearChangeTypeDef ret_change = WEAR_NO_CHANGE; + int ret_bio_algo = VC9202_BIO_STA_NONE; + /* bio算法运算 */ + if (dev9202_info.enable_bio) + { + ret_bio_algo = vc9202_drv_calc_biology_sense(bio, ps, env); + } + else + { + ret_bio_algo = VC9202_BIO_STA_HOLD; /* 20230614 因为下面的条件把(ret_bio_algo== )佩戴的条件删了,所以此处必须添加,不然在未开bio的模式下无法佩戴*/ + } +#if (FUNC_DISABLE == VC9202_CFG_WEAR_THRESHOLD_EN) + BIO_HOLD_DROP_FLAG = 2; +#endif +#if (FUNC_DISABLE == VC9202_CFG_WEAR_BIO_EN) + ret_bio_algo = VC9202_BIO_STA_NONE; + BIO_HOLD_DROP_FLAG = 2; +#endif +#if (FUNC_DISABLE == VC9202_CFG_WEAR_ALG_EN) + alg_wear_status = WEAR_STA_HOLD; +#endif +#if (FUNC_DISABLE == VC9202_CFG_WEAR_ACT_EN) + gsnesor_axis_diff = 1; +#endif + switch (pwear->wear_status) + { + case WEAR_STA_DROP: + printf("WEAR_STA_DROP ps : %d , env : %d , ret_bio_algo : %d , alg_wear_status : %d , gsnesor_axis_diff : %d , BIO_HOLD_DROP_FLAG : %d\n",ps,env,ret_bio_algo,alg_wear_status,gsnesor_axis_diff,BIO_HOLD_DROP_FLAG); + if ((ps >= VC9202_CFG_WEAR_PARAM_PS) && (env <= VC9202_CFG_WEAR_PARAM_ENVMIN) && (VC9202_BIO_STA_HOLD == ret_bio_algo) && + (WEAR_STA_HOLD == alg_wear_status) && (1 == gsnesor_axis_diff) && (BIO_HOLD_DROP_FLAG == 2)) + { + if (--pwear->wear_hold_cnt <= 0) + { + pwear->wear_hold_cnt = VC9202_CFG_WEAR_HOLD_CNT; + pwear->wear_drop_cnt = VC9202_CFG_WEAR_DROP_CNT; + ret_change = DROP_TO_HOLD; + } + } + else + { + pwear->wear_hold_cnt = VC9202_CFG_WEAR_HOLD_CNT; + } + break; + + case WEAR_STA_HOLD: + printf("WEAR_STA_HOLD ps : %d , env : %d , ret_bio_algo : %d , alg_wear_status : %d , BIO_HOLD_DROP_FLAG : %d\n",ps,env,ret_bio_algo,alg_wear_status,BIO_HOLD_DROP_FLAG); + if ((ps < VC9202_CFG_WEAR_PARAM_PS) || (env > VC9202_CFG_WEAR_PARAM_ENVMIN) || (VC9202_BIO_STA_DROP == ret_bio_algo) || (WEAR_STA_DROP == alg_wear_status) || (BIO_HOLD_DROP_FLAG == 1)) + { + if ((WEAR_STA_DROP == alg_wear_status) || (ret_bio_algo == VC9202_BIO_STA_DROP)) + { + pwear->wear_drop_cnt = 1; + } + if (--pwear->wear_drop_cnt <= 0) + { + pwear->wear_hold_cnt = VC9202_CFG_WEAR_HOLD_CNT; + pwear->wear_drop_cnt = VC9202_CFG_WEAR_DROP_CNT; + ret_change = HOLD_TO_DROP; + } + } + else + { + pwear->wear_drop_cnt = VC9202_CFG_WEAR_DROP_CNT; + } + break; + default: + break; + } + return ret_change; +} + +/** + * @brief 佩戴检测及通道的开关 + * @param 无 + * @retval 佩戴检测状态 + */ +WearStatusTypeDef vc9202_drv_get_wear_status(void) +{ + WearStatusTypeDef wear_status = dev9202_wear.last_status; /* last status */ + VC9202WearChangeTypeDef calc_status = WEAR_NO_CHANGE; + unsigned char event = 0; + unsigned char en_int = 0; + unsigned char env_value = 0, ps_value = 0; + unsigned char bio_buffer[4] = {0}; + unsigned short int bio_value = 0; + unsigned char wear_interrupt = 0; + unsigned char slot_index = 0; +#if (VC9202_CFG_WEAR_DETECTION != FUNC_ENABLE) + return WEAR_IS_HOLD; +#endif + usr_iic_read_bytes(0x11, &en_int, 1); +#if 0 + usr_iic_read_bytes(0x02, &event, 1); +#else + event = dev9202_info.int_event; +#endif +#if (FUNC_ENABLE == VC9202_CFG_DRV_BY_TIMER) /* 定时触发时,不看中断原因,可能被清掉 */ + wear_interrupt = 1; +#else + /* 使能了PsInt,且使能PS */ + if ((event & VC9202_EVENT_PS_MARK) && (en_int & (1 << 4))) + { + wear_interrupt = 1; + } + /* 未使能PsInt下,ps_ppg同频通过fifo中断去识别摘配带 */ + if (event & VC9202_EVENT_FIFO_MARK) + { + wear_interrupt = 1; + } +#endif + /* 非心率模式,就算使能了运动量,也没有去做处理,直接置1忽略 */ + if (dev9202_info.work_mode != WORK_MODE_HR && dev9202_info.work_mode != WORK_MODE_WEAR) + { + gsnesor_axis_diff = 1; + } + if (wear_interrupt) + { +/* 读取佩戴检测所需的数据 */ +#if 0 + usr_iic_read_bytes(0x43, &env_value, 1); + usr_iic_read_bytes(0x46, &ps_value, 1); + usr_iic_read_bytes(0x4B, bio_buffer, 4); + bio_value = (bio_buffer[2] << 8) + bio_buffer[3]; +#else + env_value = dev9202_wear.wear_env; + ps_value = dev9202_wear.wear_ps; + bio_value = dev9202_wear.wear_bio; +#endif + /* 根据数据得出佩戴状态 */ + calc_status = vc9202_drv_wear_status_identify(&dev9202_wear, env_value, ps_value, bio_value); + if (DROP_TO_HOLD == calc_status) + { + wear_status = WEAR_STA_HOLD; + dev9202_wear.wear_status = wear_status; + dev9202_wear.last_status = wear_status; + InitParamTypeDef init_param = {dev9202_info.fifo_time, dev9202_info.work_mode}; + vc9202_drv_reg_init(&init_param); + } + else if (HOLD_TO_DROP == calc_status) + { + wear_status = WEAR_STA_DROP; + dev9202_wear.wear_status = wear_status; + dev9202_wear.last_status = wear_status; + /* 关灯操作:获取那些ppg通道使能/记录当前使能的通道,然后关闭通道 */ + for (slot_index = 0; slot_index <= 1; slot_index++) + { + if (dev9202_info.enslot[slot_index]) + { + vc9202_drv_channel_switch(slot_index, 0); + } + } + } /* end of(HOLD_TO_DROP == calc_status) */ + } + return wear_status; +} + +/******************************************************************************************************** + * IC状态读取部分,包括IC异常状态处理,常规数据的读取,ENV、PPG调整部分 + ********************************************************************************************************/ +/** + * @brief 等效电阻等级转化为寄存器参数 + * @param level 等效电阻等级 + * @retval 寄存器参数 + */ +static VC9202ResLevelTypeDef vc9202_drv_res_level_to_register(unsigned char level) +{ + VC9202ResLevelTypeDef reg; + reg.res_val = res_table[level].res; + reg.low_res = res_table[level].low; + if (res_table[level].offset >= 4) + { + reg.offset = 0; + reg.offset3 = 1; + } + else + { + reg.offset = res_table[level].offset; + reg.offset3 = 0; + } + return reg; +} + +/** + * @brief 寄存器参数转化为等效电阻等级 + * @param 寄存器参数 + * @retval 等效电阻等级 + */ +static unsigned char vc9202_drv_res_register_to_level(VC9202ResLevelTypeDef *preg) +{ + unsigned char index = 0; + unsigned char level = 0; + VC9202ResTableTypeDef table = {0}; + if (preg->offset3 == 1) + { + table.offset = 4; + } + else + { + table.offset = preg->offset; + } + table.res = preg->res_val; + table.low = preg->low_res; + for (index = 0; index < sizeof(res_table) / sizeof(res_table[0]); index++) + { + if (res_table[index].res == table.res && res_table[index].offset == table.offset && res_table[index].low == table.low) + { + level = index; + break; + } + } + return level; +} + +/** + * @brief 设置通道电流参数 + * @param 无 + * @retval 无 + */ +static void vc9202_drv_set_ppgSlot_current(unsigned char slot_index, unsigned char cur) +{ + unsigned char reg = 0; + unsigned char addr = 0x17 + slot_index; + cur = (cur > 0x7f) ? 0x7f : cur; + usr_iic_read_bytes(addr, ®, 1); + reg &= (1 << 7); + reg |= cur; + usr_iic_write_bytes(addr, ®, 1); +} + +/** + * @brief 设置通道电阻参数 + * @param 设置的通道号,设定的值 + * @retval 无 + */ +static void vc9202_drv_set_ppgSlot_resistance(unsigned char slot_index, unsigned char level) +{ + unsigned char reg_addr = 0x1A + slot_index; + unsigned char res_reg = 0; + unsigned char res_ext[2] = {0}; + VC9202ResLevelTypeDef res = {0}; + level = (level > VC9202_MAX_RES_LEVEL) ? VC9202_MAX_RES_LEVEL : level; + res = vc9202_drv_res_level_to_register(level); + usr_iic_read_bytes(0x22, res_ext, 2); + switch (slot_index) + { + case 0: + res_ext[0] &= ~(3 << 4); /* offset */ + res_ext[0] |= (res.offset << 4); + res_ext[0] &= ~(1 << 0); /* lowres */ + res_ext[0] |= (res.low_res << 0); + res_ext[1] &= ~(1 << 0); + res_ext[1] |= (res.offset3 << 0); + break; + case 1: + res_ext[0] &= ~(3 << 6); /* offset */ + res_ext[0] |= (res.offset << 6); + res_ext[0] &= ~(1 << 1); /* lowres */ + res_ext[0] |= (res.low_res << 1); + res_ext[1] &= ~(1 << 2); + res_ext[1] |= (res.offset3 << 2); + break; + default: + break; + } + usr_iic_read_bytes(reg_addr, &res_reg, 1); + res_reg &= (0x8f); + res_reg |= (res.res_val << 4); + usr_iic_write_bytes(reg_addr, &res_reg, 1); + usr_iic_write_bytes(0x22, res_ext, 2); +} +/** + * @brief 读取PPG饱和中断时的采样值 + * @param enfifo-是否使能fifo,slot_num-通道数,slot_index-通道号, *pdata 数据指针 + * @retval 返回当前读取地址 + */ +static unsigned char vc9202_drv_adjust_ppg_read(unsigned char enfifo, unsigned char slot_num, unsigned char slot_index, unsigned int *pdata) +{ + unsigned char write_index = 0, fifo_addr = 0; + unsigned char fifo_buff[2] = {0}; + /* PPG IRQ addr */ + usr_iic_read_bytes(0x03, &write_index, 1); + fifo_addr = write_index - slot_num * 2 + slot_index * 2; + fifo_addr = (fifo_addr < 0x80) ? (fifo_addr + 0x80) : (fifo_addr); + fifo_addr = enfifo ? fifo_addr : 0x80 + slot_index * 2; + /* read PPG data */ + usr_iic_read_bytes(fifo_addr, fifo_buff, 2); + *pdata = (fifo_buff[0] << 8) + fifo_buff[1]; + return write_index; +} + +/** + * @brief 参数调整时,添加调整标志位 + * @param 无 + * @retval 无 + */ +static void vc9202_drv_adjust_set_fifo_mark(unsigned char slot_index) // SamplingDataTypeDef **pp_result, unsigned char rindex, unsigned char windex, unsigned char slot_num ) +{ + unsigned char mark_ppg_index = 0; + unsigned char mark_offset = 0; + unsigned char rindex = dev9202_info.read_fifo_address; + unsigned char windex = 0; + usr_iic_read_bytes(0x03, &windex, 1); + /* calc PPGOffset */ + if (windex > rindex) + { + mark_offset = (windex - rindex); + } + else + { + mark_offset = windex + 128 - rindex; + } + if ((mark_offset < 2) || (mark_offset > 128)) + { + return; + } + mark_ppg_index = mark_offset / 2 - dev9202_info.slot_number + slot_index; /* 如果是双通道,要判断是那个通道产生的调整 */ + ppg_mark[mark_ppg_index] = 1; +} + +/** + * @brief 检测因电阻过大,电流的最小步长调整都会反复饱和的情况 + * @param dir 调整方向, slot_index 当前通道, res_level 当前电阻等级, cur 当前电流值 + * @retval 1:错误,0:正常 + */ +static unsigned char vc9202_drv_adjust_res_error(VC9202AgcDirTypeDef dir, unsigned char slot_index, uint8_t res_level, uint8_t cur) +{ + static VC9202AgcDirTypeDef lastDir = AGC_DIR_NULL; + static uint8_t last_level = 0; + static uint8_t last_cur[2][3] = {0}; + static uint8_t log_cnt = 0; + if (lastDir != dir && last_level == res_level) + { + last_cur[slot_index][0] = last_cur[slot_index][1]; + last_cur[slot_index][1] = last_cur[slot_index][2]; + last_cur[slot_index][2] = cur; + if (last_cur[slot_index][0] == last_cur[slot_index][2]) + log_cnt++; + } + else + { + log_cnt = 0; + } + lastDir = dir; + last_level = res_level; + if (log_cnt >= 3) + { + log_cnt = 0; + return 1; + } + else + { + return 0; + } +} + +/** + * @brief PPG饱和时,上/下调整电流电阻配置参数 + * @param *padjust-调整结构体,thh_res-电阻限值,thh_cur-电流限值,slot_index-通道编号 + * @retval 执行结果 + */ +static VC9202DrvRetTypeDef vc9202_drv_adjust_ppg_slot_current_res_config(VC9202AdjustSlotTypeDef *padjust, unsigned char thh_res, unsigned char thh_cur, unsigned char slot_index) +{ + unsigned char old_res_lvl = 0; + unsigned char old_current = 0; + unsigned short int old_current_step = 0; + old_res_lvl = padjust->res_level; + old_current = padjust->current; + old_current_step = padjust->info.current_step; + /* 电流已经最大,调电阻 */ + if (padjust->current == thh_cur) + { + if (old_res_lvl == thh_res) + { + return RET_PPG_ERR; + } + else + { + if (padjust->info.dir == AGC_DIR_UP) + { + padjust->res_level = (old_res_lvl >= VC9202_MAX_RES_LEVEL) ? (VC9202_MAX_RES_LEVEL) : (old_res_lvl + 1); + } + else + { + padjust->res_level = (old_res_lvl < 1) ? (0) : (old_res_lvl - 1); + } + vc9202_drv_adjust_set_fifo_mark(slot_index); + } + } + else /* 电流可调 */ + { + /* step calc */ + if (padjust->info.last_dir == AGC_DIR_NULL) + { + old_current_step *= 16; + } + else if (padjust->info.dir == padjust->info.last_dir) + { + if ((old_current_step == 1) || (old_current_step == 2)) + { + old_current_step = (old_current_step + 1) * 16; + } + else + { + old_current_step *= VC9202_ADJUST_INCREASE; + } + } + else + { + old_current_step *= VC9202_ADJUST_DECREASE; + } + padjust->info.current_step = old_current_step >> 4; + padjust->info.current_step = (padjust->info.current_step <= VC9202_ADJUST_STEP_MIN) ? VC9202_ADJUST_STEP_MIN : padjust->info.current_step; + padjust->info.current_step = (padjust->info.current_step >= VC9202_ADJUST_STEP_MAX) ? VC9202_ADJUST_STEP_MAX : padjust->info.current_step; + /* 调整方向 */ + if (padjust->info.dir == AGC_DIR_UP) + { + padjust->current = ((old_current + padjust->info.current_step) > thh_cur) ? thh_cur : (old_current + padjust->info.current_step); + } + else + { + padjust->current = (old_current < padjust->info.current_step) ? thh_cur : old_current - padjust->info.current_step; + } + vc9202_drv_adjust_set_fifo_mark(slot_index); + padjust->info.last_dir = padjust->info.dir; + } + if (vc9202_drv_adjust_res_error(padjust->info.dir, slot_index, padjust->res_level, padjust->current)) + { + if (padjust->res_level >= 13) + { + padjust->res_level--; + } + } + /* 调整后的电流电阻写入寄存器 */ + vc9202_drv_set_ppgSlot_resistance(slot_index, padjust->res_level); + vc9202_drv_set_ppgSlot_current(slot_index, padjust->current); + return RET_OK; +} + +/** + * @brief 环境光饱和中断调整电阻 + * @param *padjust-调整结构体,slot_index-通道编号 + * @retval 执行结果 + */ +static VC9202DrvRetTypeDef vc9202_drv_adjust_env_configure(VC9202AdjustSlotTypeDef *padjust, unsigned char slot_index) +{ + if (padjust->slot_env > VC9202_ENV_MAX_TH) + { + padjust->record_env = padjust->slot_env; + if (padjust->res_level != 0) + { + padjust->res_level--; + padjust->max_res_level = padjust->res_level; + vc9202_drv_adjust_set_fifo_mark(slot_index); + vc9202_drv_set_ppgSlot_resistance(slot_index, padjust->res_level); + } + else + { + return RET_ENV_ERR; + } + } + return RET_OK; +} + +/** + * @brief 如果产生overload,降低电流,和电流的最大限制,过载电流-1 + * @param *padjust-调整结构体,slot_index-通道编号,status-状态值 + * @retval 执行结果 + */ +static VC9202DrvRetTypeDef vc9202_drv_adjust_over_load_configure(VC9202AdjustSlotTypeDef *padjust, unsigned char slot_index, unsigned char status) +{ + unsigned char overload[3] = {status & VC9202_STATUS_OVERLOAD0, status & VC9202_STATUS_OVERLOAD1, status & VC9202_STATUS_OVERLOAD2}; + if (overload[slot_index]) + { + if (padjust->current != 0) + { + padjust->current--; + padjust->max_current = padjust->current; + vc9202_drv_adjust_set_fifo_mark(slot_index); + vc9202_drv_set_ppgSlot_current(slot_index, padjust->current); + } + else + { + return RET_OVERLOAD_ERR; + } + } + return RET_OK; +} + +/** + * @brief 获取PPG进行电流电阻的调整 + * @param *padjust-调整结构体,slot_index-通道编号,enfifo-是否使能fifo, slot_num-通道数 + * @retval 执行结果 + */ +static VC9202DrvRetTypeDef vc9202_drv_adjust_ppg_configure(VC9202AdjustSlotTypeDef *padjust, unsigned char slot_index, unsigned char enfifo, unsigned char slot_num) +{ + static unsigned char last_index = 0, cnt = 0; + VC9202DrvRetTypeDef vc_ret = RET_OK; + unsigned char windex = 0; + unsigned char index_gap = 0; + unsigned int data = 0; + // 根据通道号和是否enfifo获取产生ppg中断的ppg值。 + windex = vc9202_drv_adjust_ppg_read(enfifo, slot_num, slot_index, &data); + /* 修改last dir */ + if (enfifo) + { + index_gap = (windex > last_index) ? (windex - last_index) : (windex - last_index + 0x80); + if (index_gap != slot_num * 2) + { + padjust->info.last_dir = AGC_DIR_NULL; + } + cnt++; + if (cnt >= slot_num) + { + cnt = 0; + last_index = windex; + } + } + /* 根据ppg值进行电流、等效电阻上下调节 */ + if (data >= 3770) + { + padjust->info.dir = AGC_DIR_UP; + vc_ret |= vc9202_drv_adjust_ppg_slot_current_res_config(padjust, padjust->max_res_level, padjust->max_current, slot_index); + } + else if (data < 320) + { + padjust->info.dir = AGC_DIR_DOWN; + vc_ret |= vc9202_drv_adjust_ppg_slot_current_res_config(padjust, 0, 0, slot_index); + } + return vc_ret; +} + +/** + * @brief 最大电阻的调整,通过判断环境光,来恢复电阻的最大限制 + * @param *padjust-调整结构体,slot_index-通道编号 + * @retval 无 + */ +static void vc9202_drv_res_max_resize(VC9202AdjustSlotTypeDef *padjust, unsigned char slot_index) +{ + static unsigned short int count_qares[2] = {0}; + static unsigned short int count_pdres[2] = {0}; + unsigned char min_env = 0; + // 该通道得最大电阻已经是最大的等级了,无需恢复退出 + if (padjust->max_res_level >= VC9202_MAX_RES_LEVEL) + { + return; + } + /* 获取一个环境光比较值,取上一次环境光调整时的一半或者环境光中断阈值 */ + min_env = (padjust->record_env / 2 < VC9202_ENV_MAX_TH) ? (padjust->record_env / 2) : VC9202_ENV_MAX_TH; + if (padjust->slot_env < min_env) + { + /* 环境光一直小于1,持续超过30次,那么直接将电阻限制设置为最大 */ + if (padjust->slot_env <= 1) + { + count_pdres[slot_index] = 0; + count_qares[slot_index] = (count_qares[slot_index] == 30) ? 0 : count_qares[slot_index] + 1; + padjust->max_res_level = (count_qares[slot_index] == 30) ? VC9202_MAX_RES_LEVEL : padjust->max_res_level; + } + else + { + /* 环境光一直小于比较值,持续超过120次,那么直接将电阻限制+1 */ + count_qares[slot_index] = 0; + count_pdres[slot_index] = count_pdres[slot_index] + 1; + padjust->max_res_level = (count_pdres[slot_index] == 120) ? padjust->max_res_level + 1 : padjust->max_res_level; + } + } + else + { + count_pdres[slot_index] = 0; + count_qares[slot_index] = 0; + } +} + +/* 判断PS通道的过载和环境光是否异常 */ +VC9202DrvRetTypeDef vc9202_drv_ps_slot_status(VC9202AdjustSlotTypeDef *p_slot_ps, unsigned char event, unsigned char status) +{ + VC9202DrvRetTypeDef vc_ret = RET_OK; +#if (FUNC_ENABLE == VC9202_CFG_DRV_BY_TIMER) /* 定时触发时,不看中断原因,因为可能被清掉 */ + if (status & VC9202_STATUS_OVERLOAD2) + { + vc_ret |= RET_OVERLOAD_ERR; + } + if (p_slot_ps->slot_env > VC9202_ENV_MAX_TH) + { + vc_ret |= RET_ENV_ERR; + } +#else + if ((event & VC9202_EVENT_OV_MARK) && (status & VC9202_STATUS_OVERLOAD2)) + { + vc_ret |= RET_OVERLOAD_ERR; + } + if ((event & VC9202_EVENT_ENV_MARK) && (p_slot_ps->slot_env > VC9202_ENV_MAX_TH)) + { + vc_ret |= RET_ENV_ERR; + } +#endif + return vc_ret; +} +/** + * @brief 识别IC的中断状态及中断事件,并对不同的情况进行调整处理等 + * @param status-工作状态, event-中断事件 + * @retval 执行结果 + */ +static VC9202DrvRetTypeDef vc9202_drv_status_event_verify(unsigned char status, unsigned char event) +{ + VC9202DrvRetTypeDef vc_ret = RET_OK; + unsigned char index = 0; + unsigned char index_slot = 0; + if (status & (VC9202_STATUS_CONFLICT | VC9202_STATUS_INSAMPLE)) + { + return RET_STATUS_ERR; + } + /* PS通道异常判断 */ + vc_ret |= vc9202_drv_ps_slot_status(&dev9202_adjust[2], event, status); + /* PPG通道的环境光读取,异常中断处理 */ + for (index = 0; index < dev9202_info.slot_number; index++) + { + index_slot = (dev9202_info.slot_number == 2) ? index : (dev9202_info.enslot[0] ? 0 : 1); + /* 恢复最大电阻的限制,根据当前 */ + vc9202_drv_res_max_resize(&dev9202_adjust[index_slot], index_slot); + /* over load, max current */ + if (event & VC9202_EVENT_OV_MARK) + { + printf("event & VC9202_EVENT_OV_MARK \n"); + vc_ret |= vc9202_drv_adjust_over_load_configure(&dev9202_adjust[index_slot], index_slot, status); + } + /* env, max res */ + if (event & VC9202_EVENT_ENV_MARK) + { + printf("event & VC9202_EVENT_ENV_MARK \n"); + vc_ret |= vc9202_drv_adjust_env_configure(&dev9202_adjust[index_slot], index_slot); + } + /* ppg interruput */ + if (event & VC9202_EVENT_PPG_MARK) + { + vc_ret |= vc9202_drv_adjust_ppg_configure(&dev9202_adjust[index_slot], index_slot, dev9202_info.enfifo_div, dev9202_info.slot_number); + printf("event & VC9202_EVENT_PPG_MARK vc_ret : 0x%x\n",vc_ret); + } + else + { + printf("event : 0x%x \n",event); + dev9202_adjust[index].info.dir = AGC_DIR_NULL; + dev9202_adjust[index].info.last_dir = AGC_DIR_NULL; + } + } + return vc_ret; +} + +/******************************************************************************************************** + * fifo 读取部分 + ********************************************************************************************************/ +/** + * @brief 校验fifo读写的偏移的可用性,及fifo是否翻转等错误。 + * @param rindex-读地址,windex-写地址,slot_num-通道数量,fifo_div-fifo深度 + * @retval 无 + */ +static VC9202DrvRetTypeDef vc9202_drv_fifo_infomation_check(unsigned char *rindex, unsigned char *windex, unsigned char slot_num, unsigned char fifo_div) +{ + unsigned int clk_diffs = 0; + unsigned int clk_count = usr_get_clock_ticks(); + /* 间隔时间的校验,后续看是否需要补充 */ + clk_diffs = (dev9202_clk.last_clk_count < clk_count) ? (clk_count - dev9202_clk.last_clk_count) : (clk_count - dev9202_clk.last_clk_count + VC9202_CFG_CLK_CHECK_MAX); + if (clk_diffs > (unsigned int)((64.0f / dev9202_info.work_frequency) * 0.8f * VC9202_CFG_CLK_CHECK_FREQUENCY / dev9202_info.slot_number)) + { + *windex = 0; + *rindex = 0x80; + dev9202_clk.last_clk_count = clk_count; + return RET_FIFO_ERR; + } + /* 读写地址的校验 */ + if (*windex < 0x80) + { + *rindex = *rindex + (fifo_div + 1) * 2; + if (*rindex < 0x80) + { + *rindex += 0x80; + } + dev9202_clk.last_clk_count = clk_count; + return RET_FIFO_ERR; + } + if (*rindex < 0x80) + { + *rindex = *windex; + dev9202_clk.last_clk_count = clk_count; + return RET_FIFO_ERR; + } + if (*windex == *rindex) + { + dev9202_clk.last_clk_count = clk_count; + return RET_FIFO_ERR; + } + dev9202_clk.last_clk_count = clk_count; + return RET_OK; +} + +/** + * @brief 获取fifos数据 + * @param enfifo-是否使能fifo,p_result-数据地址,psize-数据大小,rindex-读地址,windex-写地址,slot_num-通道数量 + * @retval 无 + */ +static void vc9202_drv_read_fifo_data(unsigned char enfifo, SamplingDataTypeDef *p_result, unsigned char *psize, unsigned char *rindex, unsigned char *windex, unsigned char slot_num) +{ + unsigned char i = 0; + unsigned char fifo_length = 0; + unsigned char fifoData[128] = {0}; + unsigned char fifo_index_offset = 0; /*正常情况下:r=w, 异常情况下,r=w-fifo_index_offset */ + /*------------- disable fifo -------------*/ + if (!enfifo) + { + *psize = slot_num; + usr_iic_read_bytes(0x80, fifoData, slot_num * 2); + for (i = 0; i < *psize; i++) + { + p_result->ppg_data[i] = (fifoData[i * 2] << 8) + fifoData[i * 2 + 1]; + if (ppg_mark[i]) + { + ppg_mark[i] = 0; + p_result->ppg_data[i] |= 0x1000; // 0x01000000; + } + } + return; + } + /*------------- enable fifo -------------*/ + /* 读取解析fifo数据 */ + if (*windex > *rindex) + { + fifo_length = *windex - *rindex; + if ((fifo_length % (dev9202_info.slot_number * 2)) && (dev9202_info.slot_number >= 2)) /* 当是多通道,但是通道数与字节数对应不上的异常处理 */ + { + fifo_index_offset = fifo_length % (dev9202_info.slot_number * 2); + fifo_length = fifo_length - fifo_index_offset; /* 只读取满足通道数的fifo数量,如果未出现异常,fifo_index_offset=0,跟之前处理一样 */ + } + usr_iic_read_bytes(*rindex, fifoData, fifo_length); + *rindex = *windex - fifo_index_offset; + } + else + { + fifo_length = *windex + 0x80 - *rindex; + if ((fifo_length % (dev9202_info.slot_number * 2)) && (dev9202_info.slot_number >= 2)) /* 当是多通道,但是通道数与字节数对应不上的异常处理 */ + { + fifo_index_offset = fifo_length % (dev9202_info.slot_number * 2); + fifo_length = fifo_length - fifo_index_offset; /* 只读取满足通道数的fifo数量 */ + } + if (*windex == 0x80) /* 数据刚好在0xfe-0xff截至 */ + { + usr_iic_read_bytes(*rindex, fifoData, fifo_length); + if (fifo_index_offset) + { + *rindex = *windex + 0x80 - fifo_index_offset; + } + else + { + *rindex = *windex - fifo_index_offset; /* fifo_index_offset=0 */ + } + } + else /* (*windex != 0x80): 0x80及以后还有数据 */ + { + usr_iic_read_bytes(*rindex, fifoData, (256 - *rindex)); /* 获取rindex-0xff的数据 */ + usr_iic_read_bytes(0x80, &fifoData[256 - *rindex], (*windex - 0x80) - fifo_index_offset); /* 剩下的数据部分如果存在 */ + *rindex = *windex - fifo_index_offset; + } + } + + *psize = fifo_length / 2; + for (i = 0; i < *psize; i++) + { + p_result->ppg_data[i] = (fifoData[i * 2] << 8) + fifoData[i * 2 + 1]; + if (ppg_mark[i]) + { + ppg_mark[i] = 0; + p_result->ppg_data[i] |= 0x1000; // 0x01000000; + } + } +} + +/** + * @brief 校验fifo并获取结果 + * @param p_result-数据地址,psize-数据大小 + * @retval 执行结果 + */ +static VC9202DrvRetTypeDef vc9202_drv_read_fifo_result(SamplingDataTypeDef *p_result, unsigned char *psize) +{ + VC9202DrvRetTypeDef vc_ret = RET_OK; + unsigned char write_index = 0; + unsigned char event = 0; + event = dev9202_info.int_event; +#if (VC9202_CFG_DRV_BY_TIMER == FUNC_ENABLE) + event = VC9202_EVENT_FIFO_MARK; +#endif + if (event & VC9202_EVENT_FIFO_MARK) + { + usr_iic_read_bytes(0x03, &write_index, 1); + vc_ret |= vc9202_drv_fifo_infomation_check(&dev9202_info.read_fifo_address, &write_index, dev9202_info.slot_number, dev9202_info.enfifo_div); + if (RET_OK == vc_ret && dev9202_info.osc_clk_status) + { + vc9202_drv_read_fifo_data(dev9202_info.enfifo_div, p_result, psize, &dev9202_info.read_fifo_address, &write_index, dev9202_info.slot_number); + } + } + return vc_ret; +} + +/*********************************************************************************************************/ +/** + * @brief 读取相关采样值和寄存器值 + * @param 无 + * @retval 无 + */ +static unsigned char vc9202_drv_read_reg(SamplingDataTypeDef *read_reg) +{ + unsigned char reg_val[6] = {0}; + unsigned char res_ext[2] = {0}; + VC9202ResLevelTypeDef res = {0}; + + /* 4bit数据读取 */ + usr_iic_read_bytes(0x04, reg_val, 3); + read_reg->pre_val[0] = reg_val[0] & 0x0F; + read_reg->pre_val[1] = reg_val[1] & 0x0F; + read_reg->env_val[0] = (reg_val[0] >> 4) & 0x0F; + read_reg->env_val[1] = (reg_val[1] >> 4) & 0x0F; + read_reg->env_val[2] = (reg_val[2] >> 4) & 0x0F; + read_reg->ps_val = reg_val[2] & 0x0F; + + /* old param */ + usr_iic_read_bytes(0x17, reg_val, 6); + read_reg->led_cur[0] = reg_val[0] & 0x7F; + read_reg->led_cur[1] = reg_val[1] & 0x7F; + read_reg->led_cur[2] = reg_val[2] & 0x7F; + read_reg->pd_res[0] = (reg_val[3] >> 4) & 0x07; + read_reg->pd_res[1] = (reg_val[4] >> 4) & 0x07; + read_reg->pd_res[2] = (reg_val[5] >> 4) & 0x07; + /* 8bit数据读取 */ + usr_iic_read_bytes(0x41, reg_val, 6); + read_reg->aenv_val[0] = reg_val[0]; + read_reg->aenv_val[1] = reg_val[1]; + read_reg->aenv_val[2] = reg_val[2]; + read_reg->apre_val[0] = reg_val[3]; + read_reg->apre_val[1] = reg_val[4]; + read_reg->aps_val = reg_val[5]; + /* new param */ + usr_iic_read_bytes(0x22, res_ext, 2); + /* slot0 */ + dev9202_adjust[0].slot_env = read_reg->aenv_val[0]; + dev9202_adjust[0].current = read_reg->led_cur[0]; + res.res_val = read_reg->pd_res[0]; + res.low_res = (res_ext[0] >> 0) & 0x01; + res.offset = (res_ext[0] >> 4) & 0x03; + res.offset3 = 0; + dev9202_adjust[0].res_level = vc9202_drv_res_register_to_level(&res); + /* slot1 */ + dev9202_adjust[1].slot_env = read_reg->aenv_val[1]; + dev9202_adjust[1].current = read_reg->led_cur[1]; + res.res_val = read_reg->pd_res[1]; + res.low_res = (res_ext[0] >> 1) & 0x01; + res.offset = (res_ext[0] >> 6) & 0x03; + res.offset3 = 0; + dev9202_adjust[1].res_level = vc9202_drv_res_register_to_level(&res); + /* slot2 */ + dev9202_adjust[2].slot_env = read_reg->aenv_val[2]; + dev9202_adjust[2].current = read_reg->led_cur[2]; + dev9202_adjust[2].res_level = read_reg->pd_res[2]; + /* 如果有等效电阻的等级,那么传出等效等级 */ + read_reg->pd_res[0] = dev9202_adjust[0].res_level; + read_reg->pd_res[1] = dev9202_adjust[1].res_level; + read_reg->pd_res[2] = dev9202_adjust[2].res_level; + /* pdbias */ + usr_iic_read_bytes(0x47, reg_val, 4); + read_reg->apdbias_val[0] = reg_val[0]; + read_reg->apdbias_val[1] = reg_val[1]; + read_reg->apdbias_val[2] = reg_val[2]; + read_reg->apdbias_val[3] = reg_val[3] & 0xf; + read_reg->pdbias = read_reg->apdbias_val[2] * 16 + read_reg->apdbias_val[3]; + + usr_iic_read_bytes(0x4B, reg_val, 4); + read_reg->bio_val_i = (reg_val[0] << 8) + reg_val[1]; + read_reg->bio_val_o = (reg_val[2] << 8) + reg_val[3]; + + if ((read_reg->aps_val >> 4) == read_reg->ps_val) /* 防止数据错误,4bit与8bit不匹配,则不对佩戴数据进行更新,防止因为异常摘下 */ + { + dev9202_wear.wear_ps = read_reg->aps_val; + dev9202_wear.wear_env = read_reg->pdbias; + dev9202_wear.wear_bio = read_reg->bio_val_o; + } + return 0; +} + +// 注意,该部分需要分为两部分,一部分为中断调用,一部分为定时调用,保证低端客户的可用性较高 +/** + * @brief 通过宏定义#if (FUNC_ENABLE==VC9202_CFG_DRV_BY_TIMER) 已经对定时器调用方式做了对应的处理,现在通用一个函数接口即可 + * @param 无 + * @retval 无 + */ +static VC9202DrvRetTypeDef vc9202_drv_gpio_event_handler(SamplingDataTypeDef *pResult, unsigned char *psize) +{ + VC9202DrvRetTypeDef vc_ret = RET_OK; + switch (dev9202_info.work_mode) + { + case WORK_MODE_HR: + vc_ret |= vc9202_drv_status_event_verify(dev9202_info.work_status, dev9202_info.int_event); + vc9202_drv_calibration_osc_clk(); + vc_ret |= vc9202_drv_read_fifo_result(pResult, psize); + break; + case WORK_MODE_SPO2: + vc_ret |= vc9202_drv_status_event_verify(dev9202_info.work_status, dev9202_info.int_event); + vc9202_drv_calibration_osc_clk(); + vc_ret |= vc9202_drv_read_fifo_result(pResult, psize); + break; + case WORK_MODE_HRV: + vc_ret |= vc9202_drv_status_event_verify(dev9202_info.work_status, dev9202_info.int_event); + vc9202_drv_calibration_osc_clk(); + vc_ret |= vc9202_drv_read_fifo_result(pResult, psize); + break; + case WORK_MODE_STRESS: + vc_ret |= vc9202_drv_status_event_verify(dev9202_info.work_status, dev9202_info.int_event); + vc9202_drv_calibration_osc_clk(); + vc_ret |= vc9202_drv_read_fifo_result(pResult, psize); + break; + case WORK_MODE_WEAR: + vc_ret |= vc9202_drv_status_event_verify(dev9202_info.work_status, dev9202_info.int_event); + vc9202_drv_calibration_osc_clk(); + break; + case WORK_MODE_FACTORY: + if (dev9202_info.int_event & VC9202_EVENT_OV_MARK) + { + vc_ret |= vc9202_drv_adjust_over_load_configure(&dev9202_adjust[0], 0, dev9202_info.work_status); + } + pResult->led_cur[0] = dev9202_adjust[0].max_current; + break; + case WORK_MODE_FPC_TEST: + vc9202_test_handler(pResult, &vc9202_test_info); + break; + default: + break; + } + return vc_ret; +} + +/** + * @brief 初始化驱动参数 + * @param + @pInit fifo_div fifo分频深度 + @pInit enslot 通道使能配置 + * @retval 无 + */ +void vc9202_drv_system_param_init(unsigned char fifo_div, unsigned char enslot) +{ + unsigned char i = 0; + /* dev info param init */ + dev9202_info.enfifo_div = fifo_div; + dev9202_info.enslot[0] = (enslot & 0x01); + dev9202_info.enslot[1] = (enslot & 0x02) >> 1; + dev9202_info.slot_number = dev9202_info.enslot[0] + dev9202_info.enslot[1]; + + dev9202_info.osc_clk_status = 0; + dev9202_info.read_fifo_address = 0x80; + /* dev9202_adjust info param init */ + for (i = 0; i < 2; i++) + { + dev9202_adjust[i].max_current = VC9202_MAX_CUR_LEVEL; + dev9202_adjust[i].max_res_level = VC9202_MAX_RES_LEVEL; + dev9202_adjust[i].record_env = 0; + dev9202_adjust[i].info.dir = AGC_DIR_NULL; + dev9202_adjust[i].info.last_dir = AGC_DIR_NULL; + dev9202_adjust[i].info.current_step = 4; + } + dev9202_adjust[2].max_res_level = VC9202_MAX_RES_LEVEL; + /* dev9202_wear param init */ +#if (VC9202_CFG_WEAR_DEFAULT_STATUS == WEAR_IS_HOLD) + dev9202_info.wear_status = WEAR_STA_HOLD; + dev9202_wear.wear_status = WEAR_STA_HOLD; + dev9202_wear.last_status = (WEAR_STA_NONE == dev9202_wear.last_status) ? WEAR_STA_HOLD : dev9202_wear.last_status; +#else + dev9202_wear.wear_status = WEAR_STA_DROP; + dev9202_wear.last_status = (WEAR_STA_NONE == dev9202_wear.last_status) ? WEAR_STA_DROP : dev9202_wear.last_status; +#endif +#if (VC9202_CFG_WEAR_DETECTION != FUNC_ENABLE) + dev9202_info.wear_status = WEAR_STA_HOLD; + dev9202_wear.wear_status = WEAR_STA_HOLD; + dev9202_wear.last_status = WEAR_STA_HOLD; +#endif + if (dev9202_wear.last_status == WEAR_STA_DROP && dev9202_info.work_mode != WORK_MODE_FACTORY && dev9202_info.work_mode != WORK_MODE_FPC_TEST) + { + if (dev9202_info.enslot[0]) + { + vc9202_drv_channel_switch(0, 0); + } + if (dev9202_info.enslot[1]) + { + vc9202_drv_channel_switch(1, 0); + } + } + dev9202_wear.wear_status = dev9202_wear.last_status; + dev9202_info.wear_status = dev9202_wear.wear_status; + alg_wear_status = WEAR_STA_HOLD; + dev9202_wear.wear_drop_cnt = VC9202_CFG_WEAR_DROP_CNT; + dev9202_wear.wear_hold_cnt = VC9202_CFG_WEAR_HOLD_CNT; + vc9202_drv_clear_clk(); +#if 0 + f_BIOInit(VC9202_CFG_WEAR_PARAM_BIOIN, VC9202_CFG_WEAR_PARAM_PSIN); + bio_result = VC9202_BIO_STA_NONE; + if( dev9202_wear.last_status==WEAR_STA_HOLD ) + { + bio_result = VC9202_BIO_STA_HOLD; + } + else + { + bio_result = VC9202_BIO_STA_NONE; + } +#else + printf("fifo_div : %d, enslot : %d \n", fifo_div, enslot ); + printf("f_BIOedgeinit(%d, %d) \n", VC9202_CFG_WEAR_PARAM_BIOIN, VC9202_CFG_WEAR_PARAM_PSIN ); + if (VC9202_BIO_STA_NONE == bio_result) + { + uint32_t irq_sts = osal_irq_lock(); + f_BIOInit(VC9202_CFG_WEAR_PARAM_BIOIN, VC9202_CFG_WEAR_PARAM_PSIN); + osal_irq_restore(irq_sts); + printf(" VC9202_BIO_STA_DROP == bio_result f_BIOedgeinit(%d, %d) \n", VC9202_CFG_WEAR_PARAM_BIOIN, VC9202_CFG_WEAR_PARAM_PSIN ); + } +#endif +} + +/********************************************************************************************************* + * 对外功能函数 + ********************************************************************************************************/ + +/** + * @brief 获取chip_id + * @param 无 + * @retval chip_id值 + */ +unsigned char vc9202_drv_get_chip_id(void) +{ + unsigned char chip_id = 0; + usr_iic_read_bytes(0x00, &chip_id, 1); + printf("vc9202 chip id : 0x%x\n",chip_id); + return chip_id; +} + +/** + * @brief 开始采样 + * @param 无 + * @retval 无 + */ +void vc9202_drv_start_sampling(void) +{ + unsigned char temp = 0x00; + usr_iic_read_bytes(0x10, &temp, 1); + temp |= (1 << 7); + usr_iic_write_bytes(0x10, &temp, 1); +} + +/** + * @brief 停止采样 + * @param 无 + * @retval 无 + */ +void vc9202_drv_stop_sampling(void) +{ + unsigned char temp = 0x00; + usr_iic_read_bytes(0x10, &temp, 1); + temp &= ~(1 << 7); + usr_iic_write_bytes(0x10, &temp, 1); + bio_result = VC9202_BIO_STA_NONE; +} + +/** + * @brief 软复位 + * @param 无 + * @retval 无 + */ +void vc9202_drv_soft_reset(void) +{ + unsigned char reset_reg_cfg = 0x5A; + usr_iic_write_bytes(0x3B, &reset_reg_cfg, 1); +} + +/** + * @brief 独立使/失能某一物理通道 + * @param + @num_channel 通道编号(0,1,2) + @enable_flag 使能或失能标志位 + * @retval 0执行成功 + */ +unsigned char vc9202_drv_channel_switch(unsigned char num_channel, unsigned char enable_flag) +{ + unsigned char temp = 0x00; + usr_iic_read_bytes(0x10, &temp, 1); + if (num_channel > 2) + { + return 1; + } + if (enable_flag == 1) + { + temp |= (1 << num_channel); + usr_iic_write_bytes(0x10, &temp, 1); + /* 同频处理 */ + if (WORK_MODE_SPO2 == dev9202_info.work_mode) + { + usr_iic_read_bytes(0x11, &temp, 1); + temp &= ~(1 << 4); + usr_iic_write_bytes(0x11, &temp, 1); + temp = 0; + usr_iic_write_bytes(0x16, &temp, 1); + } + } + else if (enable_flag == 0) + { + temp &= ~(1 << num_channel); + usr_iic_write_bytes(0x10, &temp, 1); + /* 同频处理 */ + usr_iic_read_bytes(0x11, &temp, 1); + temp |= (1 << 4); + usr_iic_write_bytes(0x11, &temp, 1); + temp = dev9202_info.enfifo_div; + usr_iic_write_bytes(0x16, &temp, 1); + } + else + { + return 2; + } + return 0; +} + +/** + * @brief 获取中断原因 + * @param 无 + * @retval 中断原因 + */ +unsigned char vc9202_drv_get_int_reason(void) +{ + unsigned char int_reason = 0; + unsigned char int_enable = 0; + usr_iic_read_bytes(0x02, &int_reason, 1); + usr_iic_read_bytes(0x11, &int_enable, 1); + /* 在同频时,获取到的ps中断,并非ps所需要的中断 */ + if (!(int_enable & (1 << 4))) + { + int_reason &= ~(1 << 4); + } +#if (VC9202_CFG_DRV_BY_TIMER == FUNC_ENABLE) + int_reason = (VC9202_EVENT_PS_MARK | VC9202_EVENT_FIFO_MARK); +#endif + return int_reason; +} + +/** + * @brief 防止静电打死,自动恢复,定时去调用检查即可 + * @param 无 + * @retval + */ +void vc9202_drv_electrostatic_death(void) +{ + unsigned char work_reg = 1; + unsigned char bio_reg[2] = {1, 1}; + unsigned short bio_val = 0; + usr_iic_read_bytes(0x10, &work_reg, 1); + if(dev9202_info.enable_bio) + { + usr_iic_read_bytes(0x4D, bio_reg, 2); + } + bio_val = (bio_reg[1] << 8) + bio_reg[0]; + /* dev9202 death */ + if (0 == work_reg || 0 == bio_val) + { + InitParamTypeDef init_param = {dev9202_info.fifo_time, dev9202_info.work_mode}; + vc9202_drv_soft_reset(); + vc9202_drv_reg_init(&init_param); + } +} + +/** + * @brief 防止任务队列堆积处理,导致短时间连续执行 + * @param 无 + * @retval 0-pass,-1-fail + */ +#define CHECK_GAPTIME_RATIO 3/4 /* 最短中断时间的3/4,30ms,算上频率在[16K,24K]波动,限制的时间为[25~37]ms以内会被过滤掉 */ +static int vc9202_drv_check_running_gaptime(void) +{ + unsigned int last_count = dev9202_clk.last_clk_check; + unsigned int now_count = 0; + + unsigned int count1 = 0, count2 = 0; + unsigned int count_diff = 0; + unsigned char cnt_reg[4] = {0}; + int ret = 0; + unsigned char check_gap_time = (1000/dev9202_info.work_frequency)*CHECK_GAPTIME_RATIO; + usr_iic_read_bytes(0x07, cnt_reg, 4); + count1 = (cnt_reg[0] << 8) + cnt_reg[1]; + count2 = (cnt_reg[2] << 8) + cnt_reg[3]; + count_diff = (count1 <= count2) ? (count2 - count1) : (count2 - count1 + 0xffff); + /* count值获取正确有效 */ + if (count_diff <= 5) + { + now_count = count1; + } + count_diff = 8000; + if ((0 != now_count) && (0 != last_count)) + { + count_diff = (last_count <= now_count) ? (now_count - last_count) : (now_count - last_count + 0xffff); + } + /* 间隔时间过小,算上频率在[16K,24K]波动,限制的时间为[25~37]ms以内会被过滤掉 */ + if (count_diff <= 20 * check_gap_time) + { + ret = -1; + } + dev9202_clk.last_clk_check = now_count; + return ret; +} + +/** + * @brief 寄存器配置 + * @param + @pInit_Param->work_mode 工作模式 + @pInit_Param->sampling_gap_time FIFO中断间隔时间,FIFO深度 + * @retval 无 + */ +unsigned char vc9202_drv_reg_init(InitParamTypeDef *pInit_Param) +{ + unsigned short int clk_div = 0x0000; + unsigned char fifo_div = 0; + unsigned char reg_cfg[17] = {0x00, 0x1F, 0x8A, 0x00, 0x03, 0x1F, 0x00, 0x00, 0x80, 0x00, 0x57, 0x57, 0x07, 0x16, 0x56, 0x16, 0x00}; + unsigned char reg_new = (1 << 3); + unsigned char reg_bio_cfg[2] = {0x00, 0xA2}; +/* 设置中断触发方式 0上升沿 1下降沿 */ +#if VC9202_CFG_INTERRUPT_MODE + reg_cfg[1] |= (1 << 7); +#endif + +/* 设置中断电平保持时间 */ +#ifdef VC9202_CFG_INTERRUPT_TIME + reg_cfg[1] |= (VC9202_CFG_INTERRUPT_TIME << 5); +#endif + + /* 设置FIFO深度,FIFO/PS中断间隔时间,同时设置采样分频 */ + dev9202_info.work_frequency = VC9202_CFG_SAMPLE_RATE; + dev9202_info.work_mode = (WorkModeTypeDef)pInit_Param->work_mode; + dev9202_info.fifo_time = pInit_Param->sampling_gap_time; + if (WORK_MODE_HRV == dev9202_info.work_mode) + { + dev9202_info.work_frequency = 100; + dev9202_info.fifo_time = 200; + } + fifo_div = dev9202_info.fifo_time / (1000 / dev9202_info.work_frequency) - 1; + clk_div = (20000 / dev9202_info.work_frequency) - 1; + reg_cfg[3] = fifo_div; + reg_cfg[4] = (clk_div >> 8) & 0xFF; + reg_cfg[5] = (clk_div & 0xFF); + reg_cfg[6] = fifo_div; + + /* 设置PS通道PD电阻,LED电流 */ + reg_cfg[9] = VC9202_CFG_PS_SLOT_CUR; + reg_cfg[12] = VC9202_CFG_PS_SLOT_RES; + + switch (dev9202_info.work_mode) + { + case WORK_MODE_HR: + reg_cfg[0] = 0x45; +#if VC9202_CFG_IS_HALVE_CAP + reg_cfg[10] = 0x54; /* 根据发光时间设置滤波电阻 */ + reg_cfg[13] = 0x03; // 0x03; /* 绿灯(物理通道0)映射到逻辑通道0,并设置采样时长为50us */ + reg_cfg[16] = 0x01; /* 发光时间减半,开1倍过采 */ +#else + reg_cfg[10] = 0x57; /* 根据发光时间设置滤波电阻 0x57 */ + reg_cfg[13] = 0x16; /* 绿灯(物理通道0)映射到逻辑通道0,并设置采样时长为50us */ + reg_cfg[16] = 0x00; /* 发光时间减半,开1倍过采 */ +#endif + break; + + case WORK_MODE_SPO2: + reg_cfg[0] = 0x47; + reg_cfg[1] &= ~(1 << 4); /* 关闭PS中断 */ + reg_cfg[6] = 0x00; /* PS开同频,避免引入同频干扰 */ + reg_cfg[7] = 0x80; /* slot0(红光)开32倍增益 */ + reg_cfg[13] = 0xB6; /*B6-3 红外(物理通道2)映射到逻辑通道0,并设置采样时长为200us */ + reg_cfg[14] = 0x76; /*76-3 红光(物理通道1)映射到逻辑通道0,并设置采样时长为200us */ + reg_cfg[16] = 0x04; /* 开16倍过采 */ + break; + + case WORK_MODE_HRV: + reg_cfg[0] = 0x45; +#if VC9202_CFG_IS_HALVE_CAP + reg_cfg[10] = 0x54; /* 根据发光时间设置滤波电阻 */ + reg_cfg[13] = 0x03; /* 绿灯(物理通道0)映射到逻辑通道0,并设置采样时长为50us */ + reg_cfg[16] = 0x01; /* 发光时间减半,开1倍过采 */ +#else + reg_cfg[10] = 0x57; /* 根据发光时间设置滤波电阻 */ + reg_cfg[13] = 0x16; /* 绿灯(物理通道0)映射到逻辑通道0,并设置采样时长为50us */ + reg_cfg[16] = 0x00; /* 发光时间减半,开1倍过采 */ +#endif + reg_bio_cfg[1] = 0x00; + break; + + case WORK_MODE_STRESS: + reg_cfg[0] = 0x45; +#if VC9202_CFG_IS_HALVE_CAP + reg_cfg[10] = 0x54; /* 根据发光时间设置滤波电阻 */ + reg_cfg[13] = 0x03; /* 绿灯(物理通道0)映射到逻辑通道0,并设置采样时长为50us */ + reg_cfg[16] = 0x01; /* 发光时间减半,开1倍过采 */ +#else + reg_cfg[10] = 0x57; /* 根据发光时间设置滤波电阻 */ + reg_cfg[13] = 0x16; /* 绿灯(物理通道0)映射到逻辑通道0,并设置采样时长为50us */ + reg_cfg[16] = 0x00; /* 发光时间减半,开1倍过采 */ +#endif + break; + + case WORK_MODE_WEAR: + reg_cfg[0] = 0x44; + reg_cfg[1] = (reg_cfg[1] | (1 << 4)) & 0xF0; + break; + + case WORK_MODE_FACTORY: + reg_cfg[0] = 0x47; + reg_cfg[7] = 0xEF; + reg_cfg[8] = 0xEF; + reg_cfg[10] = 0x77; + reg_cfg[11] = 0x77; + break; + case WORK_MODE_FPC_TEST: + reg_cfg[0] = 0x47; + reg_cfg[7] = 0xEF; + reg_cfg[8] = 0xEF; + reg_cfg[10] = 0x77; + reg_cfg[11] = 0x77; + vc9202_test_init(&vc9202_test_info); + break; + default: + break; + } + usr_iic_write_bytes(0x10, reg_cfg, 17); /* 心率血氧相关寄存器配置 */ + usr_iic_write_bytes(0x22, ®_new, 1); + usr_iic_write_bytes(0x23, reg_bio_cfg, 2); /*BIO相关寄存器配置 */ + if (reg_bio_cfg[1] & 0x80) + { + dev9202_info.enable_bio = 1; + } + else + { + dev9202_info.enable_bio = 0; + } + vc9202_drv_system_param_init(fifo_div, reg_cfg[0]); + /* mode位置1,芯片开始工作 */ + vc9202_drv_start_sampling(); + return 0; +} + +/** + * @brief 传递采样数据 + * @param 采样数据保存地址,ppg数量 + * @retval 执行结果 + */ +unsigned char vc9202_drv_get_sampling_data(SamplingDataTypeDef *pSamplingData, unsigned char *psize) +{ + unsigned char ret = 0; + /* 读取状态及中断事件 */ + usr_iic_read_bytes(0x01, &dev9202_info.work_status, 1); + usr_iic_read_bytes(0x02, (unsigned char *)&dev9202_info.int_event, 1); + + printf("dev9202_info.work_status : 0x%x ,dev9202_info.int_event : 0x%x\n",dev9202_info.work_status,dev9202_info.int_event); + + if (dev9202_info.work_status & 0xf8) /* 防止insample和conflict时数据错误,则不对设备进行处理 */ + { + printf("dev9202_info.work_status & 0xf8 \n"); + dev9202_info.int_event = 0; /* 异常时,避免执行佩戴检测 */ + return 1; + } + if (-1 == vc9202_drv_check_running_gaptime()) + { + printf("-1 == vc9202_drv_check_running_gaptime() \n"); + dev9202_info.int_event = 0; /* 异常时,避免执行佩戴检测 */ + return 1; + } + ret = vc9202_drv_read_reg(pSamplingData); + ret = (unsigned char)vc9202_drv_gpio_event_handler(pSamplingData, psize); + return ret; +} diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_driver_testmode.c b/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_driver_testmode.c new file mode 100644 index 000000000..763c08e75 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_driver_testmode.c @@ -0,0 +1,176 @@ +/********************************************************************************************************* + * Copyright(c) 2022, vcare Corporation. All rights reserved. + ********************************************************************************************************** + * @file vc9202_driver_testmode.c + * @brief + * @details + * @author + * @date + * @version + *********************************************************************************************************/ +//!!!注意:本文文件包含step1(id,频率,温度的读取判断),step2(bio是否满足变化/ps是否能够达到阈值)两步进行FPC测试,用于那些不使用测试板的测试的用户,排查基础问题 +//!!!注意:本文文件包含step1(id,频率,温度的读取判断),step2(bio是否满足变化/ps是否能够达到阈值)两步进行FPC测试,用于那些不使用测试板的测试的用户,排查基础问题 +//如果不需要,删除本文件和其他报错的地方即可 +#include "vc9202_common.h" +#include "vc9202_driver.h" + +VC9202TestInfoTypeDef vc9202_test_info = {0}; + +const static unsigned char disp_green[17] = {0xC5, 0x30, 0x8A, 0x40, 0x03, 0x1F, 0x00, + 0x30, 0x40, 0x5A, + 0x57, 0x37, 0x47, + 0x16, 0x56, 0x16, 0x00}; +const static unsigned char disp_red[17] = {0xC6, 0x30, 0x8A, 0x40, 0x03, 0x1F, 0x00, + 0x30, 0x40, 0x5A, + 0x57, 0x37, 0x47, + 0x16, 0x56, 0x16, 0x00}; +static unsigned int mcu_cnt[2] = {0}, chip_cnt[2] = {0}; +void vc9202_test_init(VC9202TestInfoTypeDef *ptest_info) +{ + mcu_cnt[0] = 0; + mcu_cnt[1] = 0; + chip_cnt[0] = 0; + chip_cnt[1] = 0; + memset(ptest_info, 0, sizeof(VC9202TestInfoTypeDef)); +} + +static unsigned int vc9202_test_real_frequency(void) +{ + unsigned char cnt_reg[4] = {0}; + unsigned int mcu_cnt_dif = 0; + unsigned int chp_cnt_dif = 0; + unsigned int calc_freq = 0; + + mcu_cnt[1] = mcu_cnt[0]; + chip_cnt[1] = chip_cnt[0]; + mcu_cnt[0] = usr_get_clock_ticks(); + usr_iic_read_bytes(0x07, cnt_reg, 4); + chip_cnt[0] = (cnt_reg[0] << 8) + cnt_reg[1]; + unsigned short cnt1 = (cnt_reg[0] << 8) + cnt_reg[1]; + unsigned short cnt2 = (cnt_reg[2] << 8) + cnt_reg[3]; + if (abs(cnt1 - cnt2) >= 10) + { + return 0xffff; + } + if ((0 == mcu_cnt[1]) || (0 == mcu_cnt[0])) + return 20 * 1000; + if ((0 == chip_cnt[1]) || (0 == chip_cnt[0])) + return 20 * 1000; + mcu_cnt_dif = (mcu_cnt[0] > mcu_cnt[1]) ? (mcu_cnt[0] - mcu_cnt[1]) : (VC9202_CFG_CLK_CHECK_MAX - mcu_cnt[1] + mcu_cnt[0]); + chp_cnt_dif = (chip_cnt[0] > chip_cnt[1]) ? (chip_cnt[0] - chip_cnt[1]) : (0xffff - chip_cnt[1] + chip_cnt[0]); + float timeDiff = (float)mcu_cnt_dif / (float)VC9202_CFG_CLK_CHECK_FREQUENCY; + calc_freq = (float)chp_cnt_dif / timeDiff; + if (calc_freq >= 0xffff) + calc_freq = 0xffff; + if (calc_freq <= 0) + calc_freq = 0; + return calc_freq; +} + +static int vc9202_test_led_glint(VC9202TestInfoTypeDef *ptest_info) +{ + static unsigned char glint_count = 0; + if (ptest_info->step1_ret && ptest_info->step2_ret) + { + glint_count++; + if (glint_count == 25) + { + usr_iic_write_bytes(0x10, (unsigned char *)disp_green, 17); + } + else if (glint_count == 50) + { + glint_count = 0; + usr_iic_write_bytes(0x10, (unsigned char *)disp_red, 17); + } + return 1; + } + return 0; +} +/*记录bio变化值的最大的差值,用于判断bio变化是否满足阈值判断*/ +static void vc9202_test_bio_variation(VC9202TestInfoTypeDef *ptest_info, unsigned short bio) +{ + unsigned short now_gap = 0; + ptest_info->biovalue_now[1] = ptest_info->biovalue_now[0]; + ptest_info->biovalue_now[0] = bio; + if (0 == ptest_info->biovalue_now[1] || 0 == ptest_info->biovalue_now[0]) + { + return; + } + now_gap = (ptest_info->biovalue_now[0] > ptest_info->biovalue_now[1]) ? (ptest_info->biovalue_now[0] - ptest_info->biovalue_now[1]) : (ptest_info->biovalue_now[1] - ptest_info->biovalue_now[0]); + if (now_gap > ptest_info->biovalue_max) + { + ptest_info->biovalue_max = now_gap; + ptest_info->biovalue_gap[0] = ptest_info->biovalue_now[0]; + ptest_info->biovalue_gap[1] = ptest_info->biovalue_now[1]; + } + return; +} +void vc9202_test_handler(SamplingDataTypeDef *pResult, VC9202TestInfoTypeDef *ptest_info) +{ + // 判断是否测试完成,只执行交替闪 + if (vc9202_test_led_glint(ptest_info)) + { + return; + } + // step1 计算频率 + 读取ID及中断原因 + if (0 == ptest_info->step1_ret) + { + ptest_info->max_current = pResult->led_cur[0]; + ptest_info->leak_pre_value = pResult->apre_val[0]; + ptest_info->frequency = vc9202_test_real_frequency(); + usr_iic_read_bytes(0x00, &ptest_info->icid, 1); + usr_iic_read_bytes(0x02, &ptest_info->reason, 1); + return; + } + // step2 bio.ps + if (0 != ptest_info->step1_ret && 0 == ptest_info->step2_ret) + { + ptest_info->ps_value = pResult->aps_val; + vc9202_test_bio_variation(ptest_info, pResult->bio_val_o); + } + return; +} + +// 在xx_user.c中调用 case WORK_MODE_FACTORY: factory_test_function( &data ); break; +#if 0 +static void fpc_test_function(SamplingDataTypeDef *pdata) +{ + extern VC9202TestInfoTypeDef vc9202_test_info; + /* 等待5次中断工作稳定 */ + if (vc9202_test_info.wait_cnt <= 5) + { + vc9202_test_info.wait_cnt++; + return; + } + /* 步骤一:常规数据读取判断是否在范围内,此处得数据请根据实际设计对参数进行调整 */ + if (0 == vc9202_test_info.step1_ret) + { + if (vc9202_test_info.icid == 0x21 && vc9202_test_info.reason & 0x3f && + vc9202_test_info.frequency >= 16000 && vc9202_test_info.frequency <= 24000) + { + vc9202_test_info.step1_ret = 1; + } + else + { + vc9202_test_info.step1_ret = 2; + } + } + /* 步骤二:bio/ps数据读取判断是否在范围内 */ + else if (0 == vc9202_test_info.step2_ret) + { + if (vc9202_test_info.biovalue_max >= 400 && vc9202_test_info.ps_value >= 200) + { + vc9202_test_info.step2_ret = 1; + } + } + /*注意:上述方式会一直等待,直到检测通过,也可以使用人为触发记录,进行比较两次触发时的记录值差距是否满足。 + 记录两次不同状态(模拟佩戴和摘下)下时的pdata->bio_val_o,比较差值 + 记录模拟佩戴下的pdata->ps_val,判断是否满足PS范围 + */ + /* 其中step1_ret和step2_ret,分别表示测试是否通过,由于step2_ret需要检测变化量,所以需要一直等待操作,如果测试通过后,驱动会控制红绿灯交替闪烁 */ + DEBUG("[FAT]icid=%#x,reason=%#x,frequency=%d,temperature=%d", vc9202_test_info.icid, vc9202_test_info.reason, vc9202_test_info.frequency, vc9202_test_info.temperature); + printf("[FAT]biovalue_max=%d,ps=%d,gap[%d,%d]", vc9202_test_info.biovalue_max, vc9202_test_info.ps_value, vc9202_test_info.biovalue_gap[0], vc9202_test_info.biovalue_gap[1]); + printf("[FAT]-ret(%d,%d)", vc9202_test_info.step1_ret, vc9202_test_info.step2_ret); +} + +#endif diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_hal.c b/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_hal.c new file mode 100644 index 000000000..907333c35 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_hal.c @@ -0,0 +1,211 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-03-13 15:08:38 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-24 10:25:14 + * @FilePath: /FD230/code/sdk/drivers/peripheral/ppg/hx3695/hx3695_hal.c + * @Description: + * + * Copyright (c) 2024 by ${git_name_email}, All Rights Reserved. + */ + +#include +#include +#include +#include +#include + +#include "pm_control.h" +#include "platform_core.h" +#include "pinctrl_porting.h" +#include "hal_gpio.h" + +#ifdef PPG_VC9202 +#include "vc30x91x92x_common.h" +#include "vc9202_user.h" +#endif + +#ifdef PPG_HX3695 +#include "tyhx_hrs_custom.h" +#include "hx3695.h" +#include "hx3695_hal.h" +#include "hx3695.h" + + +hx3695_msg_callback_t g_send_msg_toUI = NULL; + +// typedef void (*hx3695_msg_callback_t)(uint8_t msg_type, uint8_t *msg_data, int32_t msg_datalen); +uint32_t hx3695_register_msg_callback(hx3695_msg_callback_t func) +{ + g_send_msg_toUI = func; + return 0; +} + +hx3695_msg_callback_t hx3695_get_send_msg_callback_func(void) +{ + return g_send_msg_toUI; +} + + +#include "hx3695.h" +#include "tyhx_hrs_custom.h" + +#endif + + +// S_AGPIO_L26 心率复位 PPG reset +#define GPIO_PIN_PRG_RESET S_AGPIO_L26 +// S_AGPIO_R0 PRG_LED_POWER ON 心率等电源控制脚 +#define GPIO_PIN_PRG_LED_ON S_AGPIO_R0 + +uint32_t tyhx_poweron(void) +{ + uapi_pin_set_mode(GPIO_PIN_PRG_RESET, (pin_mode_t)HAL_PIO_FUNC_GPIO); + uapi_gpio_set_dir(GPIO_PIN_PRG_RESET, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_val(GPIO_PIN_PRG_RESET, GPIO_LEVEL_HIGH); + + uapi_pin_set_mode(GPIO_PIN_PRG_LED_ON, (pin_mode_t)HAL_PIO_FUNC_GPIO); + uapi_gpio_set_dir(GPIO_PIN_PRG_LED_ON, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_val(GPIO_PIN_PRG_LED_ON, GPIO_LEVEL_HIGH); + LOS_Udelay(30); + return 0; +} +uint32_t tyhx_poweroff(void) +{ + uapi_pin_set_mode(GPIO_PIN_PRG_RESET, (pin_mode_t)HAL_PIO_FUNC_GPIO); + uapi_gpio_set_dir(GPIO_PIN_PRG_RESET, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_val(GPIO_PIN_PRG_RESET, GPIO_LEVEL_LOW); + + uapi_pin_set_mode(GPIO_PIN_PRG_LED_ON, (pin_mode_t)HAL_PIO_FUNC_GPIO); + uapi_gpio_set_dir(GPIO_PIN_PRG_LED_ON, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_val(GPIO_PIN_PRG_LED_ON, GPIO_LEVEL_LOW); + LOS_Mdelay(1); + return 0; +} + + +void ppg_send_cmd_to_vc9202_driver(char* mode, uint8_t enable) +{ +#ifdef PPG_VC9202 + WorkModeTypeDef md = WORK_MODE_NONE; +#elif defined PPG_HX3695 + WORK_MODE_T md = NULL_MODE; +#endif + if (strcmp(mode, "hrs") == 0) { +#ifdef PPG_VC9202 + md = WORK_MODE_HR; +#elif defined PPG_HX3695 + md = HRS_MODE; +#endif + } else if (strcmp(mode, "hrv") == 0) { +#ifdef PPG_VC9202 + md = WORK_MODE_HRV; +#elif defined PPG_HX3695 + md = HRV_MODE; +#endif + } else if (strcmp(mode, "spo2") == 0) { +#ifdef PPG_VC9202 + md = WORK_MODE_SPO2; +#elif defined PPG_HX3695 + md = SPO2_MODE; +#endif + } else if (strcmp(mode, "leaklight") == 0) { +#ifdef PPG_VC9202 + md = WORK_MODE_NONE; +#elif defined PPG_HX3695 + md = FT_LEAK_LIGHT_MODE; +#endif + } else if (strcmp(mode, "graycard") == 0) { +#ifdef PPG_VC9202 + md = WORK_MODE_NONE; +#elif defined PPG_HX3695 + md = FT_GRAY_CARD_MODE; +#endif + } else if (strcmp(mode, "ledoff") == 0) { +#ifdef PPG_VC9202 + md = WORK_MODE_NONE; +#elif defined PPG_HX3695 + md = FT_LED_OFF_MODE; +#endif + } else if (strcmp(mode, "wear") == 0) { +#ifdef PPG_VC9202 + md = WORK_MODE_WEAR; +#elif defined PPG_HX3695 + md = WEAR_MODE; +#endif + } else if (strcmp(mode, "single") == 0) { +#ifdef PPG_VC9202 + md = WORK_MODE_NONE; +#elif defined PPG_HX3695 + md = FT_SINGLE_CHECK_MODE; +#endif + }else if (strcmp(mode, "factory") == 0) { +#ifdef PPG_VC9202 + md = WORK_MODE_FACTORY; +#endif + }else if (strcmp(mode, "fpc") == 0) { +#ifdef PPG_VC9202 + md = WORK_MODE_FPC_TEST; +#endif + } + +#ifdef PPG_HX3695 + if (enable == 1 || md == FT_LED_OFF_MODE) +#elif defined PPG_VC9202 + if (enable == 1) +#endif + { + // ppg_service_running_stat = true; + uapi_pm_control(PM_APP_WAKE_NEEDED); + tyhx_poweron(); +#ifdef PPG_HX3695 + hx3695_init(md); +#endif +#ifdef PPG_VC9202 + InitParamTypeDef pInit_Param = {0}; + pInit_Param.sampling_gap_time = 400; + pInit_Param.work_mode = md; + vc9202_usr_init(&pInit_Param); +#endif + } else { + +#ifdef PPG_HX3695 + // ppg_service_running_stat = false; + if(md == HRS_MODE|| md == FT_LEAK_LIGHT_MODE || md == FT_GRAY_CARD_MODE || md == WEAR_MODE || md == FT_SINGLE_CHECK_MODE) + { +#ifdef HRS_ALG_LIB + hx3695_hrs_disable(); + // PRINT("uapi_dev_runtime_get_sleep_monitoring_status : %d \n",uapi_dev_runtime_get_sleep_monitoring_status()); + // PRINT("uapi_dev_runtime_get_hrs_passivity_flag : %d \n",uapi_dev_runtime_get_hrs_passivity_flag()); + if(uapi_dev_runtime_get_sleep_monitoring_status() && uapi_dev_runtime_get_hrs_passivity_flag())//处于睡眠监测 且在心率周期检测期间 上报活体佩戴 + { + hx3695_wearstatus_update(); + uapi_dev_runtime_set_hrs_passivity(0);//清除被动检测runtime + } +#endif + }else if(md == HRV_MODE) + { +#ifdef HRV_ALG_LIB + hx3695_hrv_disable(); +#endif + }else if(md == SPO2_MODE) + { +#ifdef SPO2_ALG_LIB + hx3695_spo2_disable(); +#endif + } + else{ +#ifdef HRS_ALG_LIB + hx3695_hrs_disable(); +#endif + } + +#elif defined PPG_VC9202 + vc9202_usr_stop_sampling(); +#endif + + + uapi_pm_control(PM_APP_WAKE_NONEED); + tyhx_poweroff(); + } +} \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_user.c b/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_user.c new file mode 100644 index 000000000..5d5fbbc10 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_user.c @@ -0,0 +1,569 @@ +/********************************************************************************************************* + * Copyright(c) 2022, vcare Corporation. All rights reserved. + ********************************************************************************************************** + * @file vc9202_user.c + * @brief functional Module Layer + * @details The functional interface is implemented by calling the driver.contain init_func,irq_handler + * @author + * @date + * @version + *********************************************************************************************************/ +#include "algo.h" +#include "spo2Algo.h" +#include "vcSportMotionIntAlgo.h" +#include "vc9202_common.h" +#include "vc9202_user.h" +#include "vc9202_driver.h" +#include "custom_utils.h" +#include "platform_core.h" +#include "systick.h" +#include "pinctrl_porting.h" +#include "hal_gpio.h" +#include "los_tick.h" + +static WorkModeTypeDef work_mode = WORK_MODE_NONE; +AlgoCalcResultsTypeDef vc9202_alg_results = {0}; + +/* 蓝牙发送原始数据接口 */ +extern void vc9202_debug_send_heart_rate_data( SamplingDataTypeDef* presult ,GsensorTypeDef* paxis, unsigned char heart_rate, unsigned char algo_mode, unsigned char ppgsize ); +extern void vc9202_debug_send_spo2_data( SamplingDataTypeDef* presult ,GsensorTypeDef* paxis, unsigned char spo2_val, unsigned char ppgsize ); + +/********************************************************************************************************* + * PERIPHERAL FUNCTIONS + ********************************************************************************************************/ +#define CHIP_I2C_ADDRESS 0x33 +#define VC9202_I2C_BUS I2C_BUS_2 + +/** + * @brief iic多字节写 + * @param + * @arg reg_addr:寄存器地址 + * @arg pBuffer:缓冲区指针 + * @arg num_bytes:写的字节数 + * @retval 无 + */ +unsigned char usr_iic_write_bytes(unsigned char reg_addr, unsigned char *pBuffer, unsigned char buf_size) +{ + custom_i2c_write_reg(CHIP_I2C_ADDRESS, reg_addr, 1, pBuffer, buf_size, VC9202_I2C_BUS); + return 0; +} + + +/** + * @brief iic多字节读 + * @param + * @arg reg_addr:寄存器地址 + * @arg pBuffer:缓冲区指针 + * @arg num_bytes:读的字节数 + * @retval 无 + */ +unsigned char usr_iic_read_bytes(unsigned char reg_addr, unsigned char *pBuffer, unsigned char buf_size) +{ + custom_i2c_read_regs(CHIP_I2C_ADDRESS, reg_addr, 1 , pBuffer, buf_size, VC9202_I2C_BUS); + return 0; +} + + +/** + * @brief 校验用时钟tick获取 + * @param 无 + * @retval 时钟tick + */ +unsigned int usr_get_clock_ticks(void) +{ + return uapi_systick_get_count(); +} + +/********************************************************************************************************* + * API FUNCTIONS + ********************************************************************************************************/ +/** + * @brief 获取chip_id + * @param 无 + * @retval chip_id值 + */ +void vc9202_usr_get_chip_id( unsigned char *pchip_id, unsigned char *pvers_id ) +{ + usr_iic_read_bytes( 0x00, pchip_id, 1 ); + usr_iic_read_bytes( 0x40, pvers_id, 1 ); +} + +/** + * @brief 初始化 + * @param 无 + * @retval 无 + */ +void vc9202_usr_init( InitParamTypeDef *pInitParam ) +{ + memset( &vc9202_alg_results, 0 ,sizeof(vc9202_alg_results) ); + // vcSpo2AlgoInit(); + spo2AlgoInit(); + Algo_Init(); + vcSportMotionAlgoInit(); + work_mode = (WorkModeTypeDef)pInitParam->work_mode; + vc9202_drv_soft_reset(); + vc9202_drv_reg_init( pInitParam ); +} + +/** + * @brief 开始采样 + * @param 无 + * @retval 无 + */ +void vc9202_usr_start_sampling(void) +{ + vc9202_drv_start_sampling(); +} + + +/** + * @brief 停止采样 + * @param 无 + * @retval 无 + */ +void vc9202_usr_stop_sampling(void) +{ + vc9202_drv_stop_sampling(); +} + + +/** + * @brief 软复位 + * @param 无 + * @retval 无 + */ +void vc9202_usr_soft_reset(void) +{ + vc9202_drv_soft_reset(); +} + +/** + * @brief 独立使/失能某一物理通道 + * @param + @num_channel 通道编号(1,2,3) + @enable_flag 使能或失能标志位 + * @retval 0-成功,其他-失败 + */ +unsigned char vc9202_usr_channel_switch(unsigned char num_channel, unsigned char enable_flag) +{ + return vc9202_drv_channel_switch(num_channel, enable_flag); +} + +/** + * @brief 获取中断原因 + * @param @无 + @ int_reason 中断原因 + * @retval 中断原因 + */ +unsigned char vc9202_usr_get_int_reason(void) +{ + /* bit2:fifo,bit4:ps */ + return vc9202_drv_get_int_reason(); +} +/** + * @brief 获取佩戴检测结果 + * @param @无 + @ 无 + * @retval 佩戴检测结果 + */ +unsigned char vc9202_usr_get_wear_status(void) +{ + unsigned char sta = 0; + WearStatusTypeDef retSta = vc9202_drv_get_wear_status(); + if( WEAR_STA_DROP == retSta ) + { + sta = 0; + } + else + { + sta = 1; + } + return sta; +} + +/**********************************************************************/ + +#define ENABLE_GSENSOR 0 +/* gsensor 相关的示例代码 */ +#if ENABLE_GSENSOR +typedef struct +{ + int16_t AXIS_X; + int16_t AXIS_Y; + int16_t AXIS_Z; +} AxesRaw_t; +static void gsensor_data_match(AxesRaw_t *org_data, GsensorTypeDef *pGsensorParam, unsigned char ppg_size) +{ + const unsigned char arry10[] = {0, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12}; + const unsigned char arry20[] = {0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}; + /* ppglen = 10 抽样 */ + if( 10<=ppg_size ) + { + for (uint8_t i = 0; i < ppg_size; i++) + { + pGsensorParam->gsensor_x_axis[i] = org_data[arry10[i]].AXIS_X; + pGsensorParam->gsensor_y_axis[i] = org_data[arry10[i]].AXIS_Y; + pGsensorParam->gsensor_z_axis[i] = org_data[arry10[i]].AXIS_Z; + } + } + else /* ppglen = 20 、25抽样 */ + { + for (uint8_t i = 0; i < ppg_size; i++) + { + pGsensorParam->gsensor_x_axis[i] = org_data[arry20[i]].AXIS_X; + pGsensorParam->gsensor_y_axis[i] = org_data[arry20[i]].AXIS_Y; + pGsensorParam->gsensor_z_axis[i] = org_data[arry20[i]].AXIS_Z; + } + } +} +#endif + +static void get_gsensor_data( GsensorTypeDef *pGsensorParam, unsigned char *pgsensor_len, unsigned char ppg_size ) +{ + /* + Gsensor精度要求 ReadGsensorFIFO(+-4G11bit or +-8G12bit 256/g) + 一个fifo周期为800ms,20组ppg数据,需要获取800ms内至少获取ppgCount组gsensor数据(40ms*20),用于匹配20组ppg信号,此函数需要根据用户实际的gsensor进行匹配处理。 + */ +#if ENABLE_GSENSOR + unsigned char i = 0; + unsigned char gsensor_len = 0; + AxesRaw_t gsensor_buff[40] = {0}; + pGsensorParam->sampling_rate = 25; + pGsensorParam->sensing_range = 4; + pGsensorParam->effective_bits = 12; + // stk8321_get_fifo_data(&gsensor_len, gsensor_buff); + if (ppg_size == 0) + { + *pgsensor_len = gsensor_len; + /* 不抽样 */ + for (i = 0; i < *pgsensor_len; i++) + { + pGsensorParam->gsensor_x_axis[i] = gsensor_buff[i].AXIS_X; + pGsensorParam->gsensor_y_axis[i] = gsensor_buff[i].AXIS_Y; + pGsensorParam->gsensor_z_axis[i] = gsensor_buff[i].AXIS_Z; + } + } + /* æ­¤demo中gsensor的采样频率高于ppg采样,所以正常情况下,gsensor的数量一定是大于ppg的数据。特殊情况需要考虑ppg数量大于gsensor数量。需要对gsensor数据进行补长 */ + if( (gsensor_len>=ppg_size)&&(ppg_size!=0) ) + { + *pgsensor_len = ppg_size; + // gsensor_data_match( gsensor_buff,pGsensorParam, ppg_size ); + } + else if( (gsensor_lengsensor_x_axis[i] = gsensor_buff[i].AXIS_X; + pGsensorParam->gsensor_y_axis[i] = gsensor_buff[i].AXIS_Y; + pGsensorParam->gsensor_z_axis[i] = gsensor_buff[i].AXIS_Z; + } + for( i=gsensor_len; igsensor_x_axis[i] = gsensor_buff[gsensor_len-1].AXIS_X; + pGsensorParam->gsensor_y_axis[i] = gsensor_buff[gsensor_len-1].AXIS_Y; + pGsensorParam->gsensor_z_axis[i] = gsensor_buff[gsensor_len-1].AXIS_Z; + } + } +#endif +} + +static unsigned int vc9202_usr_gsensor_actuating_quantity(int x_axis, int y_axis, int z_axis) +{ + unsigned int ret_atc = 0; + unsigned int abs_sum_current = 0; + static unsigned int abs_sum_last = 0; + if(x_axis < 0) + { + x_axis = -x_axis; + } + if(y_axis < 0) + { + y_axis = -y_axis; + } + if(z_axis < 0) + { + z_axis = -z_axis; + } + abs_sum_current = x_axis + y_axis + z_axis; + if(abs_sum_last&&abs_sum_current) + { + if( ABS(abs_sum_last,abs_sum_current)>=40 ) + { + ret_atc = 1; + } + } + abs_sum_last = abs_sum_current; + /* 存在运动量时处理,释放掉心率算法摘下的状态 */ + if( ret_atc ) + { + vc9202_drv_set_algo_wear_status( WEAR_STA_HOLD ); + } + vc9202_drv_set_gsensor_actuating_quantity( ret_atc ); + return ret_atc; +} + +static short int alg_calculate_hr_handler( short int last_hr_val, SamplingDataTypeDef *pdata, unsigned char ppg_size, GsensorTypeDef *pgsensor, AlgoSportMode hr_alg_mode ) +{ + unsigned char i=0; + short int heart_rate_value = last_hr_val; + AlgoInputData_t alg_i_data={0}; + AlgoOutputData_t alg_o_data={0}; + /* 如果没有fifo则直接推出,此处return需要返回上一次的值 */ + if( ppg_size <= 0 ) + { + return heart_rate_value; + } + if(ppg_size>=40) + { + ppg_size = 40; + } + // printf("ppg_size : %d \n",ppg_size); + alg_i_data.envSample = pdata->env_val[0]; + for( i=0; igsensor_x_axis[i]; + alg_i_data.axes.y = pgsensor->gsensor_y_axis[i]; + alg_i_data.axes.z = pgsensor->gsensor_z_axis[i]; + alg_i_data.ppgSample = pdata->ppg_data[i]; + printf("pdata->ppg_data[%d] : %d \n",i,pdata->ppg_data[i]); + // printf(" i : alg_i_data.axes.x : %d ,alg_i_data.axes.y : %d , alg_i_data.axes.z : %d \n",i,alg_i_data.axes.x,alg_i_data.axes.y,alg_i_data.axes.z); + Algo_Input( &alg_i_data, 1000/25, hr_alg_mode, 1, 1 ); + } + Algo_Output( &alg_o_data ); + // printf("alg_o_data.hrData : %d \n",alg_o_data.hrData); + if( -1 == alg_o_data.hrData ) + { + vc9202_drv_set_algo_wear_status( WEAR_STA_DROP ); + } + else if( alg_o_data.hrData > 0 ) + { + heart_rate_value = alg_o_data.hrData; + } + return heart_rate_value; +} + +static unsigned char alg_calculate_spo2_handler( unsigned char last_spo2_val , SamplingDataTypeDef *pdata, unsigned char ppg_size, GsensorTypeDef *pgsensor, unsigned char spo2_alg_mode ) +{ + unsigned char i=0; + unsigned char spo2_value = last_spo2_val; + float ret_spo2 = 0.0f; + float ir_PPG = 0.0f; + float red_PPG = 0.0f; + float sport_flag = 0.0f; + /* 如果没有fifo则直接推出,此处return需要返回上一次的值 */ + if( ppg_size<=0 ) + { + return spo2_value; + } + for( i=0; ippg_data[i*2]; + red_PPG = (float)pdata->ppg_data[i*2+1]; + // ret_spo2 = vcSpo2Calculate( red_PPG, ir_PPG ); + ret_spo2 = spo2Algo(red_PPG,ir_PPG,1); + sport_flag = vcSportMotionCalculate( pgsensor->gsensor_x_axis[i], pgsensor->gsensor_y_axis[i], pgsensor->gsensor_z_axis[i] ); + if ( (-1!=ret_spo2)&&(!sport_flag)) + { + spo2_value = (unsigned char)ret_spo2; + } + } + return spo2_value; +} + +static void factory_test_function( SamplingDataTypeDef *pdata ) +{ + // /* 最大电流 */ + // unsigned char ledMaxCur = pdata->led_cur[0]; //最大电流,黑箱通道2 + // /* 获取测试数据 */ + // unsigned int pre0 = pdata->apre_val[0];//绿灯强度,漏光模式通道1 + // unsigned int ps = pdata->aps_val;//红外强度,灰卡模式通道3 + // /* 温度 */ + // signed short int temp0 = pdata->temp_val_i; + // signed short int temp1 = pdata->temp_val_o; + // /* compare abs(bio0,bio1) */ + // unsigned short int bio0 = pdata->bio_val_o; + // unsigned short int bio1 = pdata->bio_val_o; +} +static void fpc_test_function(SamplingDataTypeDef *pdata) +{ + extern VC9202TestInfoTypeDef vc9202_test_info; + /* 等待5次中断工作稳定 */ + if (vc9202_test_info.wait_cnt <= 5) + { + vc9202_test_info.wait_cnt++; + return; + } + /* 步骤一:常规数据读取判断是否在范围内,此处得数据请根据实际设计对参数进行调整 */ + if (0 == vc9202_test_info.step1_ret) + { + if (vc9202_test_info.icid == 0x21 && vc9202_test_info.reason & 0x3f && + vc9202_test_info.frequency >= 16000 && vc9202_test_info.frequency <= 24000 ) + { + vc9202_test_info.step1_ret = 1; + } + else + { + vc9202_test_info.step1_ret = 2; + } + } + /* 步骤二:bio/ps数据读取判断是否在范围内 */ + else if (0 == vc9202_test_info.step2_ret) + { + if (vc9202_test_info.biovalue_max >= 60 && vc9202_test_info.ps_value >= 200) + { + vc9202_test_info.step2_ret = 1; + } + } + /*注意:上述方式会一直等待,直到检测通过,也可以使用人为触发记录,进行比较两次触发时的记录值差距是否满足。 + 记录两次不同状态(模拟佩戴和摘下)下时的pdata->bio_val_o,比较差值 + 记录模拟佩戴下的pdata->ps_val,判断是否满足PS范围 + */ + /* 其中step1_ret和step2_ret,分别表示测试是否通过,由于step2_ret需要检测变化量,所以需要一直等待操作,如果测试通过后,驱动会控制红绿灯交替闪烁 */ + printf("[FAT]icid=%#x,reason=%#x,frequency=%d,temperature=%d \n", vc9202_test_info.icid, vc9202_test_info.reason, vc9202_test_info.frequency, vc9202_test_info.temperature); + printf("[FAT]biovalue_max=%d,ps=%d,gap[%d,%d] \n", vc9202_test_info.biovalue_max, vc9202_test_info.ps_value, vc9202_test_info.biovalue_gap[0], vc9202_test_info.biovalue_gap[1]); + printf("[FAT]-ret(%d,%d) \n", vc9202_test_info.step1_ret, vc9202_test_info.step2_ret); +} + +void vc9202_usr_interrupt_handler( AlgoSportMode hr_alg_mode, unsigned char spo2_alg_mode ) +{ + unsigned char wear_status = WEAR_STA_NONE; + SamplingDataTypeDef data = {0}; + GsensorTypeDef gsensor_data = {0}; + unsigned char gsensor_size = 0; + unsigned char ppg_size = 0; + VC9202DrvRetTypeDef ret = vc9202_drv_get_sampling_data( &data, &ppg_size ); + printf("ppg_size : %d work_mode : %d ret : %d\n",ppg_size,work_mode,ret); + switch ( work_mode ) + { + case WORK_MODE_HR: + printf("11111 vc9202_usr_get_int_reason : 0x%x \n",vc9202_usr_get_int_reason()); + if( vc9202_usr_get_int_reason()&(VC9202_EVENT_PS_MARK|VC9202_EVENT_FIFO_MARK) ) + { + wear_status = vc9202_usr_get_wear_status(); + printf("11111 wear_status : %d \n",wear_status); + get_gsensor_data(&gsensor_data, &gsensor_size, ppg_size); + if( gsensor_size>0 ) + { + vc9202_usr_gsensor_actuating_quantity( gsensor_data.gsensor_x_axis[0], gsensor_data.gsensor_y_axis[0], gsensor_data.gsensor_z_axis[0] ); + } + if(wear_status) + { + vc9202_alg_results.hr_rate = alg_calculate_hr_handler( vc9202_alg_results.hr_rate, &data, ppg_size, &gsensor_data, hr_alg_mode ); + printf("vc9202_alg_results.hr_rate : %d \n",vc9202_alg_results.hr_rate); + vc9202_debug_send_heart_rate_data( &data, &gsensor_data, vc9202_alg_results.hr_rate, hr_alg_mode, ppg_size ); + } + else + { + vc9202_alg_results.hr_rate = 0; + Algo_Init(); + } + } + break; + + case WORK_MODE_SPO2: + if( vc9202_usr_get_int_reason()&(VC9202_EVENT_PS_MARK|VC9202_EVENT_FIFO_MARK) ) + { + wear_status = vc9202_usr_get_wear_status(); + get_gsensor_data(&gsensor_data, &gsensor_size, ppg_size); + if(wear_status) + { + vc9202_alg_results.spo2_val = alg_calculate_spo2_handler( vc9202_alg_results.spo2_val, &data, ppg_size/2, &gsensor_data, spo2_alg_mode ); + printf("vc9202_alg_results.spo2_val : %d \n",vc9202_alg_results.spo2_val); + vc9202_debug_send_spo2_data( &data, &gsensor_data, vc9202_alg_results.spo2_val, ppg_size/2 ); + } + else + { + vc9202_alg_results.spo2_val = 0; + // vcSpo2AlgoInit(); + spo2AlgoInit(); + vcSportMotionAlgoInit(); + } + } + break; + + case WORK_MODE_HRV: + if( vc9202_usr_get_int_reason()&(VC9202_EVENT_PS_MARK|VC9202_EVENT_FIFO_MARK) ) + { + wear_status = vc9202_usr_get_wear_status(); + get_gsensor_data(&gsensor_data, &gsensor_size, ppg_size); + if(wear_status) + { + vc9202_alg_results.hr_rate = alg_calculate_hr_handler( vc9202_alg_results.hr_rate, &data, ppg_size, &gsensor_data, hr_alg_mode ); + /* 校验+算法HRV */ + } + else + { + vc9202_alg_results.hr_rate = 0; + Algo_Init(); + /* 清空数据+初始化算法 */ + } + } + break; + + case WORK_MODE_STRESS: + if( vc9202_usr_get_int_reason()&(VC9202_EVENT_PS_MARK|VC9202_EVENT_FIFO_MARK) ) + { + wear_status = vc9202_usr_get_wear_status(); + get_gsensor_data(&gsensor_data, &gsensor_size, ppg_size); + if(wear_status) + { + /* 校验+算法STRESS */ + } + else + { + /* 清空数据+初始化算法 */ + } + } + break; + + case WORK_MODE_WEAR: + if( vc9202_usr_get_int_reason() & VC9202_EVENT_PS_MARK ) + { + wear_status = vc9202_usr_get_wear_status(); + get_gsensor_data(&gsensor_data, &gsensor_size, ppg_size); + if( gsensor_size>0 ) + { + vc9202_usr_gsensor_actuating_quantity( gsensor_data.gsensor_x_axis[0], gsensor_data.gsensor_y_axis[0], gsensor_data.gsensor_z_axis[0] ); + } + /* 根据佩戴状态添加对应的处理代码 */ + if(wear_status) + { + printf("111 wear \n"); + } + else + { + printf("222 nowear \n"); + } + } + break; + case WORK_MODE_FACTORY: + factory_test_function( &data ); + break; + case WORK_MODE_FPC_TEST: + fpc_test_function(&data); + break; + + default: + break; + } +} + +#if 0 +int main(int argc, char *argv[]) +{ + board_driver_init(); //MCU外设初始化 + InitParamTypeDef config={400,WORK_MODE_HR}; //修改fifo间隔时间(关系到fifolength), 设定工作模式 + vc9102_usr_init( &config ); + while(1) + { + if( irq_msg ) + { + //调用时传入心率算法模式(日常、运动...)和血氧算法参数。 + vc9102_usr_interrupt_handler( algo_mode, 1 ); + } + } + return 0 +} +#endif + diff --git a/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_user_debug.c b/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_user_debug.c new file mode 100644 index 000000000..c40192b8c --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc9202/src/vc9202_user_debug.c @@ -0,0 +1,188 @@ +/********************************************************************************************************* + * Copyright(c) 2022, vcare Corporation. All rights reserved. + ********************************************************************************************************** + * @file vc9202_user_debug.c + * @brief example,debug send original data + * @details + * @author + * @date + * @version + *********************************************************************************************************/ + +#include "vc9202_common.h" +#include "vc9202_user.h" + +// #include "os_timer.h" +// #include +// #include "wristband_global_data.h" +// extern RtkWristBandSysType_t RtkWristbandSys; + +static unsigned char ble_buf[1024]={0}; +static unsigned short int buf_size = 0; +static unsigned short int buf_index = 0; +#define BLE_PACK_SIZE 160 +#define TIMER_BLE_GAP 100 + +/********************************************************************************************************** + * 对苹果手机APP不能连续接收长段数据,利用软件定时器,进行分包发送 + * 创建软件定时器及数据分包处理 + */ +static void send_data_by_ble( unsigned char *pbuf, unsigned short int size ) +{ + // if (RtkWristbandSys.gap_conn_state == GAP_CONN_STATE_CONNECTED) + // { + // hr_ble_service_send_v3_notify(RtkWristbandSys.wristband_conn_id, hr_ser_id, pbuf, size); + // DBG_DIRECT("ble_send_buffer_size=%d",size); + // } +} + +static int timer_send_data_function(void) +{ + unsigned short int residue_size = 0; + //数据发送已经完成 + if( buf_index >= buf_size ) + { + return 0; + } + //判断剩余数据长度是否还需要分包 + residue_size = buf_size-buf_index; + if( residue_size <= BLE_PACK_SIZE ) + { + send_data_by_ble( &ble_buf[buf_index], residue_size ); + return 0; + } + else + { + send_data_by_ble( &ble_buf[buf_index], BLE_PACK_SIZE ); + buf_index +=BLE_PACK_SIZE; + return 1; + } +} + + +/* 创建软件定时器,如果数据太长通过软件定时器发送 */ +#define TIMER_ID 0xf0 +static void *debug_send_timer = NULL; +static void debug_send_timer_start(void) +{ + if( debug_send_timer ) + { + // os_timer_restart(&debug_send_timer, TIMER_BLE_GAP); + } +} +static void debug_send_timer_stop(void) +{ + if( debug_send_timer ) + { + os_timer_stop(&debug_send_timer); + } +} + +static void debug_send_timer_callback( void *p_handle ) +{ + if( timer_send_data_function() ) + { + /* 定时器继续循环 */ + // os_timer_restart(&debug_send_timer, TIMER_BLE_GAP); + } + else + { + /* 关闭定时器 */ + debug_send_timer_stop(); + } +} + +void vc9202_debug_send_timer_init( void ) +{ + if(debug_send_timer == NULL) + { + os_timer_create(&debug_send_timer, "debug_send_timer", TIMER_ID, TIMER_BLE_GAP, false, debug_send_timer_callback); + } + // os_timer_restart(&debug_send_timer, TIMER_BLE_GAP); + os_timer_stop(&debug_send_timer); +} + +static void debug_orginal_data_send( unsigned short int size ) +{ + buf_index = 0; + buf_size = size; + if( buf_size <= BLE_PACK_SIZE ) + { + send_data_by_ble( ble_buf, size ); + } + else + { + send_data_by_ble( ble_buf, BLE_PACK_SIZE ); + buf_index += BLE_PACK_SIZE; + debug_send_timer_start(); + } +} + +/**********************************************************************************************************/ +#define ORIGINAL_DATA_DEBUG_ENABLE (1) + +#if ORIGINAL_DATA_DEBUG_ENABLE + +void vc9202_debug_send_heart_rate_data( SamplingDataTypeDef* presult ,GsensorTypeDef* paxis, unsigned char heart_rate, unsigned char algo_mode, unsigned char ppgsize ) +{ + unsigned short int i = 0; + for( i=0; ippg_data[i] >> 8; + ble_buf[i*16 + 1 ] = presult->ppg_data[i] & 0xff; + ble_buf[i*16 + 2 ] = paxis->gsensor_x_axis[i] >> 8; + ble_buf[i*16 + 3 ] = paxis->gsensor_x_axis[i] & 0xff; + ble_buf[i*16 + 4 ] = paxis->gsensor_y_axis[i] >> 8; + ble_buf[i*16 + 5 ] = paxis->gsensor_y_axis[i] & 0xff; + ble_buf[i*16 + 6 ] = paxis->gsensor_z_axis[i] >> 8; + ble_buf[i*16 + 7 ] = paxis->gsensor_z_axis[i] & 0xff; + ble_buf[i*16 + 8 ] = heart_rate; + ble_buf[i*16 + 9 ] = presult->pd_res[0]; + ble_buf[i*16 + 10] = presult->led_cur[0]; + ble_buf[i*16 + 11] = presult->env_val[2]; + ble_buf[i*16 + 12] = presult->pre_val[0]; + ble_buf[i*16 + 13] = presult->bio_val_o>>8; + ble_buf[i*16 + 14] = presult->bio_val_o&0xff; + ble_buf[i*16 + 15] = presult->aps_val; + } + if( ppgsize > 0 ) + { + debug_orginal_data_send( ppgsize*16 ); + } +} + +/**************************************************************************************** + * + */ +void vc9202_debug_send_spo2_data( SamplingDataTypeDef* presult ,GsensorTypeDef* paxis, unsigned char spo2_val, unsigned char ppgsize ) +{ + unsigned short int i = 0; + for( i=0; ippg_data[i*2] >> 8; + ble_buf[i*16 + 1 ] = presult->ppg_data[i*2] & 0xff; + ble_buf[i*16 + 2 ] = paxis->gsensor_x_axis[i] >> 8; + ble_buf[i*16 + 3 ] = paxis->gsensor_x_axis[i] & 0xff; + ble_buf[i*16 + 4 ] = paxis->gsensor_y_axis[i] >> 8; + ble_buf[i*16 + 5 ] = paxis->gsensor_y_axis[i] & 0xff; + ble_buf[i*16 + 6 ] = presult->pre_val[0]; + ble_buf[i*16 + 7 ] = presult->pre_val[1]; + ble_buf[i*16 + 8 ] = presult->ppg_data[i*2+1] >> 8; + ble_buf[i*16 + 9 ] = presult->ppg_data[i*2+1] & 0xff; + ble_buf[i*16 + 10] = spo2_val; + ble_buf[i*16 + 11] = presult->env_val[2]; + ble_buf[i*16 + 12] = presult->pd_res[0]; + ble_buf[i*16 + 13] = presult->led_cur[0]; + ble_buf[i*16 + 14] = presult->pd_res[1]; + ble_buf[i*16 + 15] = presult->led_cur[1]; + } + if( ppgsize > 0 ) + { + debug_orginal_data_send( ppgsize*16 ); + } +} + +#endif + + + diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/CMakeLists.txt b/code/sdk/zhihui/drivers/ppg/vc921x/CMakeLists.txt new file mode 100644 index 000000000..d91011b88 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/CMakeLists.txt @@ -0,0 +1,62 @@ +#=============================================================================== +# @brief cmake file +# Copyright (c) @CompanyNameMagicTag 2022-2023. All rights reserved. +#=============================================================================== +set(COMPONENT_NAME "ppg_sensor_vc921x") + +if("PPG_VC921X" IN_LIST DEFINES) +set(SOURCES ${SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/src/vc921x_core.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/vc921x_driver.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/vc921x_original_data_send.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/vc921x_user_app.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/vc921x_hal.c +) +set(PUBLIC_HEADER + ${CMAKE_CURRENT_SOURCE_DIR}/inc + +) + +else() +set(SOURCES ${SOURCES} +) +set(PUBLIC_HEADER + ${CMAKE_CURRENT_SOURCE_DIR} +) +endif() + + +set(PRIVATE_HEADER +) + +set(PRIVATE_DEFINES +) + +set(PUBLIC_DEFINES +) + +# use this when you want to add ccflags like -include xxx +set(COMPONENT_PUBLIC_CCFLAGS +) + +set(COMPONENT_CCFLAGS +) + +set(WHOLE_LINK + true +) + +set(MAIN_COMPONENT + false +) + +if("PPG_VC921X" IN_LIST DEFINES) + find_library(LIB_PATH_VC921X_1 lib/lib_BIO_v141_int_miniSDK_NA_01.a ${CMAKE_CURRENT_SOURCE_DIR}) + find_library(LIB_PATH_VC921X_2 lib/lib_hr_vH102_int_miniSDK_NA_01.a ${CMAKE_CURRENT_SOURCE_DIR}) + find_library(LIB_PATH_VC921X_3 lib/lib_spo2_vc30FS_float_V07_miniSDK_NA_01.a ${CMAKE_CURRENT_SOURCE_DIR}) + find_library(LIB_PATH_VC921X_4 lib/lib_sportMotion_std_int_miniSDK_NA_01.a ${CMAKE_CURRENT_SOURCE_DIR}) + + target_link_libraries(${TARGET_NAME} PRIVATE -Wl,--whole-archive ${LIB_PATH_VC921X_1} ${LIB_PATH_VC921X_2} ${LIB_PATH_VC921X_3} ${LIB_PATH_VC921X_4} PRIVATE -Wl,--whole-archive) +endif() +build_component() + diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/BIODetect_V14_1.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/BIODetect_V14_1.h new file mode 100644 index 000000000..9b14b9d68 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/BIODetect_V14_1.h @@ -0,0 +1,19 @@ +/* + * + * Created on: 2023-6-04 + * Author: runningHorse + */ + +#ifndef BIODETECT +#define BIODETECT + +#include +#include + +void f_BIOInit(int32_t gapDeltaBIO, int32_t gapDeltaPS); + +uint8_t f_BIOdetect(int32_t inputBIO, int32_t inputPS, int32_t PSth); + +char* bioVersion(void); + +#endif /* BIODETECT */ \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/algo.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/algo.h new file mode 100644 index 000000000..3e9531379 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/algo.h @@ -0,0 +1,70 @@ + +#ifndef APP_ALGO_ALGO_H +#define APP_ALGO_ALGO_H + +#include +#include + +typedef enum +{ + SPORT_TYPE_NORMAL =0x00, // 日常 + SPORT_TYPE_RUNNING =0x01, // 跑步 + SPORT_TYPE_RIDE_BIKE =0X02, // 骑行 + SPORT_TYPE_JUMP_ROPE =0X03, // 跳绳 + SPORT_TYPE_SWIMMING =0X04, // 游泳 + SPORT_TYPE_BADMINTON =0X05, // 羽毛球 + SPORT_TYPE_TABLE_TENNIS=0X06, // 乒乓球 + SPORT_TYPE_TENNIS =0X07, // 网球 + SPORT_TYPE_CLIMBING =0X08, // 爬山 + SPORT_TYPE_WALKING =0X09, // 徒步 + SPORT_TYPE_BASKETBALL =0X0A, // 篮球 + SPORT_TYPE_FOOTBALL =0X0B, // 足球 + SPORT_TYPE_BASEBALL =0X0C, // 棒球 + SPORT_TYPE_VOLLEYBALL =0X0D, // 排球 + SPORT_TYPE_CRICKET = 0X0E, // 板球 + SPORT_TYPE_RUGBY = 0X0F, // 橄榄球 + SPORT_TYPE_HOCKEY =0X10, // 曲棍球 + SPORT_TYPE_DANCE = 0X11, // 跳舞 + SPORT_TYPE_SPINNING = 0X12, // 动感单车 + SPORT_TYPE_YOGA = 0X13, // 瑜伽 + SPORT_TYPE_SIT_UP =0X14, // 仰卧起坐 + SPORT_TYPE_TREADMILL =0X15, // 跑步机 + SPORT_TYPE_GYMNASTICS =0X16, // 体操 + SPORT_TYPE_BOATING = 0X17, // 划船 + SPORT_TYPE_JUMPING_JACK = 0X18, // 开合跳 + SPORT_TYPE_FREE_TRAINING = 0X19,// 自由训练 + SPORT_TYPE_PERIODIC_MONITORING = 0X1A,// 定时监测模式 +} AlgoSportMode; +//未出现在上表中的运动形式均先以跑步输入 + + +typedef struct +{ + int32_t x; + int32_t y; + int32_t z; +}AlgoAxesData_t; + +typedef struct +{ + int32_t hrData; + int32_t reliability; +}AlgoOutputData_t; + +typedef struct +{ + AlgoAxesData_t axes; + int32_t ppgSample; + int32_t envSample; +}AlgoInputData_t; + +void Algo_Init(void); +void Algo_Input(AlgoInputData_t *pInputData,int32_t gaptime,AlgoSportMode sportMode,int16_t surfaceRecogMode,int16_t opticalAidMode); +void Algo_Output(AlgoOutputData_t *pOutputData); + + + +#endif /* APP_ALGO_ALGO_H_ */ + + + diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/spo2Algo_16bit.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/spo2Algo_16bit.h new file mode 100644 index 000000000..a838c5ede --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/spo2Algo_16bit.h @@ -0,0 +1,57 @@ +/* + * File: main.h + * + * MATLAB Coder version : 3.3 + * C/C++ source code generated on : 09-Sep-2020 19:11:59 + */ + +/*************************************************************************/ +/* This automatically generated example C main file shows how to call */ +/* entry-point functions that MATLAB Coder generated. You must customize */ +/* this file for your application. Do not modify this file directly. */ +/* Instead, make a copy of this file, modify it, and integrate it into */ +/* your development environment. */ +/* */ +/* This file initializes entry-point function arguments to a default */ +/* size and value before calling the entry-point functions. It does */ +/* not store or use any values returned from the entry-point functions. */ +/* If necessary, it does pre-allocate memory for returned values. */ +/* You can use this file as a starting point for a main function that */ +/* you can deploy in your application. */ +/* */ +/* After you copy the file, and before you deploy it, you must make the */ +/* following changes: */ +/* * For variable-size function arguments, change the example sizes to */ +/* the sizes that your application requires. */ +/* * Change the example values of function arguments to the values that */ +/* your application requires. */ +/* * If the entry-point functions return values, store these values or */ +/* otherwise use them as required by your application. */ +/* */ +/*************************************************************************/ +#ifndef SPO2ALGO_16bit_H +#define SPO2ALGO_16bit_H + +/* Include Files */ +#include +#include +#include +#include +#include +//#include "rt_nonfinite.h" +//#include "rtwtypes.h" +//#include "spo2_algo_interface_types.h" + +/* Function Declarations */ +extern float vcSpo2Calculate_16bit(float r_ppg, float ir_ppg, float model); +extern int vcSpo2AlgoInit_16bit(void); +extern void vcSpo2AlgoTerminate_16bit(void); +extern char * +version_info_16bit(void); +#endif + +/* + * File trailer for main.h + * + * [EOF] + */ diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_core.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_core.h new file mode 100644 index 000000000..27f915d00 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_core.h @@ -0,0 +1,325 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-09-22 17:39:10 + * @LastEditTime: 2024-03-21 15:58:48 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ + +#ifndef __VC921x_CORE_H__ +#define __VC921x_CORE_H__ + +#ifndef VC_PACKED +#define VC921x_PACKED //__attribute__((packed)) +#endif + +/*=============================================================== +regist_list_map start... +================================================================*/ +#define CORE921x_DEV_VER_ID_H_ADDR (0x00) +#define CORE921x_FDY_EXT_ID_L_ADDR (0x01) +#define CORE921x_WORK_STATUS1_ADDR (0x02) +#define CORE921x_WORK_STATUS2_ADDR (0x03) +#define CORE921x_FIFO_W_INDEX_ADDR (0x04) +#define CORE921x_OSC_CNT0_H_ADDR (0x05) +#define CORE921x_OSC_CNT0_L_ADDR (0x06) +#define CORE921x_OSC_CNT1_H_ADDR (0x07) +#define CORE921x_OSC_CNT1_L_ADDR (0x08) + +/**************************************************************** + * ic sample param structure, 0x09 - 0x23 = 28byte + ***************************************************************/ +#define CORE921x_SAMPLE_DATA_ADDR_BASE (0x09) +typedef struct +{ + /* env data, 6byte_12bit[12,0] */ + unsigned char slot0_envh6; + unsigned char slot0_envl6; + unsigned char slot1_envh6; + unsigned char slot1_envl6; + unsigned char slot2_envh6; + unsigned char slot2_envl6; + /* ps data 3byte */ + unsigned char slot0_ps; + unsigned char slot1_ps; + unsigned char slot2_ps; + /* bio data 4byte */ + unsigned char bioinn_valh; + unsigned char bioinn_vall; + unsigned char bioext_valh; + unsigned char bioext_vall; + /* temp data 6byte */ + unsigned char tempslot0_valh; /* temp0-inside */ + unsigned char tempslot0_vall; + unsigned char tempslot1_valh; /* temp1-external */ + unsigned char tempslot1_vall; + unsigned char tempref_valh; /* temp_ext_ref */ + unsigned char tempref_vall; +} VC921x_PACKED core921x_reg_result_t; + +/**************************************************************** + * ic config param structure.[SLOT_CONFIG] + ***************************************************************/ +#define CORE921x_CONFIG_SLOT_ADDR_BASE (0x40) +typedef struct +{ + unsigned char oversample : 3; + unsigned char freq_isps : 1; + unsigned char rxgain : 3; + unsigned char enable : 1; +} VC921x_PACKED core921x_slot_ppg_cfg0bit; +typedef struct +{ + unsigned char env_time : 4; + unsigned char sample_time : 2; + unsigned char txch : 2; +} VC921x_PACKED core921x_slot_ppg_cfg1bit; +typedef struct +{ + unsigned char current : 7; + unsigned char storefifo : 1; +} VC921x_PACKED core921x_slot_cfg2bit; +typedef struct +{ + unsigned char sc : 4; + unsigned char resistance : 4; +} VC921x_PACKED core921x_slot_cfg3bit; +/*---[slot regist map]---*/ +typedef struct +{ + union /* 1_byte */ + { + unsigned char byte; + core921x_slot_ppg_cfg0bit bits; + } slot_cfg0; + union /* 2_byte */ + { + unsigned char byte; + core921x_slot_ppg_cfg1bit bits; + } slot_cfg1; + union /* 3_byte */ + { + unsigned char byte; + core921x_slot_cfg2bit bits; + } slot_cfg2; + union /* 4_byte */ + { + unsigned char byte; + core921x_slot_cfg3bit bits; + } slot_cfg3; +} VC921x_PACKED core921x_slot_config_t; + +/**************************************************************** + * ic config param structure.[PD_CONFIG + IRQ_RATE_CONFIG + IRQ_CONFIG] + ***************************************************************/ +#define CORE921x_INT_RATE_IRQ_CONFIG_ADDR_BASE (0x4C) + +typedef struct +{ + unsigned char slot0_pdch : 2; + unsigned char slot1_pdch : 2; + unsigned char slot2_pdch : 2; + unsigned char reserved : 2; +} VC921x_PACKED core921x_pd_cfgbit; + +typedef struct +{ + unsigned char anti_current : 3; + unsigned char reserved1 : 1; + unsigned char deslot0 : 1; + unsigned char deslot1 : 1; + unsigned char deslot2 : 1; + unsigned char reserved2 : 1; +} VC921x_PACKED core921x_anti_cfgbit_1120mpw; + +typedef struct +{ + unsigned char anti_current : 3; + unsigned char deslot0 : 1; + unsigned char deslot1 : 1; + unsigned char deslot2 : 1; + unsigned char antiTxch : 2; +} VC921x_PACKED core921x_anti_cfgbit; + +typedef struct +{ + unsigned char cpd : 3; + unsigned char fifo_en : 1; + unsigned char fifoformat : 1; + unsigned char bio_disfreqHop : 1; /* del function */ + unsigned char i2crelease_en : 1; + unsigned char deenv_en : 1; +} VC921x_PACKED core921x_com_cfgbit; +typedef struct +{ + unsigned char ppgthl : 4; + unsigned char ppgthh : 4; +} VC921x_PACKED core921x_ppgth_cfgbit; + +typedef struct +{ + unsigned char intppg_en : 1; + unsigned char intov_en : 1; + unsigned char intfifo_en : 1; + unsigned char intfreq2_en : 1; + unsigned char intlen : 2; + unsigned char intdir : 1; + unsigned char intmode : 1; +} VC921x_PACKED core921x_int_cfgbit; + +typedef struct +{ + /* 1byte pdch_config */ + union + { + unsigned char byte; + core921x_pd_cfgbit bits; + } pd_cfg; + /* 2_byte:anti_slot */ + union + { + unsigned char byte; + core921x_anti_cfgbit bits; + }; + /* 3_byte:com&pdcfg */ + union + { + unsigned char byte; + core921x_com_cfgbit bits; + } com_cfg; + /* 4~7_byte */ + unsigned char freq1_divH; + unsigned char freq1_divL; /* ppg_freq1_div = 20K / rete1 */ + unsigned char freq2_div; /* ps_freq2_div = ppg_freq / rate2 */ + unsigned char freq3_div; /* fifo_freq3_div = ps_freq / rate3 */ + /* 8_byte */ + union + { + unsigned char byte; + core921x_ppgth_cfgbit bits; + } ppgth_cfg; + /* 9_byte */ + union + { + unsigned char byte; + core921x_int_cfgbit bits; + } int_cfg; +} VC921x_PACKED core921x_common_config_t; + +/**************************************************************** + * ic config param structure.[EXTRA_CONFIG] + ***************************************************************/ +#define CORE921x_EXTRA_CONFIG_ADDR_BASE (0x54) + +typedef struct +{ + /* byte1 */ + unsigned char bio_freq : 2; + unsigned char bio_pdch : 2; + unsigned char bio_mode : 2; /* 00/01 work in freq2,noFIFO/inFIFO, single sample data + 10/11 work in freq1,inFIFO, single sample data / the sum data on freq2 */ + unsigned char bio_inn_en : 1; + unsigned char bio_ext_en : 1; +} VC921x_PACKED core921x_bio_cfg0bit; +typedef struct +{ + unsigned char bio_time : 3; + unsigned char bio_capadd : 5; +} VC921x_PACKED core921x_bio_cfg1bit; + +typedef struct +{ + unsigned char temp_currset : 3; + unsigned char temp_reserved_1 : 1; + unsigned char temp_slotch : 2; + unsigned char temp_reserved_2 : 1; + unsigned char temp_enable : 1; +} VC921x_PACKED core921x_temp_cfgbit; +typedef struct +{ + union /* 1_byte */ + { + unsigned char byte; + core921x_bio_cfg0bit bits; + } bio_cfg0; + union /* 2_byte */ + { + unsigned char byte; + core921x_bio_cfg1bit bits; + } bio_cfg1; + union /* 3_byte */ + { + unsigned char byte; + core921x_temp_cfgbit bits; + } temp0_cfg; + union /* 4_byte */ + { + unsigned char byte; + core921x_temp_cfgbit bits; + } temp1_cfg; +} VC921x_PACKED core921x_extra_config_t; // core921x_extra_config_t + +typedef struct +{ + unsigned char ovtype : 1; + unsigned char cmpset : 2; + unsigned char reserved : 1; + unsigned char ppg_resulution : 2; + unsigned char adc_speed : 1; /* reserved */ + unsigned char env_clkdiv : 1; /* reserved */ +} VC921x_PACKED core921x_adc_config_t; + +typedef struct +{ + unsigned char ovtype : 1; + unsigned char reserved : 3; + unsigned char ppg_resulution : 2; + unsigned char adc_speed : 1; /* reserved */ + unsigned char env_clkdiv : 1; /* reserved */ +} VC921x_PACKED core921x_adc_config_t_1120mpw; + +/**************************************************************** + * work_cmd & fifo_data structure + ***************************************************************/ +#define CORE921x_WORK_CMD_ADDR_BASE (0X7B) +#define CORE921x_WORK_CMD_SIZE (1) +/* fifo addr */ +#define CORE921x_FIFO_READADDR_INDEX (0xFE) +#define CORE921x_FIFO_DATA_ADDR (0xff) + +/**************************************************************** + * inline func: vc921x_core_memset,vc921x_core_memcpy + ***************************************************************/ +#ifndef NULL +#define NULL (void *)0 +#endif +static inline void *vc921x_core_memset(void *dest, int set, unsigned int n) +{ + if (dest == NULL || n <= 0) + { + return NULL; + } + char *pdest = (char *)dest; + while (n-- > 0) + { + *pdest++ = set; + } + return dest; +} +static inline void *vc921x_core_memcpy(void *dest, void *src, unsigned int n) +{ + if (dest == NULL || src == NULL) + { + return NULL; + } + char *pdest = (char *)dest; + char *psrc = (char *)src; + while (n--) + { + *pdest++ = *psrc++; + } + return dest; +} + +#endif /* __VC921x_CORE_H__ */ diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_core_com.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_core_com.h new file mode 100644 index 000000000..47c170662 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_core_com.h @@ -0,0 +1,458 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-09-22 17:39:10 + * @LastEditTime: 2024-04-09 15:26:54 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ +//!!!!!!注意:本头文件不允许用户自己包含及调用相关的API,仅允许vc921x_driver.c包含,避免出错或者是产生的ROM占用 +//!!!!!!注意:本头文件不允许用户自己包含及调用相关的API,仅允许vc921x_driver.c包含,避免出错或者是产生的ROM占用 +#ifndef __VC921x_CORE_COM_H__ +#define __VC921x_CORE_COM_H__ + +#include "vc921x_sample_result.h" +#include "vc921x_driver_config.h" + +/* 如果需要指定变量到对应的字段在次定义,且重新编译 */ +#define CORE921x_DATA_RAM_SECTION //__attribute__((section(".core921xdata"))) +#define CORE921x_FUNC_RAM_SECTION //__attribute__((section(".core921xfunc"))) +/*============================================================================ + * CORE921x_DEBUG :debug interface + ===========================================================================*/ +// #define CORE921x_DEBUG_VERSION +/* 在编译库的时候,-D CORE921x_DEBUG_VERSION 对代码进行分版本生成库(debug/release) */ +#ifdef CORE921x_DEBUG_VERSION +extern void log_direct(unsigned int info, const char *fmt, ...); +#define CORE921x_DEBUG_LOG(fmt, ...) \ + do \ + { \ + log_direct((33 << 24), "[CORE921x_LOG]line(%d)" fmt, __LINE__, ##__VA_ARGS__); \ + } while (0) +#define CORE921x_DEBUG_ERR(fmt, ...) \ + do \ + { \ + log_direct((33 << 24), "[CORE921x_ERR]line(%d)" fmt, __LINE__, ##__VA_ARGS__); \ + } while (0) +#else +#define CORE921x_DEBUG_LOG(fmt, ...) +#define CORE921x_DEBUG_ERR(fmt, ...) +#endif + +#if 0 // CORE921x_DEBUG_LOG example +extern int (*vc921x_dbglog_user)(const char *, ...); +#define CORE921x_DEBUG_LOG(fmt, ...) \ + do \ + { \ + if (vc921x_dbglog_user) \ + vc921x_dbglog_user("[CORE921x_LOG]%s(%d)" fmt, __func__, __LINE__, ##__VA_ARGS__); \ + } while (0) +#define CORE921x_DEBUG_ERR(fmt, ...) \ + do \ + { \ + if (vc921x_dbglog_user) \ + vc921x_dbglog_user("[CORE921x_ERR]%s(%d)" fmt, __func__, __LINE__, ##__VA_ARGS__); \ + } while (0) +//#else +#define CORE921x_DEBUG_LOG(fmt, ...) +#define CORE921x_DEBUG_ERR(fmt, ...) +#endif + +/*============================================================================ + * IC resources marco + ===========================================================================*/ + /* VC921x IC type ID */ +#define VC921x_DEV_REV_ID 0x24 +#define VC921x_FDY_VER_ID0 0x10 + +#define VC921x_FDY_VER_ID1 0x30 + +#define VC921x_DEFAULT_FREQ (20000) /* 20K */ +/* work event */ +#define VC921x_EVENT_BIO_FLOW (1 << 6) +#define VC921x_EVENT_INSAMPLE (1 << 5) +#define VC921x_EVENT_CONFLICT (1 << 4) +#define VC921x_EVENT_INTPS_RDY (1 << 3) +#define VC921x_EVENT_INTFIFO_RDY (1 << 2) +#define VC921x_EVENT_INTOVLOAD (1 << 1) +#define VC921x_EVENT_INTPPG_STA (1 << 0) +/* work status */ +#define VC921x_STATUS_CH2_OVERLOAD (1 << 2) +#define VC921x_STATUS_CH1_OVERLOAD (1 << 1) +#define VC921x_STATUS_CH0_OVERLOAD (1 << 0) +/* enable event */ +#define VC921x_EN_EVT_FREQ2 (1 << 3) +#define VC921x_EN_EVT_FIFO (1 << 2) +#define VC921x_EN_EVT_OVLD (1 << 1) +#define VC921x_EN_EVT_PPG (1 << 0) + +typedef enum +{ + VC921x_CFG_DISABLE = 0, + VC921x_CFG_ENABLE = 1, +} VC921x_CFG_FUNC; + +typedef enum +{ + VC921x_PPG_SLOT_0 = 0, + VC921x_PPG_SLOT_1 = 1, + VC921x_PPG_SLOT_2 = 2, /* fix=ps */ +} VC921x_PPG_SLOT_I; /* 逻辑工作通道 */ + +typedef enum +{ + VC921x_PPG_TXCH_0 = 0, /* default:green */ + VC921x_PPG_TXCH_1 = 1, /* default: red */ + VC921x_PPG_TXCH_2 = 2, /* fix=ps,or irled */ +} VC921x_PPG_TXCH_I; /* 物理发光通道 */ + +typedef enum +{ + VC921x_FREQ_PPG = 0, /* high */ + VC921x_FREQ_PS = 1, /* low */ +} VC921x_FREQ_SEL; + +typedef enum +{ + VC921x_NO_STORE = 0, + VC921x_IN_STORE = 1, +} VC921x_STORE_FIFO; + +typedef enum +{ + VC921x_IO_PUSH_PULL = 0, + VC921x_IO_OPEN_DRAIN = 1, +} VC921x_IO_MODE; +typedef enum +{ + VC921x_IO_DIR_UP_EDGE = 0, + VC921x_IO_DIR_DOWN_EDGE = 1, +} VC921x_IO_DIR; + +typedef enum +{ + VC921x_IO_PULSE_TIM_100US = 0, + VC921x_IO_PULSE_TIM_200US = 1, + VC921x_IO_PULSE_TIM_400US = 2, + VC921x_IO_PULSE_TIM_800US = 3, +} VC921x_IO_PULSE_TIM; + +typedef enum +{ + VC921x_SAMPLE_TIM_50US = 0, + VC921x_SAMPLE_TIM_100US = 1, + VC921x_SAMPLE_TIM_150US = 2, + VC921x_SAMPLE_TIM_200US = 3, +} VC921x_SAMPLE_TIM; + +typedef enum +{ + VC921x_SLOT_CHSEL_NONE= 0, + VC921x_SLOT_CHSEL_PD01= 1, + VC921x_SLOT_CHSEL_PD10= 2, + VC921x_SLOT_CHSEL_PD11= 3, +}VC921x_SLOT_CHSEL; + +typedef enum +{ + VC921x_BIO_NOFIFO_PS_SGL= 0, + VC921x_BIO_INFIFO_PS_SGL= 1, + VC921x_BIO_INFIFO_PPG_SGL= 2, + VC921x_BIO_INFIFO_PPG_SUM= 3, +}VC921x_BIO_MODE; + +typedef enum +{ + VC921x_BIO_CHSEL_NONE= 0, + VC921x_BIO_CHSEL_ETP0= 1, + VC921x_BIO_CHSEL_ETP1= 2, + VC921x_BIO_CHSEL_PD11= 3, +}VC921x_BIO_CHSEL; + +typedef enum +{ + VC921x_TEMP_SLOT_INN = 0, + VC921x_TEMP_SLOT_EXT0 = 1, + VC921x_TEMP_SLOT_EXT1 = 2, +}VC921x_TEMP_SLOT; + +typedef enum +{ + VC921x_ADC_RESOLUTION_16BIT = 0, + VC921x_ADC_RESOLUTION_18BIT = 1, + VC921x_ADC_RESOLUTION_20BIT = 2, + VC921x_ADC_RESOLUTION_22BIT = 3, +}VC921x_ADC_RESOLUTION; + +typedef enum +{ + VC921x_STOP_WORK_CMD = 0, + VC921x_START_WORK_CMD, + VC921x_RESET_SOFT_CMD, + VC921x_WORK_CMD_NUM, +} VC921x_WORK_CMD; +/*============================================================================ + * VC921x_CORE_API + ===========================================================================*/ +/**************************************************************************** + * @description: 获取内核驱动版本信息 + * @param {char} **ppver + * @return {int} 0(success),-1(read/write/ticks func_pointer error) + ****************************************************************************/ +static inline int core921x_read_software_version(char **ppver) +{ + extern int __core921x_read_software_version(char **ppver); + return __core921x_read_software_version(ppver); +} + +/**************************************************************************** + * @description: 读取ic的ID参数 + * @param {unsigned char} *pid1 版本ID + * @param {unsigned char} *pid2 扩展ID + * @return {int} 0(success),-1(read/write error),1(parameter error),2(ic_id error) + ****************************************************************************/ +static inline int core921x_read_id(unsigned char *pid1, unsigned char *pid2) +{ + extern int __core921x_read_id(unsigned char *pid1, unsigned char *pid2); + return __core921x_read_id(pid1, pid2); +} + +/**************************************************** + * @description: 读取中断事件和过载状态 + * @param {int} *pevt 事件状态 + * @param {unsigned char} *povsta 过载状态 + * @return {int} 0(success),-1(read/write error) + ****************************************************/ +static inline int core921x_read_event_status(unsigned char *pevt, unsigned char *povsta, unsigned char *pwindex_bak, unsigned char *pwindex_rel ) +{ + extern int __core921x_read_event_status(unsigned char *pevt, unsigned char *povsta, unsigned char *pwindex_bak, unsigned char *pwindex_rel); + return __core921x_read_event_status(pevt, povsta, pwindex_bak, pwindex_rel ); +} + +/*************************************************************************** + * @description: 读取时钟tick + * @param {unsigned int} *ptick0 + * @param {unsigned int} *ptick1 + * @return {int} 0(success),-1(read/write error) + ****************************************************************************/ +static inline int core921x_read_rcosc_tick(unsigned short int *ptick0, unsigned short int *ptick1) +{ + extern int __core921x_read_rcosc_tick(unsigned short int *ptick0, unsigned short int *ptick1); + return __core921x_read_rcosc_tick(ptick0, ptick1); +} + +/**************************************************************************** + * @description: 读取基础采样数据 + * @param {result_t} *psample_ret + * @return {int} 0(success),-1(read/write error) + ****************************************************************************/ +static inline int core921x_read_output(vc921x_sample_info_t *psample_ret) +{ + extern int __core921x_read_output(vc921x_sample_info_t * psample_ret); + return __core921x_read_output(psample_ret); +} + +/**************************************************************************** + * @description: 读取fifo_PPG数据 + * @param {result_t} *psample_ret + * @param {unsigned char} *pread_fifoaddr + * @return {int} 0(success),-1(read/write error),1(analysis err) + ****************************************************************************/ +static inline int core921x_read_fifo_data(vc921x_sample_info_t *psample_ret, unsigned char *pread_fifoaddr, unsigned char read_windex) +{ + extern int __core921x_read_fifo_data(vc921x_sample_info_t * psample_ret, unsigned char *pread_fifoaddr, unsigned char read_windex); + return __core921x_read_fifo_data(psample_ret, pread_fifoaddr, read_windex); +} + +/* 通道相关配置 */ +static inline int core921x_set_enable_config(VC921x_PPG_SLOT_I slot_index, VC921x_CFG_FUNC config_enable) +{ + extern int __core921x_set_slot_enable_config(VC921x_PPG_SLOT_I slot_index, VC921x_CFG_FUNC config_enable); + return __core921x_set_slot_enable_config(slot_index, config_enable); +} + +static inline int core921x_set_slot_fix_param_config(VC921x_PPG_SLOT_I slot_index, unsigned char rxgain, VC921x_CFG_FUNC freq_isps, unsigned char oversample, VC921x_PPG_TXCH_I txslot, VC921x_SAMPLE_TIM sample_tim, VC921x_CFG_FUNC slot_infifo ) +{ + extern int __core921x_set_slot_fix_param_config(VC921x_PPG_SLOT_I slot_index, unsigned char rxgain, VC921x_CFG_FUNC freq_isps, unsigned char oversample, VC921x_PPG_TXCH_I txchsel, VC921x_SAMPLE_TIM sample_tim, VC921x_CFG_FUNC slot_infifo ); + return __core921x_set_slot_fix_param_config(slot_index, rxgain, freq_isps, oversample, txslot, sample_tim, slot_infifo ); +} + +static inline int core921x_read_slot_adjust_param(unsigned char *pmax_res, unsigned char *pmin_res, unsigned char *pmax_curr, unsigned char *pmin_curr) +{ + extern int __core921x_read_slot_adjust_param(unsigned char *pmax_res, unsigned char *pmin_res, unsigned char *pmax_curr, unsigned char *pmin_curr); + return __core921x_read_slot_adjust_param(pmax_res, pmin_res, pmax_curr, pmin_curr); +} +static inline int core921x_read_slot_current_res(VC921x_PPG_SLOT_I slot_index, unsigned char *pslot_curr, unsigned char *pslot_reslvl) +{ + extern int __core921x_read_slot_current_res(VC921x_PPG_SLOT_I slot_index, unsigned char *pslot_curr, unsigned char *pslot_reslvl); + return __core921x_read_slot_current_res(slot_index, pslot_curr, pslot_reslvl); +} + +static inline int core921x_set_ppgslot_current(VC921x_PPG_SLOT_I slot_index, unsigned char current) +{ + extern int __core921x_set_ppgslot_current(VC921x_PPG_SLOT_I slot_index, unsigned char current); + return __core921x_set_ppgslot_current(slot_index, current); +} + +static inline int core921x_set_ppgslot_resistance(VC921x_PPG_SLOT_I slot_index, unsigned char res_level, unsigned char allow_oversample ) +{ + extern int __core921x_set_ppgslot_resistance(VC921x_PPG_SLOT_I slot_index, unsigned char res_level, unsigned char allow_oversample ); + return __core921x_set_ppgslot_resistance(slot_index, res_level, allow_oversample ); +} +/*--------------------------------------------------------------*/ +extern int __core921x_set_slotx_pd_config( VC921x_SLOT_CHSEL slot0_pd, VC921x_SLOT_CHSEL slot1_pd, VC921x_SLOT_CHSEL slot2_pd ); +extern int __core921x_set_anti_slot_config( unsigned char anti_current, VC921x_CFG_FUNC deslot0, VC921x_CFG_FUNC deslot1, VC921x_CFG_FUNC deslot2, VC921x_PPG_TXCH_I antich ); +extern int __core921x_set_adc_config( VC921x_ADC_RESOLUTION adc_resulution, VC921x_CFG_FUNC ovdata_sum_enable ); +extern int __core921x_set_common_config(VC921x_CFG_FUNC deenv_en, VC921x_CFG_FUNC i2crelease_en, VC921x_CFG_FUNC bio_disfreqHop, VC921x_CFG_FUNC fifo_format, VC921x_CFG_FUNC fifo_en, unsigned char cpd); +static inline int core921x_set_common_config(unsigned char fifo_enable, unsigned fifo_format, unsigned char adc_resolution, VC921x_SLOT_CHSEL slot0_pd, VC921x_SLOT_CHSEL slot1_pd, VC921x_SLOT_CHSEL slot2_pd ) +{ + VC921x_CFG_FUNC fifo_enable_cfg = (fifo_enable) ? VC921x_CFG_ENABLE : VC921x_CFG_DISABLE; + VC921x_CFG_FUNC fifo_format_cfg = (fifo_format) ? VC921x_CFG_ENABLE : VC921x_CFG_DISABLE; + VC921x_ADC_RESOLUTION adc_resolution_cfg = VC921x_ADC_RESOLUTION_16BIT; + switch ( adc_resolution ) + { + case 16:adc_resolution_cfg=VC921x_ADC_RESOLUTION_16BIT; break; + case 18:adc_resolution_cfg=VC921x_ADC_RESOLUTION_18BIT; break; + case 20:adc_resolution_cfg=VC921x_ADC_RESOLUTION_20BIT; break; + case 22:adc_resolution_cfg=VC921x_ADC_RESOLUTION_22BIT; break; + default:adc_resolution_cfg=VC921x_ADC_RESOLUTION_16BIT; break; + } + __core921x_set_slotx_pd_config( slot0_pd, slot1_pd, slot2_pd ); + __core921x_set_anti_slot_config( 0/*0-7*/, VC921x_CFG_DISABLE,VC921x_CFG_DISABLE,VC921x_CFG_DISABLE, VC921x_PPG_TXCH_2 ); + __core921x_set_adc_config( adc_resolution_cfg, VC921x_CFG_DISABLE /* ov_type */ ); + return __core921x_set_common_config(VC921x_CFG_ENABLE, VC921x_CFG_ENABLE, VC921x_CFG_ENABLE/*dis_biofreq_hop*/, fifo_format_cfg, fifo_enable_cfg, 0); +} +/* 频率设置相关API */ +/**************************************************************************** + * @description: (ppg=20K/div1,ps=ppg/div2,fifo=ps/div3) + * @param {unsigned short int} div1 + * @param {unsigned char} div2 + * @param {unsigned char} div3 + * @return {int} 0(success),-1(read/write error) + ****************************************************************************/ +static inline int core921x_set_sample_frequency(unsigned short int div1, unsigned char div2, unsigned char div3) +{ + extern int __core921x_set_sample_frequency(unsigned short int div1, unsigned char div2, unsigned char div3); + return __core921x_set_sample_frequency(div1 - 1, div2 - 1, div3 - 1); +} + +static inline int core921x_set_threshold_config(unsigned char ppgth_h, unsigned char ppgth_l) +{ + extern int __core921x_set_threshold_config(unsigned char ppgth_h, unsigned char ppgth_l); + return __core921x_set_threshold_config(ppgth_h, ppgth_l); +} +static inline int core921x_set_interrupt_config(unsigned char evt_en) +{ + extern int __core921x_set_interrupt_config(VC921x_IO_MODE int_mode, VC921x_IO_DIR int_dir, VC921x_IO_PULSE_TIM int_len, unsigned char evt_en); + return __core921x_set_interrupt_config(VC921x_IO_PUSH_PULL, VC921x_IO_DIR_UP_EDGE, VC921x_IO_PULSE_TIM_100US, evt_en); +} + +static inline int core921x_set_bioslot_config(vc921x_sample_info_t *psample_info, unsigned char addcap) +{ + unsigned char bio_time = 6; + if( VC921x_BIO_INFIFO_PPG_SGL==psample_info->extra_result.biofifo_mode || VC921x_BIO_INFIFO_PPG_SUM==psample_info->extra_result.biofifo_mode ) + { +#if (VC921x_PS_WORK_FREQUENCY_CONFIG==1) + bio_time = 3; /* (ps=(25/13)hz~520ms,ps_div=13,fifo_div=1) 5200us */ +#elif (VC921x_PS_WORK_FREQUENCY_CONFIG==0) + switch (psample_info->ps_div) /* time = 2^n * 50 * ps_div */ + { + case 5: /* ppg=25,ps=5 */ + bio_time = 5; /* default 8ms:5-4-3-2-1 , 4ms:4-3-2-1-0 */ + break; + case 10: + bio_time = 4; + break; + case 20: + bio_time = 3; + break; + case 40: + bio_time = 2; + break; + case 80: + bio_time = 1; + break; + default: + CORE921x_DEBUG_ERR("fifo time error when underclocking"); + break; + } +#endif + } + extern int __core921x_set_bioslot_config(VC921x_CFG_FUNC bioinn_en, VC921x_CFG_FUNC bioext_en, VC921x_BIO_MODE biofifo_mode, VC921x_BIO_CHSEL bio_chsel, unsigned char biofreq, unsigned char biocap, unsigned char biotime) ; + return __core921x_set_bioslot_config((VC921x_CFG_FUNC)psample_info->extra_result.bioinn_enwork, + (VC921x_CFG_FUNC)psample_info->extra_result.bioext_enwork, + (VC921x_BIO_MODE)psample_info->extra_result.biofifo_mode, + VC921x_CFG_BIO_CHANNL_SELECT, + 2, addcap, bio_time); +} +static inline int core921x_set_bioinn_addcap(vc921x_sample_info_t *psample_info,unsigned char biocap) +{ + extern int __core921x_set_bioinn_addcap(unsigned char biocap); + return __core921x_set_bioinn_addcap(biocap); +} + +static inline int core921x_set_temperature_config(vc921x_sample_info_t *psample_info) +{ + extern int __core921x_set_temperature_config(VC921x_CFG_FUNC tempslot0_en, VC921x_TEMP_SLOT tempslot0_chsel, unsigned char tempslot0_current, + VC921x_CFG_FUNC tempslot1_en, VC921x_TEMP_SLOT tempslot1_chsel, unsigned char tempslot1_current ); + return __core921x_set_temperature_config((VC921x_CFG_FUNC)psample_info->extra_result.tempslot0_enwork, VC921x_TEMP_SLOT_INN, psample_info->extra_result.tempslot0_setcur, + (VC921x_CFG_FUNC)psample_info->extra_result.tempslot1_enwork, VC921x_TEMP_SLOT_EXT1, psample_info->extra_result.tempslot1_setcur ); +} + +/**************************************************************************** + * @description: 设置RCOSC校准值 + * @param {unsigned char} oscfreq + * @return {int} 0(success),-1(read/write error) + ****************************************************************************/ +static inline int core921x_set_rcosc_frequency(unsigned char oscfreq) +{ + extern int __core921x_set_rcosc_frequency(unsigned char oscset); + return __core921x_set_rcosc_frequency(oscfreq); +} + +/**************************************************************************** + * @description: + * @param {VC921x_WORK_CMD} cmd + * @return {int} 0(success),-1(read/write error),1(parameter error) + ****************************************************************************/ +static inline int core921x_write_work_cmd(VC921x_WORK_CMD cmd) +{ + extern int __core921x_write_work_cmd(VC921x_WORK_CMD cmd); + return __core921x_write_work_cmd(cmd); +} + +/*============================================================================ + * IC config ppg/ps slot function + ===========================================================================*/ +static inline int core921x_sample_slotx_config(VC921x_PPG_SLOT_I slot_index ,vc921x_sample_info_t *psample_info, VC921x_PPG_TXCH_I ppg_tx, unsigned char rxgain, unsigned char oversample, VC921x_SAMPLE_TIM sampletime) +{ + /* ppg slot0 config */ + VC921x_CFG_FUNC freq_isps = (1==psample_info->slot_result[slot_index].slot_freq_sel)?VC921x_CFG_ENABLE:VC921x_CFG_DISABLE; + if (psample_info->slot_result[slot_index].slot_enwork) + { + core921x_set_ppgslot_current(slot_index, psample_info->slot_result[slot_index].slot_current); + core921x_set_ppgslot_resistance(slot_index, psample_info->slot_result[slot_index].slot_resistance, 0 ); + core921x_set_slot_fix_param_config(slot_index, rxgain, freq_isps, oversample, ppg_tx, sampletime, psample_info->slot_result[slot_index].slot_storefifo ); + core921x_set_enable_config(slot_index, VC921x_CFG_ENABLE); + } + return 0; +} +/*=====================================================================================================================================*/ +extern int core921x_check_nto_version_init(void); +/**************************************************************** + * CORE_API.write()/set() + * common interface to write/read configuration + * write or set config registers, debug or other function. + ***************************************************************/ + +extern int vc921x_param_write_flash(unsigned char *pdata, unsigned int size); +extern int vc921x_param_read_flash(unsigned char *pdata, unsigned int size); + +extern void *vc921x_malloc(unsigned int size); +extern void vc921x_free(void *ptr); + +extern int vc921x_read_register(unsigned char regaddr, unsigned char *pbuf, unsigned short int size); +extern int vc921x_write_register(unsigned char regaddr, unsigned char *pbuf, unsigned short int size); +extern unsigned int vc921x_get_cputimer_tick(void); + +#endif /* __VC921x_CORE_COM_H__ */ diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver.h new file mode 100644 index 000000000..36f5bc36e --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver.h @@ -0,0 +1,127 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-09-22 17:39:10 + * @LastEditTime: 2023-12-21 15:37:20 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ + +#ifndef __VC921x_DRIVER_H__ +#define __VC921x_DRIVER_H__ + +#include "vcare_device_com.h" +/**************************************************************************** + * WearStatus_t + ***************************************************************************/ +#define VC921x_BIO_STA_NONE 0 +#define VC921x_BIO_STA_DROP 1 +#define VC921x_BIO_STA_HOLD 2 + +typedef enum +{ + VC921x_WEAR_NO_CHANGE = 0, + VC921x_HOLD_TO_DROP, + VC921x_DROP_TO_HOLD, +} vc921x_wear_change; + +typedef struct +{ + unsigned char hold_cnt; + unsigned char drop_cnt; + wear_status wear_status; + wear_status last_status; + wear_status algo_wear_status; + wear_status acce_wear_status; + int bio_wear_result; + unsigned short int last_ps_data[2]; + +} vc921x_wear_info; + +/**************************************************************************** + * oscclk_t + ***************************************************************************/ +typedef struct +{ + /* calc real frequency */ + unsigned int osc_count[6]; + unsigned int clk_count[6]; + unsigned char count_index; + unsigned char osc_gap_cnt; + unsigned short int rcosc_frequency; + /* check interrupt gaptime */ + unsigned short int last_check_ticks; + /* startup need get calc value */ + unsigned char rcosc_calibration; + unsigned char clk_calc_freq_status; /* calculate frequency finished */ + unsigned char clk_calc_status; /* calculate rcsoc status finished */ + +} vc921x_clk_info; + +/**************************************************************************** + * adjust_t + ***************************************************************************/ +#define VC921x_ADJUST_DIR_NONE 0 +#define VC921x_ADJUST_DIR_UP 1 +#define VC921x_ADJUST_DIR_DOWN 2 + +typedef struct +{ + unsigned char direction; + unsigned char direction_last; + unsigned short int current_step; +} vc921x_adjust_param; + +typedef struct +{ + unsigned char const_max_current; /* ic参数确定的最大电流电阻,固定 */ + unsigned char const_min_current; + unsigned char const_max_res_level; + unsigned char const_min_res_level; + unsigned char max_current; /* ic工作环境的最大电流电阻,可变 */ + unsigned char max_res_level; + unsigned char current; /* ic当前工作的最大电流电阻,可变 */ + unsigned char res_level; + + vc921x_adjust_param adj_param; + +} vc921x_adjust_info; +typedef struct +{ + int work_mode; + unsigned short ppg_div; + unsigned char ps_div; + unsigned char fifo_div; + unsigned char drop_div; + unsigned char hr_algo_mode; + + unsigned char int_event; + unsigned char ov_status; + unsigned char read_fifo_address; + unsigned char write_fifo_address_bak; /* write_addr when fifo event */ + unsigned char write_fifo_address_rel; /* write_addr at present */ + + int optimal_bio_gap; + int optimal_bio_temp; + unsigned char optimal_cap; + unsigned char optimal_bio_valid; + unsigned char ppg_max_thval; + unsigned char ppg_min_thval; + + unsigned char ppg_convert_ps; /* 如果不使用PS频率采集PS,使用PPGir来对佩戴检测进行识别,需要设置为1 */ + + unsigned char param_read_finished_sync; + unsigned char fifo_read_finished_sync; + unsigned char fifo_read_data_valid; /* 0-havn't fifo data, 1-normal fifo data, 2-read fifo data when fifo_windex offset */ + unsigned char event_read_finished_sync; + +} vc921x_drv_info; + +typedef struct +{ + work_mode mode; + int (*mode_init)(void); + int (*mode_handler)(const void *ops_self, vcare_ppg_device_t *pdev); +} vc921x_mode_ops; + +#endif /* __VC9211x_DRIVER_H__ */ diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver_com.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver_com.h new file mode 100644 index 000000000..55b1142a5 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver_com.h @@ -0,0 +1,148 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-09-22 17:39:10 + * @LastEditTime: 2023-09-26 15:22:19 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ + +#ifndef __VC921x_DRIVER_COM_H__ +#define __VC921x_DRIVER_COM_H__ + +#include "vcare_device_com.h" +/********************************************************************************************************* + * DRV_API + ********************************************************************************************************/ +/* 注意: +VC921x - fifo长度最长只有192字节,在多通道时,一次fifo的缓存不能超过192字节 + +typical: +ppg_div=800, ps_div=5, fifo_div=5 (ppg-25hz, psbio-5hz, fifo-1hz) 1000ms (921x_max=20*4+4*6=104) +ppg_div=800, ps_div=5, fifo_div=4 (ppg-25hz, psbio-5hz, fifo-1.25hz) 800ms (921x_max=20*4+4*6=104) +ppg_div=400, ps_div=10, fifo_div=2 (ppg-50hz, psbio-5hz, fifo-2.5hz) 400ms (921x_max=20*4+2*6=92) +ppg_div=200, ps_div=20, fifo_div=1 (ppg-100hz,psbio-5hz, fifo-5hz) 200ms (921x_max=20*4+1*6=88) +*/ +/**************************************************************************** + * @description: 初始化并且开始进行采样 + * @param {void} *pdevice + * @param {int} workmode + * @param {unsigned short int} ppgdiv + * @param {unsigned char} psdiv + * @param {unsigned char} fifodiv + * @return {*} + ****************************************************************************/ +int vc921x_drv_init_start_work(void *pdevice, int workmode, unsigned short int ppgdiv, unsigned char psdiv, unsigned char fifodiv, unsigned char dropdiv); +/**************************************************************************** + * @description: 获取驱动版本,IC的主ID,次ID + * @param {char} * + * @param {unsigned char} *pid1 + * @param {unsigned char} *pid2 + * @return {*} + ****************************************************************************/ +int vc921x_drv_get_hwsw_infomation(char **str_drv_ver, unsigned char *pid1, unsigned char *pid2); +/**************************************************************************** + * @description: 软复位IC并且退出工作(如需重新工作需要重新调用初始化) + * @param {void} *device + * @return {*} + ****************************************************************************/ +int vc921x_drv_exit_stop_work(void *device); +/**************************************************************************** + * @description: 软复位IC(如需重新工作需要重新调用初始化) + * @param {void} *device + * @return {*} + ****************************************************************************/ +int vc921x_drv_software_reset(void *device); +/**************************************************************************** + * @description: 唤醒工作(可以唤醒调用sleep停止工作) + * @param {void} *device + * @return {*} + ****************************************************************************/ +int vc921x_drv_wakeup_work(void *device); +/**************************************************************************** + * @description: 休眠工作(休眠后可以用wakeup唤醒工作) + * @param {void} *device + * @return {*} + ****************************************************************************/ +int vc921x_drv_sleep_work(void *device); +/**************************************************************************** + * @description: 心率算法扩展的摘下状态设置 + * @param {void} *device + * @param {int} status + * @return {*} + ****************************************************************************/ +int vc921x_drv_set_algo_wear_status(void *device, int status); +/**************************************************************************** + * @description: 运动量判断佩戴的状态设置 + * @param {void} *device + * @param {int} status + * @return {*} + ****************************************************************************/ +int vc921x_drv_set_acce_wear_status(void *device, int status); +/**************************************************************************** + * @description: 获取参数并且进行佩戴状态判断 + * @param {void} *device + * @return {*} + ****************************************************************************/ +wear_status vc921x_drv_get_wear_status(void *device); +/**************************************************************************** + * @description: 获取具体的佩戴状态各子状态 + * @param {unsigned char} *bio_ret + * @param {unsigned char} *algo_ret + * @param {unsigned char} *acce_ret + * @return {*} + ****************************************************************************/ +void vc921x_drv_get_wear_detail_result( unsigned char *bio_ret, unsigned char *algo_ret, unsigned char *acce_ret ); +/**************************************************************************** + * @description: 获取采样数据,包含PPG,PS,BIO,TMEP + * @param {void} *device + * @return {*} + ****************************************************************************/ +int vc921x_drv_get_result_handler(void *device); +/**************************************************************************** + * @description: 是否为fifo中断事件 + * @param {void} *pdevice + * @return {*} + ****************************************************************************/ +int vc921x_drv_is_fifo_event(void *pdevice); +/**************************************************************************** + * @description: 是否为PS中断事件 + * @param {void} *pdevice + * @return {*} + ****************************************************************************/ +int vc921x_drv_is_ps_event(void *pdevice); +/**************************************************************************** + * @description: 是否为PPG中断事件 + * @param {void} *pdevice + * @return {*} + ****************************************************************************/ +int vc921x_drv_is_ppg_event(void *pdevice); +/**************************************************************************** + * @description: 配平校准活体 + * @param {void} *pdevice + * @return {*} + ****************************************************************************/ +int vc921x_drv_clear_reset_bio_param(void *pdevice); +/**************************************************************************** + * @description: 同步驱动状态,清理状态数据 + * @param {void} *pdevice + * @return {*} + ****************************************************************************/ +int vc921x_drv_finished_sync(void *pdevice); +/**************************************************************************** + * @description: 提前读取中断信息,避免任务延时响应导致信息被刷掉 + * @param {*} + * @return {*} + ****************************************************************************/ +void vc921x_drv_read_event_infomation_byIRQ(void); +/**************************************************************************** + * @description: 软件定时器读取RO信息,用于定时温度检测 + * @param {*} + * @return {*} + ****************************************************************************/ +int vc921x_drv_read_check_temperature_abnormal( void *pdevice, unsigned char *p_freq_ret, unsigned char *p_temp_ret, unsigned short *pdev_cnt1, unsigned short *pdev_cnt2 ); + +int vc921x_drv_memroy_alloc(void); +int vc921x_drv_memroy_free(void); + +#endif /* __DRV_DEV_MANAGER_H__ */ diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver_config.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver_config.h new file mode 100644 index 000000000..15acae30e --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_driver_config.h @@ -0,0 +1,115 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-09-22 17:39:10 + * @LastEditTime: 2024-03-21 11:16:25 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ +#ifndef __VC921x_DRIVER_CONFIG_H__ +#define __VC921x_DRIVER_CONFIG_H__ + +/* wear default status select */ +#define VC921x_WEAR_DFT_NONE (0) +#define VC921x_WEAR_DFT_DROP (1) +#define VC921x_WEAR_DFT_HOLD (2) +/* function enable select */ +#define VC921x_FUNC_DISABLE (0) +#define VC921x_FUNC_ENABLE (1) + +#define VC921x_FREQUENCY_MAX 20000 +#define VC921x_FREQUENCY_MIN 19900 +//#define VC921x_CFG_MCU_TICK_FREQUENCY 32000 // counter 32K +extern unsigned short int VC921x_CFG_MCU_TICK_FREQUENCY; /* extern unsigned short int VC921x_CFG_MCU_TICK_FREQUENCY; */ +#define VC921x_CFG_MCU_TICK_MAX 0xffffff // 24-bit counters +#define VC921x_CFG_CLK_CALC_DEVIATION 3 +#define VC921x_CFG_CLK_CALC_GAPTIME 10 // interrupt gap times( 25hz_fifo_irq:20*40~0.4sec; 5hz_fifo_irq: 20*200 ~ 2sec; 1.25hz_fifo_irq: 10*800 ~ 8sec ) + +#define VC921x_CFG_CLK_CLAC_IRQGAP_EN (VC921x_FUNC_DISABLE) +/* interrupt run check timegap,dont write "(3/4)" !!! */ +/* 25hz(40ms~30ms), 50hz(20ms~15ms), 100hz(10ms~7ms) */ +#define VC921x_IRQ_GAP_RATIO 3/4 + +#define VC921x_CFG_IRQ_BY_TIMER_EN (VC921x_FUNC_DISABLE) +/* notice:ps_freq = ppg_freq/drop_div, and VC921x_PS_WORK_FREQUENCY_CONFIG(0). */ +#define VC921x_CFG_DROP_REDUCE_FREQ (VC921x_FUNC_ENABLE) + +#define VC921x_CFG_DEFAULT_PPGGAIN_EN (1) /* 1-最小使用2倍增益, 0-最小使用单倍增益 */ + +/**************************************************************************** + * Configuration hardware:PD/BIO channl; sport heartrate param; + ***************************************************************************/ +/* (VC921x_SLOT_CHSEL_PD11):setting ppgchx pd0+pd1, (VC921x_SLOT_CHSEL_PD01),setting pd0, (VC921x_SLOT_CHSEL_PD10),setting pd1 vc921x_core_com.h@VC921x_SLOT_CHSEL*/ +#define VC921x_CFG_PPG_CHANNL_SELECT (VC921x_SLOT_CHSEL_PD11) +/* (VC921x_BIO_CHSEL_PD11),setting biochx pd0+pd1, (VC921x_BIO_CHSEL_ETP1),setting extern touch vc921x_core_com.h@VC921x_BIO_CHSEL*/ +#define VC921x_CFG_BIO_CHANNL_SELECT (VC921x_BIO_CHSEL_PD11) +#define VC921x_CFG_SPORT_HR_OVERSAMPLE (3) /* oversample = 2^N */ +#define VC921x_CFG_SPORT_HR_SAMPLETIME (VC921x_SAMPLE_TIM_200US) + +/**************************************************************************** + * Configuration wear parameters + ***************************************************************************/ +#define VC921x_CFG_WEAR_DETECTION (VC921x_FUNC_ENABLE) +#define VC921x_CFG_WEAR_DEFAULT_STA (VC921x_WEAR_DFT_HOLD) +#define VC921x_CFG_WEAR_BIO_EN (VC921x_FUNC_ENABLE) +#define VC921x_CFG_WEAR_ALG_EN (VC921x_FUNC_DISABLE) +#define VC921x_CFG_WEAR_ACC_EN (VC921x_FUNC_DISABLE) +#define VC921x_CFG_WEAR_HOLD_CNT (1) +#define VC921x_CFG_WEAR_DROP_CNT (3) +/* sensor serial default wear param */ +#define VC921x_CFG_WEAR_BIO_OLD_EN (VC921x_FUNC_DISABLE) /* 使用算法相对的方式计算活体的佩戴状态 */ +#define VC921x_WEAR_PARAM_ENVMIN 100 /* 80nA/D */ +#define VC921x_WEAR_PARAM_ENVMAX 400 +#define VC921x_WEAR_PARAM_PSTH 0xA000 /* 16bit:0xA400~40960 , 8bit:0xA0~160 */ +#define VC921x_WEAR_PARAM_PS_DELTA 0x3000 /* 16bit:0x3000~12288 , 8bit:0x30~48 */ +#define VC921x_WEAR_PARAM_BIO_DELTA 100 /* bio0/1,建议取测试变化量的45%作为参数设置 */ + +//#define VC921x_WEAR_PARAM_BIO_ABS_HOLD 200 /* 80% */ +//#define VC921x_WEAR_PARAM_BIO_ABS_DROP 160 /* 50% */ +extern unsigned short int VC921x_WEAR_PARAM_BIO_ABS_HOLD; /* extern unsigned short int VC921x_WEAR_PARAM_BIO_ABS_HOLD; */ +extern unsigned short int VC921x_WEAR_PARAM_BIO_ABS_DROP; /* extern unsigned short int VC921x_WEAR_PARAM_BIO_ABS_DROP; */ + +/* 约定最后的一个slot一定作为红外采样 */ +#define VC921x_WEAR_PARAM_PS_CURRENT 0x25 /*range:0-0x7F */ +#define VC921x_WEAR_PARAM_PS_RESISTANCE 6 /*range:0-13 */ +#define VC921x_WEAR_PARAM_PS_GAIN 1 /* 2^n */ + +#if VC921x_CFG_DEFAULT_PPGGAIN_EN +#define VC921x_PPG_CURT 0x40 +#define VC921x_PPG_RESISTANCE 9 /* 9-1 ~ 4.48M */ +#define VC921x_PPG_GAIN 1 +/* Set the maximum current parameter in FAT1/FAT2 work_mode */ +#define VC921x_FAT_GREEN_MAX_CURT 0x60 /* 0-0x7F */ +#define VC921x_FAT_RED_MAX_CURT 0x60 /* 0-0x7F */ +#define VC921x_FAT_IR_MAX_CURT 0x60 /* 0-0x7F */ +#define VC921x_FAT_RESISTANCE 9 /* 9~4.48M, 11~8.96M */ +#define VC921x_FAT_GAIN 1 +#else +#define VC921x_PPG_CURT 0x40 +#define VC921x_PPG_RESISTANCE 11 /* 9-1 ~ 4.48M */ +#define VC921x_PPG_GAIN 0 +/* Set the maximum current parameter in FAT1/FAT2 work_mode */ +#define VC921x_FAT_GREEN_MAX_CURT 0x60 /* 0-0x7F */ +#define VC921x_FAT_RED_MAX_CURT 0x60 /* 0-0x7F */ +#define VC921x_FAT_IR_MAX_CURT 0x60 /* 0-0x7F */ +#define VC921x_FAT_RESISTANCE 11 /* 9~4.48M, 11~8.96M */ +#define VC921x_FAT_GAIN 0 +#endif + +/**************************************************************************** + * PPG AUTO ADJUST PARAMETERS + ***************************************************************************/ +#define VC921x_PPG_MIN_DEFT_THVAL (0x6) +#define VC921x_PPG_MAX_DEFT_THVAL (0xA) /*0xA~0xB*/ +#define VC921x_PPG_SPO2_MIN_DEFT_THVAL (0x09) +#define VC921x_PPG_SPO2_MAX_DEFT_THVAL (0x0A) /* 0x0A~0x0B */ +#define VC921x_ADJ_I_INCREASE 22 // 1.4 << 4 = 22.4//1.4f +#define VC921x_ADJ_I_DECREASE 11 // 0.7 << 4 = 11.2//0.7f +#define VC921x_ADJ_I_STEP_MAX 8 +#define VC921x_ADJ_I_STEP_MIN 1 +#define VC921x_ADJ_I_STEP_DFT 4 // default step + +/* fix 16bit ppg */ +#define VC921x_PPG_CONV_BIT4(data) ((data)>>4) + +#endif /* __VC921x_DRIVER_CONFIG_H__ */ diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_hal.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_hal.h new file mode 100644 index 000000000..735e6662c --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_hal.h @@ -0,0 +1,53 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-06-07 11:18:26 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-12 18:03:51 + * @FilePath: /FD230/code/sdk/drivers/peripheral/ppg/hx3695/hx3695_hal.h + * @Description: + * + * Copyright (c) 2024 by ${git_name_email}, All Rights Reserved. + */ +#ifndef VC_921X_H__ +#define VC_921X_H__ +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#include "stdint.h" + +#ifdef PPG_HX3695 + +#include "tyhx_hrs_custom.h" + + +typedef uint32_t PPG_CMD_TYPE_T ; + +typedef enum{ + HX3695_MSG_TYPE_HRS_UPDATE, + HX3695_MSG_TYPE_HRV_UPDATE, + HX3695_MSG_TYPE_SPO2_UPDATE, + HX3695_MSG_TYPE_LEAKLIGHT_UPDATE, + HX3695_MSG_TYPE_GRAYCARD_UPDATE, + HX3695_MSG_TYPE_WEARSTAT_UPDATE, + HX3695_MSG_TYPE_STATIC_HRS_UPDATE, +} hx3695_msg_type_t; + + +typedef void (*hx3695_msg_callback_t)(uint8_t msg_type, uint8_t *msg_data, int32_t msg_datalen); +uint32_t hx3695_register_msg_callback(hx3695_msg_callback_t func); +hx3695_msg_callback_t hx3695_get_send_msg_callback_func(void); + +#endif + +void ppg_send_cmd_to_vc921x_driver(char* mode, uint8_t enable); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_sample_result.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_sample_result.h new file mode 100644 index 000000000..9b164f4dc --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_sample_result.h @@ -0,0 +1,158 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-09-22 17:39:10 + * @LastEditTime: 2024-03-21 10:49:02 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ +#ifndef __VC921x_SAMPLE_RESULT_H__ +#define __VC921x_SAMPLE_RESULT_H__ + +/**************************************************************************** +drviver vc921x RAM: +core+driver: 400bytes + fifo_data_memory <= 1kb +fifo_data_memory static: 128*2 + 64*(2*2+2*1) = 448 bytes +fifo_data_memory dynamic: 128*x + 64*(2*y+2*z) = 0~448 bytes +fifo_data_memory fixdync: +*/ +#define VC921x_USE_STATIC_MEMORY (0) +#define VC921x_USE_FIXDYNC_MEMORY (1) /* memory is allocated when the device is registered and freed when the device is deregistered */ +#define VC921x_USE_MEMORY_SELECT (VC921x_USE_STATIC_MEMORY) +#define VC921x_PPGSLOT_BUFFER_NUM (64) /* memory_buffer(40~64) = enable_ppgslot_num * ( VC921x_PPGSLOT_BUFFER_NUM * sizeof(unsigned short int) ) */ +#define VC921x_PS2SLOT_BUFFER_NUM (32) /* memory_buffer(20~32) = enable_bioslot_num * ( VC921x_PS2SLOT_BUFFER_NUM * sizeof(unsigned short int) ) + 1*( VC921x_PS2SLOT_BUFFER_NUM * sizeof(unsigned short int) ) */ + +/* ps sample frequency config, if work_fast,the frequency=12.5hz, otherwise is 5hz +[0] ps= 5hz~200ms, fifo_time=1000ms, 800ms, 600ms, 400ms, 200ms +[1] ps=(1000/520)hz~520ms, fifo_time=520ms(25hz), 260ms(50hz), 130ms(100hz), user defined time, ps-fifo working on the same frequency (~9201,VC30F). + !!!notice: need config bio_param_time @ vc30fx_core_com.h @ core30fx_set_bioslot_config +[2]........ +*/ +#define VC921x_PS_WORK_FREQUENCY_CONFIG (0) + +/* ppg/ps slot parameter description: +slot_config: structure handle. +enwork: slot configuration enable work, 0-disbale, 1-work_ppg, 2-work_ps. +freqsel: frequency selection, 0-ppg_frequency, 1-ps_frequency. vc921x_core_com.h:enum@VC921x_FREQ_SEL. +storefifo: fifo configuration, 0-nofifo,1-infifo, available when slot_freq_sel is ps_frequency. @VC921x_STORE_FIFO. +current: ppg_slot work current set(0x00~0x7f). +resistance: ppg_slot work resistance set(0~13). +*/ +#define VC921x_SLOT_PARAM_CONFIG(slot_config, enwork, freqsel, storefifo, current, resistance) \ + { \ + slot_config.slot_enwork = enwork; \ + slot_config.slot_freq_sel = freqsel; \ + slot_config.slot_storefifo = storefifo; \ + slot_config.slot_current = current; \ + slot_config.slot_resistance = resistance; \ + } +#define VC921x_PPG_SLOT_PARAM_CONFIG(slot_config, current, resistance) VC921x_SLOT_PARAM_CONFIG(slot_config, 1, VC921x_FREQ_PPG, VC921x_IN_STORE, current, resistance) +/*------PS slot default use slot2------*/ +#define VC921x_PS_SLOT_PARAM_CONFIG(slot_config, storefifo, current, resistance) VC921x_SLOT_PARAM_CONFIG(slot_config, 2, VC921x_FREQ_PS, storefifo, current, resistance) +/* bio slot parameter description: +bio_config: structure handle. +inn_enable: inside slot configuration enable work, @VC921x_CFG_FUNC. +ext_enable: external slot configuration enable work, @VC921x_CFG_FUNC. +fifo_mode: fifo and ppgmode configuration, @VC921x_BIO_MODE. +*/ +#define VC921x_BIO_SLOT_PARAM_CONFIG(bio_config, inn_enable, ext_enable, fifo_mode ) \ + { \ + bio_config.bioinn_enwork = inn_enable; \ + bio_config.bioext_enwork = ext_enable; \ + bio_config.biofifo_mode = fifo_mode; \ + } +/* tmp slot parameter description: +tmp_config: structure handle. +inn_enable: inside slot configuration enable work, @VC921x_CFG_FUNC. +ext_enable: external slot configuration enable work, @VC921x_CFG_FUNC. +setcur: external slot config work current(0~7). +*/ +#define VC921x_TMP_SLOT_PARAM_CONFIG(tmp_config, slot0_enable, slot1_enable, setcur) \ + { \ + tmp_config.tempslot0_enwork = slot0_enable; \ + tmp_config.tempslot1_enwork = slot1_enable; \ + tmp_config.tempslot0_setcur = setcur; \ + tmp_config.tempslot1_setcur = setcur; \ + } + +/**************************************************************** + * structures:vc921x_sample_info_t + ***************************************************************/ +typedef struct +{ + /* slot_config */ + unsigned char slot_enwork; /* slot_enwork, 0-disbale, 1-work_ppg, 2-work_ps. */ + unsigned char slot_freq_sel; /* 0-ppg_frequency, 1-ps_frequency. vc921x_core_com.h, @VC921x_FREQ_SEL. */ + unsigned char slot_storefifo; /* 0-nofifo,1-infifo, available when slot_freq_sel is ps_frequency. */ + unsigned char slot_led_sel; /* 0-green led, 1-red led, 2-infrared led */ + /* slot_variable_config */ + unsigned char slot_current; /* 0~0x7F */ + unsigned char slot_resistance; /* 0~13/0~23 */ + /* slot_result */ + unsigned short int env_data; + union + { + unsigned short int ps_data; + unsigned short int pre_data; + } u; + unsigned char ppg_num; + unsigned char ppg_buffer[VC921x_PPGSLOT_BUFFER_NUM*3]; /* 按字节存储,可以节省最大RAM */ +} vc921x_sample_slot_t; + +typedef struct +{ + /* bio_slot config */ + unsigned char bioinn_enwork; + unsigned char bioext_enwork; + unsigned char biofifo_mode; /* @VC921x_BIO_MODE */ + /* temp_slot config */ + unsigned char tempslot0_enwork; + unsigned char tempslot0_setcur; + unsigned char tempslot1_enwork; + unsigned char tempslot1_setcur; + /* extern result */ + unsigned char bio_fifo_num; + unsigned short int bioinn_data; + unsigned short int bioext_data; + unsigned short int bioabs_calc; /* abs(base-ext_now)?thh */ + unsigned short int temp0_data; + unsigned short int temp1_data; + unsigned short int tempref_data; + unsigned short int bioinn_buffer[VC921x_PS2SLOT_BUFFER_NUM]; + unsigned short int bioext_buffer[VC921x_PS2SLOT_BUFFER_NUM]; +} vc921x_sample_extra_t; + +typedef struct +{ + unsigned char ic_type_id; + unsigned char ic_ficv_id; + unsigned short int ppg_div; /* ppg_frequency = 20K / ppg_div */ + unsigned char ps_div; /* ps_frequency = ppg_frequency / ps_div */ + unsigned char fifo_div; /* fifo_frequency = ps_frequency / fifo_div */ + unsigned char hold_div; /* ps/fifo_frequency = ppg_frequency / hold_div */ + unsigned char drop_div; /* ps/fifo_frequency = ppg_frequency / drop_div */ + unsigned char fifo_enable; /* fifo_enable = 1*/ + unsigned char fifo_format; /* fifo_format,0:16bit, 1:24bit */ + unsigned char adc_resolution; /* */ + unsigned char wear_drop_flag; /* send original data flag as drop event happens */ + vc921x_sample_extra_t extra_result; + vc921x_sample_slot_t slot_result[3]; +} vc921x_sample_info_t; + +static inline unsigned int VC921x_GET_SLOT_SAMPLE_DATA( vc921x_sample_info_t* psample_info, unsigned char slot_index, unsigned char ppg_index ) +{ + if( psample_info->fifo_format == 0 ) + { + return (psample_info->slot_result[slot_index].ppg_buffer[ppg_index*2]<<8) + (psample_info->slot_result[slot_index].ppg_buffer[ppg_index*2+1] ); + } + else + { + return (psample_info->slot_result[slot_index].ppg_buffer[ppg_index*3]<<16) + (psample_info->slot_result[slot_index].ppg_buffer[ppg_index*3+1]<<8) + (psample_info->slot_result[slot_index].ppg_buffer[ppg_index*3+2] ); + } +} +static inline unsigned int VC921x_GET_SLOT_PS_DATA( vc921x_sample_info_t* psample_info, unsigned char ppg_index ) +{ + return (psample_info->slot_result[2].ppg_buffer[ppg_index*2]<<8) + (psample_info->slot_result[2].ppg_buffer[ppg_index*2+1] ); +} + +#endif /* __VC921x_SAMPLE_RESULT_H__ */ diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_user_app.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_user_app.h new file mode 100644 index 000000000..e4c85209b --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vc921x_user_app.h @@ -0,0 +1,47 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-09-22 17:39:10 + * @LastEditTime: 2023-10-17 09:50:21 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ +#ifndef __VC921x_USER_APP_H__ +#define __VC921x_USER_APP_H__ + +#include "vcare_device_com.h" + +/* vc921x设备函数接口 */ +void vc921x_usr_device_init( InitParamTypeDef *pinitconfig ); +void vc921x_usr_get_dev_info(unsigned char *pchip_id, unsigned char *pvers_id, char **drv_name, char **dev_name); +void vc921x_usr_start_work(void); +void vc921x_usr_stop_work(void); +void vc921x_usr_soft_reset(void); +void vc921x_usr_reset_bio_param(void); +int vc921x_usr_get_wear_status(void); +void vc921x_usr_get_event_status_byIRQ(void); +void vc921x_usr_device_handler( unsigned char heart_algo_mode, unsigned char spo2_algo_mode ); +void vc921x_usr_check_temperature_abnormal(void); + + +void tyhx_poweron(void); +void tyhx_poweroff(void); + + +/* 获取vc921x设备/如果动态内存则会申请驱动内存,以下为获取的设备API,设备对象中会记录一部分计算参数和结果,用户需要直接使用即可,具体参考头文件"vcare_device_com.h": +{ + .dev_init = vc921x_usr_device_init, + .get_dev_info = vc921x_usr_get_dev_info, + .start_work = vc921x_usr_start_work, + .stop_work = vc921x_usr_stop_work, + .soft_reset = vc921x_usr_soft_reset, + .get_events = NULL, + .set_slot_enable = NULL, + .reset_bio_param = vc921x_usr_reset_bio_param, + .get_wear_status = vc921x_usr_get_wear_status, + .dev_irq_handler = vc921x_usr_device_handler, +}; +*/ +vcare_ppg_device_t *get_vc921x_device(void); + +#endif // ! diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/vcSportMotionIntAlgo.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vcSportMotionIntAlgo.h new file mode 100644 index 000000000..9b4e7f307 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vcSportMotionIntAlgo.h @@ -0,0 +1,12 @@ +#ifndef VC_SPORT_MOTION_ALGO_H +#define VC_SPORT_MOTION_ALGO_H + +#include +#include +#include + + +void vcSportMotionAlgoInit(void); +int32_t vcSportMotionCalculate(int32_t x, int32_t y, int32_t z); +char *sportMotionVersion(void); +#endif diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/inc/vcare_device_com.h b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vcare_device_com.h new file mode 100644 index 000000000..a73436433 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/inc/vcare_device_com.h @@ -0,0 +1,200 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-02-23 17:47:18 + * @LastEditTime: 2023-11-14 17:38:46 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ + +#ifndef __VCARE_DEVICE_COM_H__ +#define __VCARE_DEVICE_COM_H__ + +/* parameter type sleep and retain */ +#ifndef PARAM_RETAIN +#define PARAM_RETAIN +#endif +/* define NULL 0 */ +#ifndef NULL +#define NULL ((void *)0) +#endif + +/* gsensor struct,user peripheral data */ +typedef struct +{ + short int xData[40]; + short int yData[40]; + short int zData[40]; +} gsensor_axes; + +typedef enum +{ + WEAR_STA_NONE = 0, + WEAR_STA_DROP, + WEAR_STA_HOLD, +} wear_status; + +typedef enum +{ + WORK_MODE_HR = 0, + WORK_MODE_SPO2, + WORK_MODE_HRV, + WORK_MODE_STRESS, + WORK_MODE_BLOODPRESSURE, + WORK_MODE_WEAR, + WORK_MODE_TEMPERATURE, + WORK_MODE_FACTORY0, + WORK_MODE_FACTORY1, + WORK_MODE_HRSPO2, + + WORK_MODE_UNDEFINED, + WORK_MODE_NUMBER, +} work_mode; + +typedef struct +{ + unsigned short sampling_gap_time; /* reserved. */ + unsigned char work_mode; /* work_mode,WorkModeTypeDef/unsigned char */ + unsigned char hr_algo_mode; +}InitParamTypeDef; + + +typedef void (*FUNC_DEV_INIT)( InitParamTypeDef *pInitconfig ); +typedef void (*FUNC_GET_DEV_INFO)(unsigned char *pchip_id, unsigned char *pvers_id, char **drv_name, char **dev_name); +typedef void (*FUNC_START_WORK)(void); +typedef void (*FUNC_STOP_WORK)(void); +typedef void (*FUNC_SOFT_RESET)(void); +typedef int (*FUNC_GET_EVENTS)(void); +typedef int (*FUNC_GET_WEAR_STATUS)(void); +typedef void (*FUNC_SET_SLOT_ENABLE)(unsigned char slot_index, unsigned char slot_enable); +typedef void (*FUNC_BIO_RESET_PARAM)(void); +typedef void (*FUNC_DEV_IRQ_HANDLER)(unsigned char heart_algo_mode, unsigned char spo2_algo_mode); +typedef void (*FUNC_DEV_CHK_TEMP_ERR)(void); + +typedef struct +{ + FUNC_DEV_INIT dev_init; + FUNC_GET_DEV_INFO get_dev_info; /* reserved */ + FUNC_START_WORK start_work; + FUNC_STOP_WORK stop_work; + FUNC_SOFT_RESET soft_reset; + FUNC_GET_EVENTS get_events; /* reserved */ + FUNC_GET_WEAR_STATUS get_wear_status; + FUNC_SET_SLOT_ENABLE set_slot_enable; /* reserved */ + FUNC_BIO_RESET_PARAM reset_bio_param; + FUNC_DEV_IRQ_HANDLER dev_irq_handler; + FUNC_DEV_CHK_TEMP_ERR dev_check_temp_error; +} dev_ops; + +typedef struct +{ + char *dev_name; + unsigned char id1; + unsigned char id2; + unsigned char dev_work_status; /* 0-don't work, 1-working */ + + unsigned char heart_algo_mode; + unsigned char spo2_algo_mode; + work_mode workmode; + wear_status wear; + /* raw result */ + void *result; + /* device ops */ + const dev_ops *ops; + /* calc result */ + int heart_rate; + int spo2; + int hrv; + int stress; + int ecg; + int temperature0; + int temperature1; + int blood_pressure; +} vcare_ppg_device_t; + +/**-------------------- defined functions --------------------**/ +#define ENABLE_MY_MEMFUNC 1 +#if ENABLE_MY_MEMFUNC +static inline void *vcare_memset(void *dest, int set, unsigned int n) +{ + if (dest == NULL || n <= 0) + { + return NULL; + } + char *pdest = (char *)dest; + while (n-- > 0) + { + *pdest++ = set; + } + return dest; +} + +static inline void *vcare_memcpy(void *dest, void *src, unsigned int n) +{ + if (dest == NULL || src == NULL) + { + return NULL; + } + char *pdest = (char *)dest; + char *psrc = (char *)src; + while (n--) + { + *pdest++ = *psrc++; + } + return dest; +} + +/* 2^(n/16) */ +static const float exponent_tab[]={1.000000,1.044274,1.090508,1.138789,1.189207,1.241858,1.296840,1.354256,1.414214, + 1.476826,1.542211,1.610490,1.681793,1.756252,1.834008,1.915207,2.000000}; +static inline float vcare_I_exponent_func( float base_i, unsigned char data_i ) +{ + unsigned char data_h = (data_i>>4)&0xf; + unsigned char data_l = (data_i>>0)&0xf; + return (base_i*(1<ijWmIW5`YO)~KSFJ*e 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 literal 0 HcmV?d00001 diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/lib/lib_spo2_vc30FS_float_V07_miniSDK_NA_01.a b/code/sdk/zhihui/drivers/ppg/vc921x/lib/lib_spo2_vc30FS_float_V07_miniSDK_NA_01.a new file mode 100644 index 0000000000000000000000000000000000000000..b4afede5207fe8e2e453a1312c339625ff40ae1a GIT binary patch literal 184840 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

SNz*5^~9L-9#c5_w$-P=F4*ucz5=P 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

bBxD8IvqmAOdwS<#VCU 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%0A?EVlckPC ze*Y$!m^jin0^9H4%8B<1^IL*`Vyoe2o5XqF6IKNMdgW$A7r(;*?0W1&J#IxA*)Dvo zlX@(KpQK^7-^KfGp1>IWX#77lJHOr|g6EmzyyVvnKNM%wON3xH-jjQT;4IhTS)Of1 t55lh=*T(HrU&F!t>T&Ru+p3=0ig(opDHP%)Ki(VL?;VtHtwdUW{{t$XIo1FG literal 0 HcmV?d00001 diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_core.c b/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_core.c new file mode 100644 index 000000000..e86ebd052 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_core.c @@ -0,0 +1,956 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-09-22 17:39:10 + * @LastEditTime: 2024-03-21 14:38:58 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ + +#include "vc921x_core.h" +#include "vc921x_core_com.h" + +#define CORE921x_FORMAT_ADCBIT_COMPARE(format_n,bit_n) ( ((format_n)>(bit_n))?((format_n)-(bit_n)):0) +#define CORE921x_DATA_FORMAT_COVNVERT(format_n,bit_n,data) ( (data)>>( CORE921x_FORMAT_ADCBIT_COMPARE( format_n, bit_n ) ) ) + +static const char CORE921x_VERSION[] = "core921x_v0.22"; + +/* rxgain+pdres ~ level */ +struct vc921x_core_res_table +{ + unsigned char level; + unsigned char pdres; + unsigned char rxgain; +}; +#if VC921x_CFG_DEFAULT_PPGGAIN_EN +#define MAX_RES_LEVEL 21 +#define MIN_RES_LEVEL 0 +#define MAX_CURRENT 0x7f +#define MIN_CURRENT 0 +const struct vc921x_core_res_table vc921x_res_table[] = + { + /* level, pdres, rxgain */ + {0, 0, 1}, /* 200K */ + {1, 1, 1}, + {2, 2, 1}, /* 400K */ + {3, 3, 1}, + {4, 4, 1}, /* 800K */ + {5, 5, 1}, + {6, 6, 1}, /* 1.6M */ + {7, 7, 1}, + {8, 8, 1},/* 3.2M */ + {9, 9, 1}, + {10, 10, 1}, /* 6.4M */ + {11, 11, 1}, /*8.96M*/ + {12, 12, 1}, + {13, 13, 1}, /*17.92*/ + {14, 12, 2}, + {15, 13, 2},/*35.84*/ + {16, 12, 3}, + {17, 13, 3},/*71.68*/ + {18, 12, 4}, + {19, 13, 4}, /* 143.36 */ + {20, 12, 5}, + {21, 13, 5}, /* 286.72 */ +}; +#else +#define MAX_RES_LEVEL 23 +#define MIN_RES_LEVEL 0 +#define MAX_CURRENT 0x7f +#define MIN_CURRENT 0 +const struct vc921x_core_res_table vc921x_res_table[] = + { + /* level, pdres, rxgain */ + {0, 0, 0}, /* 100K */ + {1, 1, 0}, + {2, 2, 0}, /* 200K */ + {3, 3, 0}, + {4, 4, 0}, /* 400K */ + {5, 5, 0}, + {6, 6, 0}, /* 800K */ + {7, 7, 0}, + {8, 8, 0}, /* 1.6M */ + {9, 9, 0}, + {10, 10, 0},/* 3.2M */ + {11, 11, 0}, + {12, 12, 0}, /* 6.4M */ + {13, 13, 0}, /*8.96M*/ + {14, 12, 1}, + {15, 13, 1}, /*17.92*/ + {16, 12, 2}, + {17, 13, 2},/*35.84*/ + {18, 12, 3}, + {19, 13, 3},/*71.68*/ + {20, 12, 4}, + {21, 13, 4}, /* 143.36 */ + {22, 12, 5}, + {23, 13, 5}, /* 286.72 */ +}; +#endif + +static unsigned char core921x_nto_version=0; /* 0127nto or(0808-1120mpw) */ +static unsigned char core921x_0808mpw=0; /* 0808mpw ~ 1120mpw */ +/* 需要在刚刚初始化完成之后去调用判断 */ +CORE921x_FUNC_RAM_SECTION int core921x_check_nto_version_init(void) +{ + unsigned char mpw0808_test=0xA0; + unsigned char read_buffer[4]={0}; + unsigned short count1=0, count2=0; + unsigned int delay_tick=1000; + vc921x_write_register(0x5C, &mpw0808_test, 1); + mpw0808_test=0; + vc921x_read_register(0x5C, &mpw0808_test, 1); + if( mpw0808_test==0xA0 ) + { + core921x_0808mpw=0; + mpw0808_test=0; + vc921x_write_register(0x5C, &mpw0808_test, 1); /* write 0, clear DEBUG register. */ + } + else + { + core921x_0808mpw=1; + } + /* 0127nto ~ 0808_1120mpw */ + vc921x_read_register( 0x05, &read_buffer[0], 2 ); + while(delay_tick--); + vc921x_read_register( 0x07, &read_buffer[2], 2 ); + count1 = (read_buffer[0]<<8)+read_buffer[1]; + count2 = (read_buffer[2]<<8)+read_buffer[3]; + if( count2-count1>=1 && count2-count1<=255 ) + { + core921x_nto_version=0; + } + else + { + core921x_nto_version=1; + } + CORE921x_DEBUG_LOG("0127nto=%d~0808mpw=%d", core921x_nto_version,core921x_0808mpw); + return core921x_nto_version; +} + +CORE921x_FUNC_RAM_SECTION static unsigned char get_level_by_res_rxgain(unsigned char pdres, unsigned char rxgain) +{ + unsigned char index = 0; + for (index = 0; index < sizeof(vc921x_res_table) / sizeof(vc921x_res_table[0]); index++) + { + if ((vc921x_res_table[index].pdres == pdres) && (vc921x_res_table[index].rxgain == rxgain)) + { + return vc921x_res_table[index].level; + } + } + return 0; +} +CORE921x_FUNC_RAM_SECTION static int get_res_rxgain_by_level(unsigned char level, unsigned char *ppdres, unsigned char *prxgain) +{ + *ppdres = vc921x_res_table[level].pdres; + *prxgain = vc921x_res_table[level].rxgain; + return 0; +} + +/**************************************************************** + * CORE_API.read() + * read IC event or sample data + ***************************************************************/ +CORE921x_FUNC_RAM_SECTION int __core921x_read_software_version(char **ppver) // +{ + if (ppver) + { + *ppver = (char *)CORE921x_VERSION; + return 0; + } + return -1; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_read_id(unsigned char *pid1, unsigned char *pid2) // +{ + int ret = 0; + if ((NULL == pid1) || (NULL == pid2)) + { + return 1; + } + ret = vc921x_read_register(CORE921x_DEV_VER_ID_H_ADDR, pid1, 1); + ret = vc921x_read_register(CORE921x_FDY_EXT_ID_L_ADDR, pid2, 1); + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_read_event_status(unsigned char *pevt, unsigned char *povsta, unsigned char *pwindex_bak, unsigned char *pwindex_rel ) // +{ + int ret = 0; + unsigned char reg[4] = {0}; + ret = vc921x_read_register(CORE921x_WORK_STATUS1_ADDR, reg, 4); + if (pevt) + { + *pevt = reg[0]; + } + if (povsta) + { + *povsta = reg[1]; + } + if( core921x_nto_version ) + { + if (pwindex_bak) + { + *pwindex_bak = reg[2]; + } + if (pwindex_rel) + { + *pwindex_rel = reg[3]; + } + } + else + { + if (pwindex_bak) + { + *pwindex_bak = reg[2]; + } + } + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_read_rcosc_tick(unsigned short int *ptick0, unsigned short int *ptick1) // +{ + int ret = 0; + unsigned char reg[4] = {0}; + if( core921x_nto_version==1 ) + { + ret = vc921x_read_register(CORE921x_OSC_CNT0_H_ADDR+1, reg, 4); + } + else + { + ret = vc921x_read_register(CORE921x_OSC_CNT0_H_ADDR, reg, 4); + } + if (ptick0) + { + *ptick0 = (reg[0] << 8) + reg[1]; + } + if (ptick1) + { + *ptick1 = (reg[2] << 8) + reg[3]; + } + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_read_output(vc921x_sample_info_t *psample_ret) +{ + int ret = 0; + core921x_reg_result_t reg_result = {0}; + if( core921x_nto_version==1 ) + { + ret = vc921x_read_register(CORE921x_SAMPLE_DATA_ADDR_BASE+1, (unsigned char *)®_result, sizeof(core921x_reg_result_t)); + } + else + { + ret = vc921x_read_register(CORE921x_SAMPLE_DATA_ADDR_BASE, (unsigned char *)®_result, sizeof(core921x_reg_result_t)); + } + + if (psample_ret) + { + psample_ret->slot_result[0].env_data = (reg_result.slot0_envh6 << 8) | reg_result.slot0_envl6; + psample_ret->slot_result[1].env_data = (reg_result.slot1_envh6 << 8) | reg_result.slot1_envl6; + psample_ret->slot_result[2].env_data = (reg_result.slot2_envh6 << 8) | reg_result.slot2_envl6; + + psample_ret->slot_result[0].u.ps_data = reg_result.slot0_ps; + psample_ret->slot_result[1].u.ps_data = reg_result.slot1_ps; + psample_ret->slot_result[2].u.ps_data = reg_result.slot2_ps; + + psample_ret->extra_result.bioinn_data = (reg_result.bioinn_valh << 8) | reg_result.bioinn_vall; + psample_ret->extra_result.bioext_data = (reg_result.bioext_valh << 8) | reg_result.bioext_vall; + psample_ret->extra_result.temp0_data = (reg_result.tempslot0_valh << 8) | reg_result.tempslot0_vall; + psample_ret->extra_result.temp1_data = (reg_result.tempslot1_valh << 8) | reg_result.tempslot1_vall; + psample_ret->extra_result.tempref_data = (reg_result.tempref_valh << 8) | reg_result.tempref_vall; + } + return ret; +} + +/*------------------------------------------------------------------------------------------------------------------------------------- +fifo 解析数据相关的函数功能模块 +-------------------------------------------------------------------------------------------------------------------------------------*/ +CORE921x_FUNC_RAM_SECTION static inline int fifo_calc_sample_slot_nums(vc921x_sample_info_t *psample_ret, unsigned char *p_ppgfreq_slotnum, unsigned char *p_psfreq_slotnum, unsigned char *p_bioppgfreq_slotnum) +{ + /* 1 ppg slot infomation */ + for( unsigned char i=0; i<3; i++ ) + { + if( psample_ret->slot_result[i].slot_enwork==1 && psample_ret->slot_result[i].slot_freq_sel==VC921x_FREQ_PPG ) + { + (*p_ppgfreq_slotnum)++; + } + else if( psample_ret->slot_result[i].slot_enwork==2 && psample_ret->slot_result[i].slot_freq_sel==VC921x_FREQ_PS && psample_ret->slot_result[i].slot_storefifo==VC921x_IN_STORE ) + { + (*p_psfreq_slotnum)++; + } + } + + /* 3 bio slot infomation */ + if ((psample_ret->extra_result.bioext_enwork==1||psample_ret->extra_result.bioinn_enwork==1)&&(psample_ret->extra_result.biofifo_mode==VC921x_BIO_INFIFO_PS_SGL||psample_ret->extra_result.biofifo_mode==VC921x_BIO_INFIFO_PPG_SUM)) + { + if (psample_ret->extra_result.bioext_enwork == 1) + { + (*p_psfreq_slotnum)++; + } + if (psample_ret->extra_result.bioinn_enwork == 1) + { + (*p_psfreq_slotnum)++; + } + } + else if ((psample_ret->extra_result.bioext_enwork==1||psample_ret->extra_result.bioinn_enwork==1)&&(psample_ret->extra_result.biofifo_mode==VC921x_BIO_INFIFO_PPG_SGL)) + { + if (psample_ret->extra_result.bioext_enwork == 1) + { + (*p_bioppgfreq_slotnum)++; + } + if (psample_ret->extra_result.bioinn_enwork == 1) + { + (*p_bioppgfreq_slotnum)++; + } + } + CORE921x_DEBUG_LOG("slotnum_ppgfreq[%d],slotnum_psfreq[%d], slotnum_bioppg[%d]", *p_ppgfreq_slotnum, *p_psfreq_slotnum, *p_bioppgfreq_slotnum); + return 0; +} + +CORE921x_FUNC_RAM_SECTION static inline int read_data_from_fifo_buffer(const unsigned char *pbuffer, unsigned char *poffset, unsigned char fifo_format_is24bit, unsigned char adc_resolution ) +{ + int data = 0; + if( fifo_format_is24bit ) + { + if( (*poffset)+2 >=192 ) return 0; + data = (pbuffer[(*poffset)]<<16) + (pbuffer[(*poffset)+1]<<8) + pbuffer[(*poffset)+2]; + data = CORE921x_DATA_FORMAT_COVNVERT( 24, adc_resolution, data ); + *poffset += 3; + } + else + { + if( (*poffset)+1 >=192 ) return 0; + data = (pbuffer[(*poffset)] << 8) + pbuffer[(*poffset) + 1]; + data = CORE921x_DATA_FORMAT_COVNVERT( 16, adc_resolution, data ); + *poffset += 2; + } + return data; +} + +CORE921x_FUNC_RAM_SECTION static inline int write_data_to_fifo_buffer( unsigned char *pbuffer, unsigned char offset, unsigned int ppg_data, unsigned char fifo_format_is24bit ) +{ + if( fifo_format_is24bit ) + { + if( offset*3+2>=VC921x_PPGSLOT_BUFFER_NUM*3 ) return -1; + pbuffer[offset*3+0]=(ppg_data>>16)&0xff; + pbuffer[offset*3+1]=(ppg_data>>8) &0xff; + pbuffer[offset*3+2]=(ppg_data>>0) &0xff; + } + else + { + if( offset*2+1>=VC921x_PPGSLOT_BUFFER_NUM*3 ) return -1; + pbuffer[offset*2+0]=(ppg_data>>8) &0xff; + pbuffer[offset*2+1]=(ppg_data>>0) &0xff; + } + return 0; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_read_fifo_data(vc921x_sample_info_t *psample_ret, unsigned char *pread_fifoaddr, unsigned char read_windex) // +{ + int ret = 0; + unsigned char i = 0, j = 0; + unsigned char rindex = *pread_fifoaddr; + unsigned char read_size = 0, wait_size = 0;; + unsigned char fifo_data[192] = {0}; + /* fifo有效通道数量 */ + unsigned char ppg_freq_slot_num = 0; /* ppg频率下ppg_slot的数量*/ + unsigned char ps_freq_slot_num = 0; /* ps频率下psbio_slot的数量*/ + unsigned char bio_ppgfreq_slot_num = 0; /* ppg频率下bio_slot的数量 */ + unsigned char ppg_ps_fifo_ratio = 0; /* ppg频率与ps频率的占比,用于计算有效ps频率的fifo数据位置 */ + + unsigned char fifo_bytes= 0; /* fifo_bytes= group_len*fifo_div */ + unsigned char group_len = 0; /* ppg0(slot1,2) ppg1(slot1,2) ps(1,2) */ + unsigned char group_num = 0; /* group_len * group_num = fifosize */ + /* array(ppg_freq+ps_freq) */ + unsigned char ppg_freq_array_len = 0; + unsigned char ps_freq_array_len = 0; + unsigned char array_offset = 0; /* group 组内偏移 */ + unsigned char slot_index = 0; + /* 当bio模式为VC921x_BIO_INFIFO_PPG_SGL,需要驱动进行加算 */ + unsigned short int bioext_ppgsgl_sum=0,bioinn_ppgsgl_sum=0; + /* 解析临时缓存ppg数据 */ + unsigned int analysis_ppg_data=0; + /************* 1.获取配置参数 *************/ + if (!psample_ret) + return 1; + ppg_ps_fifo_ratio = psample_ret->ps_div; /* 假设25/10=2.5hz,ppg频率中间隔10个,插入一个ps */ + fifo_calc_sample_slot_nums(psample_ret, &ppg_freq_slot_num, &ps_freq_slot_num, &bio_ppgfreq_slot_num); /* 获取各个fifo信息 */ + /* calc ppgfreq_fifodata/psfreq_fifodata array length, group length=ppg*ratio+ps */ + if( 1==psample_ret->fifo_format ) + { + ppg_freq_array_len += (ppg_freq_slot_num*3); + } + else + { + ppg_freq_array_len += (ppg_freq_slot_num*2); + } + ppg_freq_array_len+= (bio_ppgfreq_slot_num*2); + ps_freq_array_len += (ps_freq_slot_num * 2); + group_len = ppg_freq_array_len * ppg_ps_fifo_ratio + ps_freq_array_len; + fifo_bytes= group_len*psample_ret->fifo_div; + CORE921x_DEBUG_LOG("group_len(%d)=(%d*%d)+%d", group_len, ppg_freq_array_len, ppg_ps_fifo_ratio, ps_freq_array_len); + if(group_len==0||fifo_bytes==0) + { + return -1; + } + /************** 2.校验参数读取 *************/ + /* group=[ ppg0(slot0,slot1)+ppg1(slot0,slot1)+...ppgN(slot0,slot1)+ps(ps,bioinn,bioext) ] + fifo =[ group0,group1... ]n */ + if (0 == psample_ret->fifo_enable) + { + CORE921x_DEBUG_LOG("psample_ret->fifo_enable=%d", psample_ret->fifo_enable); + rindex = 0x00; /*disable fifo:fifo=ps频率,数据始于,0x00 */ + } + if (read_windex == rindex) /* 数据溢出或则无数据 */ + { + return -2; + } + if (read_windex > rindex) /* rindex-windex */ + { + wait_size = (read_windex - rindex); + } + else /* [rindex-192]+[0-windex] */ + { + wait_size = 192 - rindex + read_windex; + } + if( wait_size < fifo_bytes ) + { + CORE921x_DEBUG_ERR("[%x~%x]waite_size=%d, fifo_bytes=%d", rindex, read_windex, wait_size, fifo_bytes ); + return -3; + } + /* windex error process, the windex cover all fifo area and more than the rindex. */ + if (wait_size % fifo_bytes) + { + read_size = (wait_size/fifo_bytes)*fifo_bytes; + CORE921x_DEBUG_ERR("read_fifo_bytes(%d)/group_len(%d) != 0 read_size[%d]", wait_size, fifo_bytes, read_size ); + ret=1; + if( core921x_nto_version ) + { + return ret; + } + } + else + { + read_size=wait_size; + } + *pread_fifoaddr = ((rindex + read_size) >= 192) ? (rindex + read_size - 192) : (rindex + read_size); + ret = vc921x_write_register(CORE921x_FIFO_READADDR_INDEX, &rindex, 1); + ret = vc921x_read_register(CORE921x_FIFO_DATA_ADDR, &fifo_data[0], read_size); + + CORE921x_DEBUG_LOG("[%#x]-[%#x],fifo_bytes=%d,", rindex, read_windex, read_size); + CORE921x_DEBUG_LOG("PPG_data[0- 8]:0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x", fifo_data[0], fifo_data[1], fifo_data[2], fifo_data[3], fifo_data[4], fifo_data[5], fifo_data[6], fifo_data[7]); + CORE921x_DEBUG_LOG("PPG_data[9-16]:0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x", fifo_data[8], fifo_data[9], fifo_data[10], fifo_data[11], fifo_data[12], fifo_data[13], fifo_data[14], fifo_data[15]); + CORE921x_DEBUG_LOG("..............:0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x", fifo_data[read_size - 8], fifo_data[read_size - 7], fifo_data[read_size - 6], fifo_data[read_size - 5], fifo_data[read_size - 4], fifo_data[read_size - 3], fifo_data[read_size - 2], fifo_data[read_size - 1]); + /******** 3.group,array,cell analysis ********/ + if(group_len==0) + { + return 3; + } + group_num = read_size / group_len; + for (i = 0; i < group_num; i++) /* i=0~N,每次所有通道的采样PPG群数,每个群的处理 */ + { + array_offset = i * group_len; + bioinn_ppgsgl_sum=0,bioext_ppgsgl_sum=0; + /* ppg_freq array analysis*/ + for (j = 0; j < ppg_ps_fifo_ratio; j++) /* j=0-n,每个群里面拥有的ppg_freq的组数轮询 */ + { + for (slot_index = 0; slot_index < 3; slot_index++) + { + if (1 == psample_ret->slot_result[slot_index].slot_enwork && VC921x_FREQ_PPG == psample_ret->slot_result[slot_index].slot_freq_sel) + { + psample_ret->slot_result[slot_index].ppg_num += 1; + analysis_ppg_data = read_data_from_fifo_buffer(fifo_data, &array_offset, psample_ret->fifo_format, psample_ret->adc_resolution ); + write_data_to_fifo_buffer( psample_ret->slot_result[slot_index].ppg_buffer, psample_ret->slot_result[slot_index].ppg_num-1, analysis_ppg_data, psample_ret->fifo_format ); + CORE921x_DEBUG_LOG("slot_result[%d].ppg_data[%d]=%d", slot_index, i*ppg_ps_fifo_ratio+j, analysis_ppg_data ); + } + } + if( bio_ppgfreq_slot_num && VC921x_BIO_INFIFO_PPG_SGL==psample_ret->extra_result.biofifo_mode ) /* VC921x_BIO_INFIFO_PPG_SGL */ + { + if (1 == psample_ret->extra_result.bioinn_enwork) bioinn_ppgsgl_sum+=read_data_from_fifo_buffer(fifo_data, &array_offset, 0, 16 ); + if (1 == psample_ret->extra_result.bioext_enwork) bioext_ppgsgl_sum+=read_data_from_fifo_buffer(fifo_data, &array_offset, 0, 16 ); + } + } + if( bio_ppgfreq_slot_num && VC921x_BIO_INFIFO_PPG_SGL==psample_ret->extra_result.biofifo_mode ) + { + psample_ret->extra_result.bio_fifo_num += 1; + if (1 == psample_ret->extra_result.bioinn_enwork) + { + psample_ret->extra_result.bioinn_buffer[i] = bioinn_ppgsgl_sum; + CORE921x_DEBUG_LOG("result.bioinn_buffer[%d]=%d", i, psample_ret->extra_result.bioinn_buffer[i]); + } + if (1 == psample_ret->extra_result.bioext_enwork) + { + psample_ret->extra_result.bioext_buffer[i] = bioext_ppgsgl_sum; + CORE921x_DEBUG_LOG("result.bioext_buffer[%d]=%d", i, psample_ret->extra_result.bioext_buffer[i]); + } + } + /* ps_freq array analysis */ + // CORE921x_DEBUG_LOG("PS_ARRAY_OFFSET=(%d)?(%d)", ppg_freq_array_len * ppg_ps_fifo_ratio, array_offset); + /* 数据交换处理 */ + if ( VC921x_BIO_INFIFO_PS_SGL==psample_ret->extra_result.biofifo_mode ) + { + if (2 == psample_ret->slot_result[2].slot_enwork && VC921x_IN_STORE == psample_ret->slot_result[2].slot_storefifo) + { + psample_ret->slot_result[2].ppg_num += 1; + analysis_ppg_data = read_data_from_fifo_buffer( fifo_data, &array_offset, 0, 16 ); + write_data_to_fifo_buffer( psample_ret->slot_result[2].ppg_buffer, psample_ret->slot_result[2].ppg_num-1, analysis_ppg_data, 0 ); + CORE921x_DEBUG_LOG("slot_result[2].ps_data[%d]=%d", i, analysis_ppg_data); + } + if ( 1 == psample_ret->extra_result.bioinn_enwork || 1 == psample_ret->extra_result.bioext_enwork) + { + psample_ret->extra_result.bio_fifo_num += 1; + if (1 == psample_ret->extra_result.bioinn_enwork) + { + psample_ret->extra_result.bioinn_buffer[i] = read_data_from_fifo_buffer(fifo_data, &array_offset, 0, 16 ); + CORE921x_DEBUG_LOG("result.bioinn_buffer[%d]=%d", i, psample_ret->extra_result.bioinn_buffer[i]); + } + if (1 == psample_ret->extra_result.bioext_enwork) + { + psample_ret->extra_result.bioext_buffer[i] = read_data_from_fifo_buffer(fifo_data, &array_offset, 0, 16 ); + CORE921x_DEBUG_LOG("result.bioext_buffer[%d]=%d", i, psample_ret->extra_result.bioext_buffer[i]); + } + } + } + else if(VC921x_BIO_INFIFO_PPG_SUM == psample_ret->extra_result.biofifo_mode) + { + if ( 1==psample_ret->extra_result.bioinn_enwork||1==psample_ret->extra_result.bioext_enwork ) + { + psample_ret->extra_result.bio_fifo_num += 1; + if (1 == psample_ret->extra_result.bioinn_enwork) + { + psample_ret->extra_result.bioinn_buffer[i] = read_data_from_fifo_buffer(fifo_data, &array_offset, 0, 16 ); + CORE921x_DEBUG_LOG("result.bioinn_buffer[%d]=%d", i, psample_ret->extra_result.bioinn_buffer[i]); + } + if (1 == psample_ret->extra_result.bioext_enwork) + { + psample_ret->extra_result.bioext_buffer[i] = read_data_from_fifo_buffer(fifo_data, &array_offset, 0, 16 ); + CORE921x_DEBUG_LOG("result.bioext_buffer[%d]=%d", i, psample_ret->extra_result.bioext_buffer[i]); + } + } + if (2 == psample_ret->slot_result[2].slot_enwork && VC921x_IN_STORE == psample_ret->slot_result[2].slot_storefifo) + { + psample_ret->slot_result[2].ppg_num += 1; + analysis_ppg_data = read_data_from_fifo_buffer(fifo_data, &array_offset, 0, 16 ); + write_data_to_fifo_buffer( psample_ret->slot_result[2].ppg_buffer, psample_ret->slot_result[2].ppg_num-1, analysis_ppg_data, 0 ); + CORE921x_DEBUG_LOG("slot_result[2].ps_data[%d]=%d", i, analysis_ppg_data); + } + } + else /* VC921x_BIO_NOFIFO_PS_SGL VC921x_BIO_INFIFO_PPG_SGL */ + { + if (2 == psample_ret->slot_result[2].slot_enwork && VC921x_IN_STORE == psample_ret->slot_result[2].slot_storefifo) + { + psample_ret->slot_result[2].ppg_num += 1; + analysis_ppg_data = read_data_from_fifo_buffer(fifo_data, &array_offset, 0, 16 ); + write_data_to_fifo_buffer( psample_ret->slot_result[2].ppg_buffer, psample_ret->slot_result[2].ppg_num-1, analysis_ppg_data, 0 ); + CORE921x_DEBUG_LOG("slot_result[2].ps_data[%d]=%d", i, analysis_ppg_data); + } + } + } /* end of for(i 7) ? 7 : oversample; + slotcfg.slot_cfg1.bits.txch = txchsel; + slotcfg.slot_cfg2.bits.storefifo = slot_infifo; + + switch (sample_tim) + { + case VC921x_SAMPLE_TIM_50US: + slotcfg.slot_cfg1.bits.env_time = 2; + slotcfg.slot_cfg1.bits.sample_time = 0; + slotcfg.slot_cfg3.bits.sc = 3; /* env_time_config+1 */ + break; + case VC921x_SAMPLE_TIM_100US: + slotcfg.slot_cfg1.bits.env_time = 5; + slotcfg.slot_cfg1.bits.sample_time = 1; + slotcfg.slot_cfg3.bits.sc = 6; + break; + case VC921x_SAMPLE_TIM_150US: + slotcfg.slot_cfg1.bits.env_time = 8; + slotcfg.slot_cfg1.bits.sample_time = 2; + slotcfg.slot_cfg3.bits.sc = 9; + break; + case VC921x_SAMPLE_TIM_200US: + slotcfg.slot_cfg1.bits.env_time = 11; + slotcfg.slot_cfg1.bits.sample_time = 3; + slotcfg.slot_cfg3.bits.sc = 12; + break; + default: + slotcfg.slot_cfg1.bits.env_time = 5; + slotcfg.slot_cfg1.bits.sample_time = 1; + slotcfg.slot_cfg3.bits.sc = 6; + break; + } + ret = vc921x_write_register(slot_base_addr, (unsigned char *)&slotcfg, sizeof(core921x_slot_config_t)); + return ret; +} + +/* PPG通道及调整的函数接口 */ +CORE921x_FUNC_RAM_SECTION int __core921x_read_slot_adjust_param(unsigned char *pmax_res, unsigned char *pmin_res, unsigned char *pmax_curr, unsigned char *pmin_curr) +{ + *pmax_res = MAX_RES_LEVEL; + *pmin_res = MIN_RES_LEVEL; + *pmax_curr = MAX_CURRENT; + *pmin_curr = MIN_CURRENT; + return 0; +} + +/* 电流+电阻等级的读写,这部分是在使用过程中会反复调用的 */ +CORE921x_FUNC_RAM_SECTION int __core921x_read_slot_current_res(VC921x_PPG_SLOT_I slot_index, unsigned char *pslot_curr, unsigned char *pslot_reslvl) +{ + int ret = 0; + core921x_slot_config_t slotcfg = {0}; + /* read current */ + unsigned char cfg2_addr = CORE921x_CONFIG_SLOT_ADDR_BASE + (slot_index * 4) + 2; + ret = vc921x_read_register(cfg2_addr, (unsigned char *)&slotcfg.slot_cfg2, 1); + if (pslot_curr) + { + *pslot_curr = slotcfg.slot_cfg2.bits.current; + } + /* read reslvl */ + unsigned char cfg0_addr = CORE921x_CONFIG_SLOT_ADDR_BASE + (slot_index * 4); + unsigned char cfg3_addr = CORE921x_CONFIG_SLOT_ADDR_BASE + (slot_index * 4) + 3; + vc921x_read_register(cfg0_addr, (unsigned char *)&slotcfg.slot_cfg0, 1); + vc921x_read_register(cfg3_addr, (unsigned char *)&slotcfg.slot_cfg3, 1); + if (pslot_reslvl) + { + //*pslot_reslvl = slotcfg.slot_cfg3.bits.resistance; + *pslot_reslvl = get_level_by_res_rxgain(slotcfg.slot_cfg3.bits.resistance, slotcfg.slot_cfg0.bits.rxgain); + } + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_set_ppgslot_current(VC921x_PPG_SLOT_I slot_index, unsigned char current) +{ + int ret = 0; + core921x_slot_cfg2bit slotcfg2 = {0}; + unsigned char cfg2_addr = CORE921x_CONFIG_SLOT_ADDR_BASE + (slot_index * 4) + 2; + ret = vc921x_read_register(cfg2_addr, (unsigned char *)&slotcfg2, 1); + slotcfg2.current = (current >= MAX_CURRENT) ? MAX_CURRENT : current; + ret = vc921x_write_register(cfg2_addr, (unsigned char *)&slotcfg2, 1); + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_set_ppgslot_resistance(VC921x_PPG_SLOT_I slot_index, unsigned char res_level, unsigned char allow_oversample ) +{ + int ret = 0; + unsigned char res = 0, rxgain = 0; + core921x_slot_ppg_cfg0bit slotcfg0 = {0}; + core921x_slot_cfg3bit slotcfg3 = {0}; + unsigned char cfg0_addr = CORE921x_CONFIG_SLOT_ADDR_BASE + (slot_index * 4); + unsigned char cfg3_addr = CORE921x_CONFIG_SLOT_ADDR_BASE + (slot_index * 4) + 3; + ret = vc921x_read_register(cfg0_addr, (unsigned char *)&slotcfg0, 1); + ret = vc921x_read_register(cfg3_addr, (unsigned char *)&slotcfg3, 1); + res_level = (res_level >= MAX_RES_LEVEL) ? MAX_RES_LEVEL : res_level; + get_res_rxgain_by_level(res_level, &res, &rxgain); + slotcfg3.resistance = res; + slotcfg0.rxgain=rxgain; +#if 1 + if (slot_index <= 1 && allow_oversample) + { + slotcfg0.oversample = 1 * rxgain; + } +#endif + ret = vc921x_write_register(cfg0_addr, (unsigned char *)&slotcfg0, 1); + ret = vc921x_write_register(cfg3_addr, (unsigned char *)&slotcfg3, 1); + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_set_slotx_pd_config( VC921x_SLOT_CHSEL slot0_pd, VC921x_SLOT_CHSEL slot1_pd, VC921x_SLOT_CHSEL slot2_pd ) +{ + int ret = 0; + core921x_pd_cfgbit pd_config = {0}; + pd_config.slot0_pdch = slot0_pd; + pd_config.slot1_pdch = slot1_pd; + pd_config.slot2_pdch = slot2_pd; + ret = vc921x_write_register(0x4C, (unsigned char *)&pd_config, sizeof(pd_config)); + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_set_anti_slot_config( unsigned char anti_current, VC921x_CFG_FUNC deslot0, VC921x_CFG_FUNC deslot1, VC921x_CFG_FUNC deslot2, VC921x_PPG_TXCH_I antich ) +{ + int ret = 0; + core921x_anti_cfgbit anti_config = {0}; + core921x_anti_cfgbit_1120mpw anti_config_1120mpw={0}; + if( core921x_nto_version ) + { + anti_config.anti_current = anti_current>=7?7:anti_current; + anti_config.deslot0 = deslot0; + anti_config.deslot1 = deslot1; + anti_config.deslot2 = deslot2; + anti_config.antiTxch=antich; + ret = vc921x_write_register(0x4D, (unsigned char *)&anti_config, sizeof(core921x_anti_cfgbit)); + } + else + { + if( core921x_0808mpw ) return ret; + anti_config_1120mpw.anti_current = anti_current>=7?7:anti_current; + anti_config_1120mpw.deslot0 = deslot0; + anti_config_1120mpw.deslot1 = deslot1; + anti_config_1120mpw.deslot2 = deslot2; + ret = vc921x_write_register(0x4D, (unsigned char *)&anti_config_1120mpw, sizeof(core921x_anti_cfgbit_1120mpw)); + } + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_set_common_config(VC921x_CFG_FUNC deenv_en, VC921x_CFG_FUNC i2crelease_en, VC921x_CFG_FUNC bio_disfreqHop, VC921x_CFG_FUNC fifo_format, VC921x_CFG_FUNC fifo_en, unsigned char cpd) +{ + int ret = 0; + core921x_com_cfgbit com_config = {0}; + com_config.deenv_en = deenv_en; + com_config.i2crelease_en = i2crelease_en; + com_config.bio_disfreqHop = bio_disfreqHop; + com_config.fifoformat = fifo_format; + com_config.fifo_en = fifo_en; + com_config.cpd = cpd; + if( core921x_0808mpw ) + { + com_config.bio_disfreqHop = 0; /* deFlick */ + ret = vc921x_write_register(0x4D, (unsigned char *)&com_config, sizeof(com_config)); + } + else + { + ret = vc921x_write_register(0x4E, (unsigned char *)&com_config, sizeof(com_config)); + } + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_set_sample_frequency(unsigned short int div1, unsigned char div2, unsigned char div3) +{ + int ret = 0; + unsigned char freq_config[4] = {0}; + freq_config[0] = (div1 >> 8); + freq_config[1] = (div1 & 0xFF); + freq_config[2] = div2; + freq_config[3] = div3; + if( core921x_0808mpw ) + { + ret = vc921x_write_register(0x4E, freq_config, sizeof(freq_config)); + } + else + { + ret = vc921x_write_register(0x4F, freq_config, sizeof(freq_config)); + } + return ret; +} + +/**************************************************************** + * CORE_API.write()/set() + * txchx_comconfig & ppg threshold configuration + * write or set config registers + ***************************************************************/ +CORE921x_FUNC_RAM_SECTION int __core921x_set_threshold_config(unsigned char ppgth_h, unsigned char ppgth_l) +{ + int ret = 0; + core921x_ppgth_cfgbit ppgth_config = {0}; + ppgth_config.ppgthh = (ppgth_h >= 0xf) ? 0xf : ppgth_h; + ppgth_config.ppgthl = (ppgth_l >= 0xf) ? 0xf : ppgth_l; + if( core921x_0808mpw ) + { + ret = vc921x_write_register(0x52, (unsigned char *)&ppgth_config, 1); + } + else + { + ret = vc921x_write_register(0x53, (unsigned char *)&ppgth_config, 1); + } + return ret; +} +/**************************************************************** + * CORE_API.write()/set() + * extra(ADC,BIO,temperature,AT/DT) configuration + * write or set config registers + ***************************************************************/ +CORE921x_FUNC_RAM_SECTION int __core921x_set_interrupt_config(VC921x_IO_MODE int_mode, VC921x_IO_DIR int_dir, VC921x_IO_PULSE_TIM int_len, unsigned char evt_en) +{ + int ret = 0; + core921x_int_cfgbit int_config = {0}; + int_config.intmode = (unsigned char)int_mode; + int_config.intdir = (unsigned char)int_dir; + int_config.intlen = (unsigned char)int_len; + if (evt_en & VC921x_EN_EVT_FREQ2) + { + int_config.intfreq2_en = 1; + } + if (evt_en & VC921x_EN_EVT_FIFO) + { + int_config.intfifo_en = 1; + } + if (evt_en & VC921x_EN_EVT_OVLD) + { + int_config.intov_en = 1; + } + if (evt_en & VC921x_EN_EVT_PPG) + { + int_config.intppg_en = 1; + } + if( core921x_0808mpw ) + { + ret = vc921x_write_register(0x53, (unsigned char *)&int_config, sizeof(int_config)); + } + else + { + ret = vc921x_write_register(0x54, (unsigned char *)&int_config, sizeof(int_config)); + } + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_set_bioslot_config(VC921x_CFG_FUNC bioinn_en, VC921x_CFG_FUNC bioext_en, VC921x_BIO_MODE biofifo_mode, VC921x_BIO_CHSEL bio_chsel, unsigned char biofreq, unsigned char biocap, unsigned char biotime) +{ + int ret = 0; + core921x_bio_cfg0bit bio_config0 = {0}; + core921x_bio_cfg1bit bio_config1 = {0}; + bio_config0.bio_inn_en = bioinn_en; + bio_config0.bio_ext_en = bioext_en; + bio_config0.bio_mode = biofifo_mode; + bio_config0.bio_pdch = bio_chsel; + bio_config0.bio_freq = (biofreq > 3) ? 3 : biofreq; + + bio_config1.bio_capadd = (biocap > 0x1f) ? (0x1f) : biocap; + bio_config1.bio_time = (biotime > 0x7) ? (0xf) : biotime; + if( core921x_0808mpw ) + { + ret = vc921x_write_register(0x54, (unsigned char *)&bio_config0, 1); + ret = vc921x_write_register(0x55, (unsigned char *)&bio_config1, 1); + } + else + { + ret = vc921x_write_register(0x55, (unsigned char *)&bio_config0, 1); + ret = vc921x_write_register(0x56, (unsigned char *)&bio_config1, 1); + } + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_set_bioinn_addcap(unsigned char biocap) +{ + int ret = 0; + core921x_bio_cfg1bit bio_config1 = {0}; + if( core921x_0808mpw ) + { + vc921x_read_register(0x55, (unsigned char *)&bio_config1, 1); + bio_config1.bio_capadd = (biocap > 0x1f) ? (0x1f) : biocap; + ret = vc921x_write_register(0x55, (unsigned char *)&bio_config1, 1); + } + else + { + vc921x_read_register(0x56, (unsigned char *)&bio_config1, 1); + bio_config1.bio_capadd = (biocap > 0x1f) ? (0x1f) : biocap; + ret = vc921x_write_register(0x56, (unsigned char *)&bio_config1, 1); + } + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_set_temperature_config(VC921x_CFG_FUNC tempslot0_en, VC921x_TEMP_SLOT tempslot0_chsel, unsigned char tempslot0_current, +VC921x_CFG_FUNC tempslot1_en, VC921x_TEMP_SLOT tempslot1_chsel, unsigned char tempslot1_current ) +{ + int ret = 0; + core921x_temp_cfgbit temp_config[2] = {0}; + temp_config[0].temp_enable = tempslot0_en; + temp_config[0].temp_slotch = tempslot0_chsel; + temp_config[0].temp_currset = (tempslot0_current >= 7) ? 7 : tempslot0_current; + temp_config[1].temp_enable = tempslot1_en; + temp_config[1].temp_slotch = tempslot1_chsel; + temp_config[1].temp_currset = (tempslot1_current >= 7) ? 7 : tempslot1_current; + if(core921x_0808mpw) + { + ret = vc921x_write_register(0x56, (unsigned char *)&temp_config, sizeof(temp_config)); + } + else + { + ret = vc921x_write_register(0x57, (unsigned char *)&temp_config, sizeof(temp_config)); + } + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_set_adc_config( VC921x_ADC_RESOLUTION adc_resulution, VC921x_CFG_FUNC ovdata_sum_enable ) +{ + int ret=0; + core921x_adc_config_t adc_config={0}; + core921x_adc_config_t_1120mpw adc_config_1120mpw={0}; + if( core921x_nto_version ) + { + adc_config.ppg_resulution=adc_resulution; + adc_config.ovtype = ovdata_sum_enable; + adc_config.cmpset = 3; + adc_config.env_clkdiv=0; + adc_config.adc_speed=0; + ret = vc921x_write_register(0x59, (unsigned char *)&adc_config, sizeof(adc_config)); + } + else + { + adc_config_1120mpw.ppg_resulution=adc_resulution; + adc_config_1120mpw.ovtype = ovdata_sum_enable; + adc_config_1120mpw.reserved = 0; + adc_config_1120mpw.env_clkdiv=0; + adc_config_1120mpw.adc_speed=0; + if( core921x_0808mpw ) + { + ret = vc921x_write_register(0x58, (unsigned char *)&adc_config_1120mpw, sizeof(adc_config_1120mpw)); + } + else + { + ret = vc921x_write_register(0x59, (unsigned char *)&adc_config_1120mpw, sizeof(adc_config_1120mpw)); + } + } + //CORE921x_DEBUG_LOG("write register adc_resulution=[%d],ovdata_sum_enable=[%d]", adc_resulution, ovdata_sum_enable); + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_set_rcosc_frequency(unsigned char oscset) +{ + int ret = 0; + if( core921x_0808mpw ) + { + ret = vc921x_write_register(0x59, &oscset, 1); + } + else + { + ret = vc921x_write_register(0x5A, &oscset, 1); + } + return ret; +} + +CORE921x_FUNC_RAM_SECTION int __core921x_write_work_cmd(VC921x_WORK_CMD cmd) // +{ + int ret = 0; + unsigned char wcmd = 0; + switch (cmd) + { + case VC921x_STOP_WORK_CMD: + wcmd = 0x00; + break; + case VC921x_START_WORK_CMD: + wcmd = 0x5A; + break; + case VC921x_RESET_SOFT_CMD: + wcmd = 0xA5; + break; + default: + return 1; + } + ret = vc921x_write_register(CORE921x_WORK_CMD_ADDR_BASE, &wcmd, 1); + return ret; +} diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_driver.c b/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_driver.c new file mode 100644 index 000000000..1f6b548fd --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_driver.c @@ -0,0 +1,1907 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-11-03 01:20:20 + * @LastEditTime: 2024-04-11 20:37:31 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ +#include "vc921x_core_com.h" +#include "vc921x_driver_config.h" +#include "vc921x_driver.h" + +#if (VC921x_CFG_WEAR_BIO_OLD_EN==VC921x_FUNC_ENABLE) +#include "BIODetect_V14_1.h" +#endif + +#ifndef VC_ABS_MAX +#define VC_ABS_MAX(a, b, max) (((a) > (b)) ? ((a) - (b)) : (max + (a) - (b))) +#endif +#ifndef VC_ABS +#define VC_ABS(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) +#endif + +#ifndef VC921x_ENV_CONVERT +#define VC921x_ENV_CONVERT( hex_env ) (((hex_env)>>8)*50+((hex_env)&0xff)) +#endif + +unsigned short int VC921x_CFG_MCU_TICK_FREQUENCY=32000; /* extern unsigned short int VC921x_CFG_MCU_TICK_FREQUENCY; */ +unsigned short int VC921x_WEAR_PARAM_BIO_ABS_HOLD=100; /* extern unsigned short int VC921x_WEAR_PARAM_BIO_ABS_HOLD; */ +unsigned short int VC921x_WEAR_PARAM_BIO_ABS_DROP=100; /* extern unsigned short int VC921x_WEAR_PARAM_BIO_ABS_HOLD; */ + +unsigned int vc921x_calc_ps_reflect_thh( unsigned char psI, unsigned char psR, unsigned char ps_adcbit ) +{ + unsigned int calc_psthh = 0; + float fix_I = vcare_I_exponent_func( 0.8f, VC921x_WEAR_PARAM_PS_CURRENT ); + float fix_R = vcare_R_exponent_func( 100.0f, VC921x_WEAR_PARAM_PS_RESISTANCE ); + float real_I= vcare_I_exponent_func( 0.8f, psI ); +#if VC921x_CFG_DEFAULT_PPGGAIN_EN + float real_R= vcare_R_exponent_func( 200.0f, psR ); /* !!notice:gain_min=1 */ +#else + float real_R= vcare_R_exponent_func( 100.0f, psR ); /* !!notice:gain_min=0 */ +#endif + unsigned int adc_gain=1<<(ps_adcbit-16); + float k = (real_I*real_R)/(fix_I*fix_R*(1<wear_drop_flag = 0; + if (drv_info_handle->fifo_read_data_valid) + { + /* ppg ps slot */ + for (i = 0; i < 3; i++) + { + presult_info->slot_result[i].ppg_num = 0; + vcare_memset(presult_info->slot_result[i].ppg_buffer, 0, sizeof(presult_info->slot_result[i].ppg_buffer)); + } + /* bioinn bioext */ + presult_info->extra_result.bio_fifo_num = 0; + vcare_memset(presult_info->extra_result.bioinn_buffer, 0, sizeof(presult_info->extra_result.bioinn_buffer)); + vcare_memset(presult_info->extra_result.bioext_buffer, 0, sizeof(presult_info->extra_result.bioext_buffer)); + } +} + +/******************************************************************************************************** + * 驱动参数保存部分 + ********************************************************************************************************/ +static int drv_infomation_param_save(void) +{ + int ret = 0; + unsigned char save_buf[12] = {0}; + save_buf[0] = 0xAA; + save_buf[1] = drv_info_handle->optimal_bio_valid = 1; + save_buf[2] = drv_info_handle->optimal_cap; + + save_buf[3] = (drv_info_handle->optimal_bio_gap >> 24) & 0xff; /* calibration deviation */ + save_buf[4] = (drv_info_handle->optimal_bio_gap >> 16) & 0xff; + save_buf[5] = (drv_info_handle->optimal_bio_gap >> 8) & 0xff; + save_buf[6] = (drv_info_handle->optimal_bio_gap >> 0) & 0xff; + save_buf[7] = (drv_info_handle->optimal_bio_temp >> 24) & 0xff; /* temperature deviation */ + save_buf[8] = (drv_info_handle->optimal_bio_temp >> 16) & 0xff; + save_buf[9] = (drv_info_handle->optimal_bio_temp >> 8) & 0xff; + save_buf[10] = (drv_info_handle->optimal_bio_temp >> 0) & 0xff; + ret = vc921x_param_write_flash(save_buf, sizeof(save_buf)); + VCARE_DBG_LOG("save bio param: %d,%d,%d,%d(temp)", drv_info_handle->optimal_bio_valid, drv_info_handle->optimal_cap, drv_info_handle->optimal_bio_gap, drv_info_handle->optimal_bio_temp); + return ret; /* successed return 0 */ +} +static int drv_infomation_param_load(void) +{ + int ret = 0; + unsigned char load_buf[12] = {0}; + ret = vc921x_param_read_flash(load_buf, sizeof(load_buf)); + if (load_buf[0] == 0xAA) + { + drv_info_handle->optimal_bio_valid = load_buf[1]; + drv_info_handle->optimal_cap = load_buf[2]; + drv_info_handle->optimal_bio_gap = (int)((load_buf[3] << 24) + (load_buf[4] << 16) + (load_buf[5] << 8) + (load_buf[6])); + drv_info_handle->optimal_bio_temp= (int)((load_buf[7] << 24) + (load_buf[8] << 16) + (load_buf[9] << 8) + (load_buf[10])); + } + else + { + drv_info_handle->optimal_bio_valid = 0; + drv_info_handle->optimal_cap = 0; + drv_info_handle->optimal_bio_gap = 0; + drv_info_handle->optimal_bio_temp=0; + ret=0; + } + VCARE_DBG_LOG("load bio param: %d,%d,%d,%d(temp)", drv_info_handle->optimal_bio_valid, drv_info_handle->optimal_cap, drv_info_handle->optimal_bio_gap, drv_info_handle->optimal_bio_temp); + return ret; /* successed return 0 */ +} +/* clear param,and */ +static int drv_infomation_param_reset(void) +{ + drv_info_handle->optimal_bio_valid = 0; + drv_info_handle->optimal_cap = 0; + drv_info_handle->optimal_bio_gap = 0; + drv_info_handle->optimal_bio_temp=0; + /* init [dichotomy]param */ + last_ext_bio=0; + remember_biomax_mark=0; + remember_biomin_mark=0; + remember_biomax_value = 0; + remember_biomin_value = 0; + /* 二分中值及采样标志 */ + last_inn_bio_mid=0; + set_flg_mid=0; + /* 二分初始数据 */ + set_cap_max=31; + set_cap_mid=16; + set_cap_min=1; + return 0; +} +/***************************************************************************** + * driver_work_mode table: + * {WORK_MODE, MODE_INIT_FUNC, MODE_HANDLER_FUNC } + *****************************************************************************/ +static const vc921x_mode_ops *sel_mode_ops = NULL; +static int drv_heartrate_mode_init(void); +static int drv_spo2_mode_init(void); +static int drv_bloodpressure_mode_init(void); +static int drv_wear_mode_init(void); +static int drv_temperature_mode_init(void); +static int drv_factory_green_ps_mode_init(void); +static int drv_factory_ir_red_mode_init(void); +static int drv_hrspo2_mode_init(void); +/*handler*/ +static int drv_ppg_mode_handler(const void *ops_self, vcare_ppg_device_t *pdev); +static int drv_factory_mode_handler(const void *ops_self, vcare_ppg_device_t *pdev); + +static const vc921x_mode_ops mode_ops[] = + { + /* work_mode, work_mode_init, work_mode_handler */ + {WORK_MODE_HR, drv_heartrate_mode_init, drv_ppg_mode_handler}, + {WORK_MODE_SPO2, drv_spo2_mode_init, drv_ppg_mode_handler}, + {WORK_MODE_HRV, drv_heartrate_mode_init, drv_ppg_mode_handler}, + {WORK_MODE_STRESS, drv_heartrate_mode_init, drv_ppg_mode_handler}, + {WORK_MODE_BLOODPRESSURE, drv_bloodpressure_mode_init, drv_ppg_mode_handler}, + {WORK_MODE_WEAR, drv_wear_mode_init, drv_ppg_mode_handler}, + {WORK_MODE_TEMPERATURE, drv_temperature_mode_init, drv_ppg_mode_handler}, + {WORK_MODE_FACTORY0, drv_factory_green_ps_mode_init, drv_factory_mode_handler}, + {WORK_MODE_FACTORY1, drv_factory_ir_red_mode_init, drv_factory_mode_handler}, + {WORK_MODE_HRSPO2, drv_hrspo2_mode_init, drv_ppg_mode_handler}, +}; + +/****************************************************************************** + * 初始化部分 + ******************************************************************************/ +static void drv_infomation_param_init(vcare_ppg_device_t *pdev) +{ + int i; + unsigned char max_cur = 0, min_cur = 0, max_res_lvl = 0, min_res_lvl = 0; + /* slot_adjust param */ + core921x_read_slot_adjust_param(&max_res_lvl, &min_res_lvl, &max_cur, &min_cur); + for (i = 0; i < 3; i++) + { + adjust_info_handle[i]->const_max_current = max_cur; + adjust_info_handle[i]->const_min_current = min_cur; + adjust_info_handle[i]->max_current = max_cur; + adjust_info_handle[i]->const_max_res_level = max_res_lvl; + adjust_info_handle[i]->const_min_res_level = min_res_lvl; + adjust_info_handle[i]->max_res_level = max_res_lvl; + adjust_info_handle[i]->adj_param.current_step = VC921x_ADJ_I_STEP_DFT; + adjust_info_handle[i]->adj_param.direction = VC921x_ADJUST_DIR_NONE; + adjust_info_handle[i]->adj_param.direction_last = VC921x_ADJUST_DIR_NONE; + } + /* dev_wear param */ + drv_info_handle->optimal_bio_valid =1; /* 先置1,等待在factory模式下主动清除后进行配平 */ +#if (VC921x_WEAR_DFT_HOLD == VC921x_CFG_WEAR_DEFAULT_STA) + wear_info_handle->last_status = (WEAR_STA_NONE == wear_info_handle->last_status) ? WEAR_STA_HOLD : wear_info_handle->last_status; +#else + #if (VC921x_CFG_WEAR_BIO_OLD_EN!=VC921x_FUNC_ENABLE) + /* 注意:只能非边沿活体的条件下,第一次或者停止工作后,重置活体状态为DROP。边沿的方式调用stop因为机制问题,无论如何都不能完美处理stop之后的状态 */ + wear_info_handle->bio_wear_result = (WEAR_STA_NONE == wear_info_handle->last_status) ? VC921x_BIO_STA_DROP : wear_info_handle->bio_wear_result; + #endif + wear_info_handle->last_status = (WEAR_STA_NONE == wear_info_handle->last_status) ? WEAR_STA_DROP : wear_info_handle->last_status; +#endif +#if (VC921x_FUNC_ENABLE != VC921x_CFG_WEAR_DETECTION) + wear_info_handle->last_status = WEAR_STA_HOLD; +#endif + /* 切换模式如果为摘下时,需要关灯 */ + if (wear_info_handle->last_status == WEAR_STA_DROP && (drv_info_handle->work_mode != WORK_MODE_FACTORY0 && drv_info_handle->work_mode != WORK_MODE_FACTORY1)) + { + for (i = 0; i < 3; i++) + { + if (sample_info_handle->slot_result[i].slot_enwork == 1) + { + core921x_set_enable_config((VC921x_PPG_SLOT_I)i, VC921x_CFG_DISABLE); + sample_info_handle->slot_result[i].slot_enwork = 0; + } + if( drv_info_handle->ppg_convert_ps ) + { + drv_info_handle->ppg_convert_ps=0; + VC921x_PS_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[2] /*slot2_ps*/, VC921x_IN_STORE /*storefifo*/, VC921x_WEAR_PARAM_PS_CURRENT /*current*/, VC921x_WEAR_PARAM_PS_RESISTANCE /*resistance*/); + core921x_sample_slotx_config( VC921x_PPG_SLOT_2, sample_info_handle, VC921x_PPG_TXCH_2, VC921x_WEAR_PARAM_PS_GAIN /*rxgain*/, 0 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + } + } + } + wear_info_handle->wear_status = wear_info_handle->last_status; + pdev->wear = wear_info_handle->last_status; + + wear_info_handle->drop_cnt = VC921x_CFG_WEAR_DROP_CNT; + wear_info_handle->hold_cnt = VC921x_CFG_WEAR_HOLD_CNT; + wear_info_handle->acce_wear_status = WEAR_STA_HOLD; + wear_info_handle->algo_wear_status = WEAR_STA_HOLD; + /* dev_clk param */ + drv_calibration_clk_clear(); + /* read_fifo_address */ + drv_info_handle->read_fifo_address = 0; + if( drv_info_handle->work_mode==WORK_MODE_SPO2 || drv_info_handle->work_mode==WORK_MODE_HRSPO2 ) + { + drv_info_handle->ppg_max_thval = VC921x_PPG_SPO2_MAX_DEFT_THVAL; + drv_info_handle->ppg_min_thval = VC921x_PPG_SPO2_MIN_DEFT_THVAL; + } + else + { + drv_info_handle->ppg_max_thval = VC921x_PPG_MAX_DEFT_THVAL; + drv_info_handle->ppg_min_thval = VC921x_PPG_MIN_DEFT_THVAL; + } + if (wear_info_handle->bio_wear_result == VC921x_BIO_STA_NONE) + { +#if (VC921x_CFG_WEAR_BIO_OLD_EN==VC921x_FUNC_ENABLE) + f_BIOInit( VC921x_WEAR_PARAM_BIO_DELTA, VC921x_WEAR_PARAM_PS_DELTA ); + VCARE_DBG_LOG("f_BIOedgeinit(%d, %d, %d)", VC921x_WEAR_PARAM_BIO_DELTA, VC921x_WEAR_PARAM_PS_DELTA ); +#endif + } +} + +static int drv_calc_bioinn_auto_addcap(unsigned char status, vc921x_sample_info_t *psample_info) +{ + unsigned char finished_set=0; + unsigned char finished_mark=0; + if (status & VC921x_EVENT_INTPS_RDY) + { + if (drv_info_handle->optimal_bio_valid) + { + return 0; + } + /* 判断是否bioext发生变化,如果变化太大,那么直接校准失败 */ + if( last_ext_bio!=0 && VC_ABS(last_ext_bio,psample_info->extra_result.bioext_data)>=20 ) + { + VCARE_DBG_ERR("calc bioinn error, bioext running..."); + } + last_ext_bio = psample_info->extra_result.bioext_data; + /* 二分法校准,循环赋值,设置参数与采样值呈负相关 */ + if( set_flg_mid==1 ) + { + set_flg_mid=0; + last_inn_bio_mid=psample_info->extra_result.bioinn_data; + } + if( last_inn_bio_mid==0 ) + { + core921x_set_bioinn_addcap(psample_info,set_cap_mid); + set_flg_mid=1; + return 0; + } + /* 在合理范围内快速退出 */ + if( (psample_info->extra_result.bioext_data-VC921x_CALC_BIOINN_OFFSET)<=last_inn_bio_mid && (psample_info->extra_result.bioext_data+VC921x_CALC_BIOINN_OFFSET)>=last_inn_bio_mid ) + { + finished_set=set_cap_mid; + finished_mark=1; + VCARE_DBG_LOG("(+-100)now set cap[%d,%d,%d],now_bio_val[%d](%d) ->set[%d]", set_cap_min,set_cap_mid, set_cap_max, + last_inn_bio_mid, psample_info->extra_result.bioext_data, last_inn_bio_mid ); + } + else /* 不在范围,范围比较大,继续二分 */ + { + /* 检索完成,需要再min和max中取一个值 */ + if( (set_cap_max-set_cap_min)<=1 ) + { + if(remember_biomax_mark==1) + { + remember_biomax_mark=0; + remember_biomax_value = psample_info->extra_result.bioinn_data; + } + if( remember_biomin_mark==1 ) + { + remember_biomin_mark=0; + remember_biomin_value = psample_info->extra_result.bioinn_data; + } + if( remember_biomax_value==0 ) + { + remember_biomax_mark=1; + core921x_set_bioinn_addcap(psample_info,set_cap_max); + return 0; + } + if( remember_biomin_value==0 ) + { + remember_biomin_mark=1; + core921x_set_bioinn_addcap(psample_info,set_cap_min); + return 0; + } + if( VC_ABS(remember_biomin_value,psample_info->extra_result.bioext_data) <= VC_ABS(remember_biomax_value,psample_info->extra_result.bioext_data) ) + { + finished_set=set_cap_min;/* 取min,结束 */ + last_inn_bio_mid = remember_biomin_value; + } + else + { + finished_set=set_cap_max;/* 取max,结束 */ + last_inn_bio_mid = remember_biomax_value; + } + finished_mark=1; + VCARE_DBG_LOG("mingap(%d)[%d],maxgap(%d)[%d]->(%d)",set_cap_min,VC_ABS(remember_biomin_value,psample_info->extra_result.bioext_data), + set_cap_max,VC_ABS(remember_biomax_value,psample_info->extra_result.bioext_data), finished_set); + } + /* 继续执行二分逼近 */ + if( last_inn_bio_midextra_result.bioext_data ) + { + VCARE_DBG_LOG("(midset[%d]", set_cap_min,set_cap_mid, set_cap_max, + last_inn_bio_mid, psample_info->extra_result.bioext_data, (set_cap_min+set_cap_mid)/2 ); + set_cap_max = set_cap_mid; + set_cap_mid = (set_cap_min+set_cap_mid)/2; + set_flg_mid = 1; + core921x_set_bioinn_addcap(psample_info,set_cap_mid); + } + else if( last_inn_bio_mid>psample_info->extra_result.bioext_data ) + { + VCARE_DBG_LOG("(mid>bioext)now set cap[%d,%d,%d],now_bio_val[%d](%d) ->set[%d]", set_cap_min,set_cap_mid, set_cap_max, + last_inn_bio_mid, psample_info->extra_result.bioext_data, (set_cap_max+set_cap_mid)/2 ); + set_cap_min = set_cap_mid; + set_cap_mid = (set_cap_max+set_cap_mid)/2; + set_flg_mid = 1; + core921x_set_bioinn_addcap(psample_info,set_cap_mid); + } + else if( last_inn_bio_mid==psample_info->extra_result.bioext_data ) /* 完全相等,直接结束 */ + { + VCARE_DBG_LOG("(mid=bioext)now set cap[%d,%d,%d],now_bio_val[%d](%d) ->set[%d]", set_cap_min,set_cap_mid, set_cap_max, + last_inn_bio_mid, psample_info->extra_result.bioext_data, set_cap_mid); + finished_set=set_cap_mid; + finished_mark=1; + } + } + if( finished_mark ) /* 查找结束处理 */ + { + drv_info_handle->optimal_cap = finished_set; + drv_info_handle->optimal_bio_gap = last_inn_bio_mid - psample_info->extra_result.bioext_data; + drv_info_handle->optimal_bio_temp= psample_info->extra_result.temp0_data; /* inn_temp */ + /* set optimal_cap */ + core921x_set_bioinn_addcap(psample_info,drv_info_handle->optimal_cap); + VCARE_DBG_LOG("optimal_cap=%d, optimal_bio_gap=%d", drv_info_handle->optimal_cap, drv_info_handle->optimal_bio_gap ); + /* save flash_ok */ + drv_infomation_param_save(); + } + } + return -1; +} + +static void drv_calibration_entry_fast_irq( void ) +{ + core921x_set_interrupt_config( VC921x_EN_EVT_FREQ2|VC921x_EN_EVT_FIFO | VC921x_EN_EVT_OVLD | VC921x_EN_EVT_PPG ); +} +static void drv_calibration_exit_fast_irq( void ) +{ + if( drv_info_handle->work_mode==WORK_MODE_FACTORY0 || drv_info_handle->work_mode==WORK_MODE_FACTORY1 ) + { + core921x_set_interrupt_config( VC921x_EN_EVT_FREQ2 | VC921x_EN_EVT_FIFO | VC921x_EN_EVT_OVLD ); + } + else + { + core921x_set_interrupt_config( VC921x_EN_EVT_FIFO | VC921x_EN_EVT_OVLD | VC921x_EN_EVT_PPG ); + } +} + +static void drv_common_register_config( vc921x_drv_info *pdrv_info ) +{ + sample_info_handle->ppg_div = pdrv_info->ppg_div; + sample_info_handle->ps_div = pdrv_info->ps_div; + sample_info_handle->fifo_div = pdrv_info->fifo_div; + sample_info_handle->hold_div = pdrv_info->ps_div; + sample_info_handle->drop_div = pdrv_info->drop_div; +#if (VC921x_CFG_DROP_REDUCE_FREQ==VC921x_FUNC_ENABLE && 0==VC921x_PS_WORK_FREQUENCY_CONFIG) + if( WEAR_STA_DROP==wear_info_handle->wear_status ) + { + sample_info_handle->ps_div= pdrv_info->drop_div; + sample_info_handle->fifo_div=1; + } +#endif + sample_info_handle->fifo_enable = 1; + sample_info_handle->adc_resolution = 16; /* adc位数设置=16,18,20,22,注意初版0808mp的22bitADC不可用 */ + sample_info_handle->fifo_format = ( sample_info_handle->adc_resolution>=18 )?1:0; + core921x_set_common_config(sample_info_handle->fifo_enable, sample_info_handle->fifo_format, sample_info_handle->adc_resolution, + VC921x_CFG_PPG_CHANNL_SELECT, VC921x_CFG_PPG_CHANNL_SELECT, VC921x_CFG_PPG_CHANNL_SELECT); + /* set frequency and enable IRQ */ + core921x_set_sample_frequency(sample_info_handle->ppg_div, sample_info_handle->ps_div, sample_info_handle->fifo_div); + core921x_set_interrupt_config( VC921x_EN_EVT_FIFO | VC921x_EN_EVT_OVLD | VC921x_EN_EVT_PPG ); + /* ps2 */ + VC921x_PS_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[2] /*slot2_ps*/, VC921x_IN_STORE /*storefifo*/, VC921x_WEAR_PARAM_PS_CURRENT /*current*/, VC921x_WEAR_PARAM_PS_RESISTANCE /*resistance*/); + core921x_sample_slotx_config( VC921x_PPG_SLOT_2, sample_info_handle, VC921x_PPG_TXCH_2, VC921x_WEAR_PARAM_PS_GAIN/*rxgain*/, 0 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + /* bio */ +#if (VC921x_FUNC_ENABLE == VC921x_CFG_WEAR_BIO_EN) + VC921x_BIO_SLOT_PARAM_CONFIG(sample_info_handle->extra_result, VC921x_CFG_ENABLE /*bio_innenable*/, VC921x_CFG_ENABLE /*bio_extenable*/, VC921x_BIO_INFIFO_PPG_SUM /*biofifo_mode*/); + core921x_set_bioslot_config(sample_info_handle, drv_info_handle->optimal_cap); +#endif + /* inn-slot0,用于修正活体问题偏差及检验IC异常状态 */ + VC921x_TMP_SLOT_PARAM_CONFIG(sample_info_handle->extra_result, VC921x_CFG_ENABLE /*tmpslot0_enable*/, VC921x_CFG_ENABLE /*tmpslot1_enable*/, 6 /*tmp_setcur*/); + core921x_set_temperature_config(sample_info_handle); +} + +static int drv_heartrate_mode_init(void) +{ + unsigned char hr_over_sample_config=0; + VC921x_SAMPLE_TIM sample_time_config = VC921x_SAMPLE_TIM_100US; + if( WORK_MODE_HR==sel_mode_ops->mode ) + { + hr_over_sample_config = 0; + sample_info_handle->adc_resolution = 16; /* adc位数设置=16,18,20 */ + if( drv_info_handle->hr_algo_mode != 0 ) /* 20240531.心率普通模式特殊配置,开启16倍过采,200us发光@algo.h */ + { + hr_over_sample_config = VC921x_CFG_SPORT_HR_OVERSAMPLE; + sample_time_config = VC921x_CFG_SPORT_HR_SAMPLETIME; + } + sample_info_handle->fifo_format = ( sample_info_handle->adc_resolution>=18 )?1:0; + core921x_set_common_config(sample_info_handle->fifo_enable, sample_info_handle->fifo_format, sample_info_handle->adc_resolution, + VC921x_CFG_PPG_CHANNL_SELECT, VC921x_CFG_PPG_CHANNL_SELECT, VC921x_CFG_PPG_CHANNL_SELECT); + } + /* ppg0 */ + VC921x_PPG_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[0] /*slot0_ppg*/, VC921x_PPG_CURT /*current*/, VC921x_PPG_RESISTANCE /*resistance*/); + core921x_sample_slotx_config( VC921x_PPG_SLOT_0, sample_info_handle, VC921x_PPG_TXCH_0, VC921x_PPG_GAIN /*rxgain*/, hr_over_sample_config /*oversample,2^N*/, sample_time_config); + return 0; +} + +static int drv_spo2_mode_init(void) +{ + sample_info_handle->adc_resolution = 16; /* adc位数设置=16,18,20,22 */ + sample_info_handle->fifo_format = ( sample_info_handle->adc_resolution>=18 )?1:0; + core921x_set_common_config(sample_info_handle->fifo_enable, sample_info_handle->fifo_format, sample_info_handle->adc_resolution, + VC921x_CFG_PPG_CHANNL_SELECT, VC921x_CFG_PPG_CHANNL_SELECT, VC921x_CFG_PPG_CHANNL_SELECT); + /* ppg0 */ + VC921x_PPG_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[0] /*slot0_ppg*/, VC921x_PPG_CURT /*current*/, VC921x_PPG_RESISTANCE /*resistance*/); + core921x_sample_slotx_config(VC921x_PPG_SLOT_0, sample_info_handle, VC921x_PPG_TXCH_2, VC921x_PPG_GAIN /*rxgain*/, 3 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + /* ppg1 */ + VC921x_PPG_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[1] /*slot0_ppg*/, VC921x_PPG_CURT /*current*/, VC921x_PPG_RESISTANCE /*resistance*/); + core921x_sample_slotx_config(VC921x_PPG_SLOT_1, sample_info_handle, VC921x_PPG_TXCH_1, VC921x_PPG_GAIN /*rxgain*/, 3 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + return 0; +} +static int drv_bloodpressure_mode_init(void) +{ + /* ppg0 */ + VC921x_PPG_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[0] /*slot0_ppg*/, VC921x_PPG_CURT /*current*/, VC921x_PPG_RESISTANCE /*resistance*/); + core921x_sample_slotx_config(VC921x_PPG_SLOT_0, sample_info_handle, VC921x_PPG_TXCH_2, VC921x_PPG_GAIN /*rxgain*/, 0 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + return 0; +} +static int drv_wear_mode_init(void) +{ + return 0; +} +static int drv_factory_green_ps_mode_init(void) +{ + /* ppg0 */ + VC921x_PPG_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[0] /*slot0_ppg*/, VC921x_FAT_GREEN_MAX_CURT /*current*/, VC921x_FAT_RESISTANCE /*resistance*/); + core921x_sample_slotx_config(VC921x_PPG_SLOT_0, sample_info_handle, VC921x_PPG_TXCH_0, VC921x_FAT_GAIN /*rxgain*/, 0 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + /* ppg1 */ + VC921x_PPG_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[1] /*slot0_ppg*/, VC921x_FAT_RED_MAX_CURT /*current*/, VC921x_FAT_RESISTANCE /*resistance*/); + core921x_sample_slotx_config(VC921x_PPG_SLOT_1, sample_info_handle, VC921x_PPG_TXCH_1, VC921x_FAT_GAIN /*rxgain*/, 0 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + /* ps2 */ + VC921x_PS_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[2] /*slot2_ps*/, VC921x_IN_STORE /*storefifo*/, VC921x_WEAR_PARAM_PS_CURRENT /*current*/, VC921x_WEAR_PARAM_PS_RESISTANCE /*resistance*/); + core921x_sample_slotx_config(VC921x_PPG_SLOT_2, sample_info_handle, VC921x_PPG_TXCH_2, VC921x_WEAR_PARAM_PS_GAIN/*rxgain*/, 0 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + /* bio/temp */ +#if (VC921x_FUNC_ENABLE == VC921x_CFG_WEAR_BIO_EN) + VC921x_BIO_SLOT_PARAM_CONFIG(sample_info_handle->extra_result, VC921x_CFG_ENABLE /*bio_innenable*/, VC921x_CFG_ENABLE /*bio_extenable*/, VC921x_BIO_INFIFO_PPG_SUM /*biofifo_mode*/ ); + core921x_set_bioslot_config(sample_info_handle, drv_info_handle->optimal_cap); +#endif + /* += enable freq2ps irq */ + core921x_set_interrupt_config( VC921x_EN_EVT_FREQ2 | VC921x_EN_EVT_FIFO | VC921x_EN_EVT_OVLD ); + return 0; +} +static int drv_factory_ir_red_mode_init(void) +{ + /* ppg0 */ + VC921x_PPG_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[0] /*slot0_ppg*/, VC921x_FAT_IR_MAX_CURT /*current*/, VC921x_FAT_RESISTANCE /*resistance*/); + core921x_sample_slotx_config(VC921x_PPG_SLOT_0, sample_info_handle, VC921x_PPG_TXCH_2, VC921x_FAT_GAIN /*rxgain*/, 0 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + /* ppg1 */ + VC921x_PPG_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[1] /*slot0_ppg*/, VC921x_FAT_RED_MAX_CURT /*current*/, VC921x_FAT_RESISTANCE /*resistance*/); + core921x_sample_slotx_config(VC921x_PPG_SLOT_1, sample_info_handle, VC921x_PPG_TXCH_1, VC921x_FAT_GAIN /*rxgain*/, 0 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + /* ps2 */ + VC921x_PS_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[2] /*slot2_ps*/, VC921x_IN_STORE /*storefifo*/, VC921x_WEAR_PARAM_PS_CURRENT /*current*/, VC921x_WEAR_PARAM_PS_RESISTANCE /*resistance*/); + core921x_sample_slotx_config(VC921x_PPG_SLOT_2, sample_info_handle, VC921x_PPG_TXCH_2, VC921x_WEAR_PARAM_PS_GAIN/*rxgain*/, 0 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + /* bio/temp */ +#if (VC921x_FUNC_ENABLE == VC921x_CFG_WEAR_BIO_EN) + VC921x_BIO_SLOT_PARAM_CONFIG(sample_info_handle->extra_result, VC921x_CFG_ENABLE /*bio_innenable*/, VC921x_CFG_ENABLE /*bio_extenable*/, VC921x_BIO_INFIFO_PPG_SUM /*biofifo_mode*/ ); + core921x_set_bioslot_config(sample_info_handle, drv_info_handle->optimal_cap); +#endif + VC921x_TMP_SLOT_PARAM_CONFIG(sample_info_handle->extra_result, VC921x_CFG_ENABLE /*tmpslot0_enable*/, VC921x_CFG_ENABLE /*tmpslot1_enable*/, 6 /*tmp_setcur*/); + core921x_set_temperature_config(sample_info_handle); + /* += enable freq2ps irq */ + core921x_set_interrupt_config( VC921x_EN_EVT_FREQ2 | VC921x_EN_EVT_FIFO | VC921x_EN_EVT_OVLD ); + return 0; +} +static int drv_temperature_mode_init(void) +{ + return 0; +} + +static int drv_hrspo2_mode_init(void) +{ + drv_info_handle->ppg_convert_ps=1; + /* ppg0 */ + VC921x_PPG_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[0] /*slot0_ppg*/, VC921x_PPG_CURT /*current*/, VC921x_PPG_RESISTANCE /*resistance*/); + core921x_sample_slotx_config( VC921x_PPG_SLOT_0, sample_info_handle, VC921x_PPG_TXCH_0, VC921x_PPG_GAIN /*rxgain*/, 0 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + /* ppg1 */ + VC921x_PPG_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[1] /*slot0_ppg*/, VC921x_PPG_CURT /*current*/, VC921x_PPG_RESISTANCE /*resistance*/); + core921x_sample_slotx_config( VC921x_PPG_SLOT_1, sample_info_handle, VC921x_PPG_TXCH_1, VC921x_PPG_GAIN /*rxgain*/, 3 /*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + /* ppg-ps2 */ + VC921x_PPG_SLOT_PARAM_CONFIG(sample_info_handle->slot_result[2] /*slot2_ppg*/, VC921x_PPG_CURT /*current*/, VC921x_PPG_RESISTANCE /*resistance*/); + core921x_sample_slotx_config( VC921x_PPG_SLOT_2, sample_info_handle, VC921x_PPG_TXCH_2, VC921x_PPG_GAIN /*rxgain*/, 3/*oversample,2^N*/, VC921x_SAMPLE_TIM_100US); + return 0; +} + +static void drv_work_init(vcare_ppg_device_t *pdev, int mode, unsigned short int ppg_div, unsigned char ps_div, unsigned char fifo_div, unsigned char drop_div, unsigned char algo_mode) +{ + if( VC921x_DEV_REV_ID==pdev->id1 && VC921x_FDY_VER_ID0==pdev->id2 ) + { + sample_info_handle->ic_type_id = VC921x_DEV_REV_ID; + sample_info_handle->ic_ficv_id = VC921x_FDY_VER_ID0; + } + else + { + VCARE_DBG_ERR("vc921x id error"); + return ; + } + /* set & init param */ + drv_info_handle->work_mode = mode; + drv_info_handle->ppg_div = ppg_div; + drv_info_handle->ps_div = ps_div; + drv_info_handle->fifo_div = fifo_div; + drv_info_handle->drop_div = drop_div; + drv_info_handle->hr_algo_mode = algo_mode; /* 新增传入算法模式,配置不同心率配置 */ + drv_info_handle->ppg_convert_ps=0; + drv_infomation_param_load(); + core921x_write_work_cmd(VC921x_RESET_SOFT_CMD); + core921x_write_work_cmd(VC921x_STOP_WORK_CMD); + core921x_write_work_cmd(VC921x_START_WORK_CMD); + drv921x_nto_version = core921x_check_nto_version_init(); /* 初始化判断1120mpw和0127nto */ + core921x_write_work_cmd(VC921x_RESET_SOFT_CMD); + core921x_write_work_cmd(VC921x_STOP_WORK_CMD); + /* 公共配置初始化 */ + drv_common_register_config( drv_info_handle ); + /* 模式配置初始化 */ + if (sel_mode_ops) + { + if (sel_mode_ops->mode_init) sel_mode_ops->mode_init(); + } + else + { + return; + } + /* 驱动参数初始化*/ + drv_infomation_param_init(pdev); + /* 血样模式配置不同的阈值 */ + core921x_set_threshold_config(drv_info_handle->ppg_max_thval, drv_info_handle->ppg_min_thval); +#if 1 + unsigned char config_debug_reg[32] = {0}; + unsigned char debug_index = 0; + vc921x_read_register(0x40, config_debug_reg, 26); + VCARE_DBG_LOG("[0x40]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); + debug_index += 8; + VCARE_DBG_LOG("[0x48]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); + debug_index += 8; + VCARE_DBG_LOG("[0x50]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); + debug_index += 8; + VCARE_DBG_LOG("[0x58]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); +#endif + core921x_write_work_cmd(VC921x_START_WORK_CMD); +} + +/******************************************************************************************************** + *佩戴检测部分代码 + ********************************************************************************************************/ +#if (VC921x_CFG_WEAR_BIO_OLD_EN==VC921x_FUNC_ENABLE) +static int drv_calc_biology_sense( vc921x_wear_info *pwear_info, vc921x_sample_info_t *presult, unsigned short int bioinn_data, unsigned short int bioext_data, unsigned short int ps_data ) +{ + static unsigned int calc_ps = 0; + static int calc_bio_ext=0; + int ret = 0; + /* bio ps 有效判断 */ + if( bioext_data && ps_data ) + { + calc_bio_ext = bioext_data; + calc_ps = ps_data; + } + ret = f_BIOdetect(calc_bio_ext, calc_ps, VC921x_WEAR_PARAM_PSTH); + VCARE_DBG_LOG("__f_TPedge(%d,%d,%d)=%d =>[%d]", calc_bio_ext, calc_ps, VC921x_WEAR_PARAM_PSTH, ret, pwear_info->bio_wear_result); + if ( VC921x_BIO_STA_HOLD == ret) + { + pwear_info->bio_wear_result = VC921x_BIO_STA_HOLD; + pwear_info->algo_wear_status = WEAR_STA_HOLD; /* bio重新佩戴重置心率算法佩戴状态 */ + } + else if ( VC921x_BIO_STA_DROP == ret) + { + pwear_info->bio_wear_result = VC921x_BIO_STA_DROP; + } + return pwear_info->bio_wear_result; +} +#else +static int drv_calc_biology_sense_absolute(vc921x_wear_info *pwear_info, vc921x_sample_info_t *presult, unsigned short int bioinn_data, unsigned short int bioext_data, unsigned short int ps_data) +{ + unsigned short int bio_refence_base = 0; + unsigned char bio_ps_decline = 0; /* 1-单次下降满足,2-连续下降满足 */ + if (presult->extra_result.bioinn_enwork == 0 && presult->extra_result.bioext_enwork ==0 ) + { + return VC921x_BIO_STA_NONE; + } + if( !drv_info_handle->optimal_bio_valid ) + { + return VC921x_BIO_STA_NONE; + } + /* ps 有效判断 */ + if (ps_data) + { + /* 单次下降达到阈值 */ + if (pwear_info->last_ps_data[0] > ps_data && (pwear_info->last_ps_data[0] - ps_data) >= VC921x_WEAR_PARAM_PS_DELTA) + { + bio_ps_decline = 1; + } + /*连续两次供下降达到了阈值的1.5倍,避免刚好遇到两次采样缓慢下降的情况*/ + if ((pwear_info->last_ps_data[1] > pwear_info->last_ps_data[0]) && (pwear_info->last_ps_data[0] > ps_data) && (pwear_info->last_ps_data[1] - ps_data) >= (VC921x_WEAR_PARAM_PS_DELTA * 15 / 10)) + { + bio_ps_decline = 2; + } + /* buffer move */ + pwear_info->last_ps_data[1] = pwear_info->last_ps_data[0]; + pwear_info->last_ps_data[0] = ps_data; + } + /* bio 有效判断 */ + if (bioinn_data && bioext_data) + { + int real_bio_gap = drv_info_handle->optimal_bio_gap+(drv_info_handle->optimal_bio_temp-presult->extra_result.temp0_data)/36.0f*1.735f; + VCARE_DBG_LOG("calc_temp=%f", (drv_info_handle->optimal_bio_temp-presult->extra_result.temp0_data)/36.0f*1.735f ); + bio_refence_base = bioinn_data - real_bio_gap; + VCARE_DBG_LOG("bio_gap=%d(real=%d), bio_base=%d, bio_ext=%d, abs_gap(%d?[%d,%d])", + drv_info_handle->optimal_bio_gap, real_bio_gap, bio_refence_base, + bioext_data, VC_ABS(bioext_data, bio_refence_base), VC921x_WEAR_PARAM_BIO_ABS_DROP, VC921x_WEAR_PARAM_BIO_ABS_HOLD); + presult->extra_result.bioabs_calc = VC_ABS(bioext_data, bio_refence_base); + if (VC_ABS(bioext_data, bio_refence_base) >= VC921x_WEAR_PARAM_BIO_ABS_HOLD) + { + pwear_info->bio_wear_result = VC921x_BIO_STA_HOLD; + pwear_info->algo_wear_status = WEAR_STA_HOLD; /* bio重新佩戴重置心率算法佩戴状态 */ + } + else if ((VC_ABS(bioext_data, bio_refence_base) <= VC921x_WEAR_PARAM_BIO_ABS_DROP) && bio_ps_decline) + { + pwear_info->bio_wear_result = VC921x_BIO_STA_DROP; + } + } + return pwear_info->bio_wear_result; +} +#endif + +static vc921x_wear_change drv_wear_status_identify(vc921x_wear_info *pwear_info, vc921x_sample_info_t *presult) +{ + vc921x_wear_change ret_change = VC921x_WEAR_NO_CHANGE; + int ret_bio_algo = VC921x_BIO_STA_NONE; + /* 根据是否存入fifo对参数进行取值 和 采样次数进行判断 */ + unsigned char data_index = 0; + unsigned char sample_num = 1; + unsigned int ps = 0; + unsigned short int env = 0; + unsigned short int bioinn = 0; + unsigned short int bioext = 0; + unsigned int calc_psthh=VC921x_WEAR_PARAM_PSTH; + + if (presult->slot_result[2].slot_storefifo == 1) + { + sample_num = presult->slot_result[2].ppg_num; + } + if( drv_info_handle->ppg_convert_ps ) + { + sample_num=1; + } + for (data_index = 0; data_index < sample_num; data_index++) + { + if (presult->slot_result[2].slot_storefifo == 1) + { + ps = VC921x_GET_SLOT_PS_DATA(presult, data_index ); + } + else + { + ps = presult->slot_result[2].u.ps_data << 8; + } + if (presult->extra_result.biofifo_mode == VC921x_BIO_NOFIFO_PS_SGL) + { + bioinn = presult->extra_result.bioinn_data; + bioext = presult->extra_result.bioext_data; + } + else + { + bioinn = presult->extra_result.bioinn_buffer[data_index]; + bioext = presult->extra_result.bioext_buffer[data_index]; + } + env = presult->slot_result[2].env_data; +#if (VC921x_FUNC_DISABLE == VC921x_CFG_WEAR_BIO_EN) + ret_bio_algo = VC921x_BIO_STA_NONE; +#else + if( drv_info_handle->ppg_convert_ps ) + { + ret_bio_algo = VC921x_BIO_STA_NONE; + } + else + { + #if (VC921x_CFG_WEAR_BIO_OLD_EN==VC921x_FUNC_ENABLE) + ret_bio_algo = drv_calc_biology_sense( pwear_info, presult, bioinn, bioext, ps ); + #else + ret_bio_algo = drv_calc_biology_sense_absolute(pwear_info, presult, bioinn, bioext, ps); + #endif + } +#endif + VCARE_DBG_LOG("status[%d] ps=%d,env=%d,bio_inn ext=(%d,%d)>bioret=%d", pwear_info->wear_status, ps, env, bioinn, bioext, ret_bio_algo); + VCARE_DBG_LOG("extern heart_algo_ret=%d,gsnesor_axis_ret=%d", pwear_info->algo_wear_status, pwear_info->acce_wear_status); +#if (VC921x_FUNC_DISABLE == VC921x_CFG_WEAR_ALG_EN) + pwear_info->algo_wear_status = WEAR_STA_HOLD; +#endif +#if (VC921x_FUNC_DISABLE == VC921x_CFG_WEAR_ACC_EN) + pwear_info->acce_wear_status = WEAR_STA_HOLD; +#endif + if( drv_info_handle->ppg_convert_ps ) + { + calc_psthh=vc921x_calc_ps_reflect_thh( presult->slot_result[2].slot_current, presult->slot_result[2].slot_resistance, presult->adc_resolution ); + VCARE_DBG_LOG("PS,fix(%d-%d),dyn(%d-%d)", ps, VC921x_WEAR_PARAM_PSTH, \ + VC921x_GET_SLOT_SAMPLE_DATA(presult, 2, presult->slot_result[2].ppg_num-1 ) ,calc_psthh ); + ps=VC921x_GET_SLOT_SAMPLE_DATA(presult, 2, presult->slot_result[2].ppg_num-1 ); + } + switch (pwear_info->wear_status) + { + case WEAR_STA_DROP: + if ((ps >= calc_psthh) && (env <= VC921x_WEAR_PARAM_ENVMIN) && (VC921x_BIO_STA_NONE == ret_bio_algo || VC921x_BIO_STA_HOLD == ret_bio_algo) && + (WEAR_STA_HOLD == pwear_info->algo_wear_status) && (WEAR_STA_HOLD == pwear_info->acce_wear_status)) + { + VCARE_DBG_LOG("drop->hold_cnt=%d", pwear_info->hold_cnt); + if (--pwear_info->hold_cnt <= 0) + { + pwear_info->hold_cnt = VC921x_CFG_WEAR_HOLD_CNT; + pwear_info->drop_cnt = VC921x_CFG_WEAR_DROP_CNT; + pwear_info->wear_status = WEAR_STA_HOLD; + ret_change = VC921x_DROP_TO_HOLD; + goto CHANGE_RETURN; + } + } + else + { + pwear_info->hold_cnt = VC921x_CFG_WEAR_HOLD_CNT; + } + break; + case WEAR_STA_HOLD: + if ((ps < calc_psthh) || (env > VC921x_WEAR_PARAM_ENVMAX) || (VC921x_BIO_STA_DROP == ret_bio_algo) || (WEAR_STA_DROP == pwear_info->algo_wear_status)) + { + if ((WEAR_STA_DROP == pwear_info->algo_wear_status) || (VC921x_BIO_STA_DROP == ret_bio_algo)) + { + pwear_info->drop_cnt = 1; + } + VCARE_DBG_LOG("hold->drop_cnt=%d", pwear_info->drop_cnt); + if (--pwear_info->drop_cnt <= 0) + { + pwear_info->hold_cnt = VC921x_CFG_WEAR_HOLD_CNT; + pwear_info->drop_cnt = VC921x_CFG_WEAR_DROP_CNT; + pwear_info->wear_status = WEAR_STA_DROP; + ret_change = VC921x_HOLD_TO_DROP; + goto CHANGE_RETURN; + } + } + else + { + pwear_info->drop_cnt = VC921x_CFG_WEAR_DROP_CNT; + } + break; + default: + break; + } + } +CHANGE_RETURN: + return ret_change; +} + +static int drv_read_wear_sample_data(vc921x_sample_info_t *pwear_result_info) +{ + drv_read_sample_param(pwear_result_info); + drv_read_fifo_data(pwear_result_info); + if( !clk_info_handle->clk_calc_status ) + { + pwear_result_info->slot_result[0].ppg_num=0;/* clear ppg_data number, the clock_frequency wait calibration */ + pwear_result_info->slot_result[1].ppg_num=0; + } + return 0; +} + +static wear_status drv_get_wear_status(vcare_ppg_device_t *pdev) +{ + wear_status calc_status = wear_info_handle->last_status; + vc921x_wear_change wear_change = VC921x_WEAR_NO_CHANGE; + vc921x_sample_info_t *result_info_ptr = pdev->result; +#if (VC921x_CFG_WEAR_DETECTION == VC921x_FUNC_DISABLE) + return WEAR_STA_HOLD; +#endif + drv_read_event_status(drv_info_handle); + drv_read_wear_sample_data(result_info_ptr); + VCARE_DBG_LOG("fifo_read_data_valid=%d", drv_info_handle->fifo_read_data_valid); + /* 数据读取及处理 */ + if ( drv_info_handle->fifo_read_data_valid ) + { + wear_change = drv_wear_status_identify(wear_info_handle, result_info_ptr); + if (VC921x_DROP_TO_HOLD == wear_change) + { + calc_status = WEAR_STA_HOLD; + wear_info_handle->wear_status = calc_status; + wear_info_handle->last_status = calc_status; + pdev->wear = calc_status; + drv_calibration_clk_clear(); + drv_work_init(pdev, drv_info_handle->work_mode, drv_info_handle->ppg_div, drv_info_handle->ps_div, drv_info_handle->fifo_div, drv_info_handle->drop_div, drv_info_handle->hr_algo_mode ); + } + else if (VC921x_HOLD_TO_DROP == wear_change) + { + calc_status = WEAR_STA_DROP; + wear_info_handle->wear_status = calc_status; + wear_info_handle->last_status = calc_status; + pdev->wear = calc_status; + /* 必须重新初始化,重置windex,否则摘下的瞬间,会出现windex错误偏移,导致数据解析出错 */ + drv_work_init(pdev, drv_info_handle->work_mode, drv_info_handle->ppg_div, drv_info_handle->ps_div, drv_info_handle->fifo_div, drv_info_handle->drop_div, drv_info_handle->hr_algo_mode); + result_info_ptr->wear_drop_flag = 1; /* 用于发送摘下时原始数据标志 */ + } + } + return calc_status; +} + +/******************************************************************************************************** + * 自动化调整部分代码 + ********************************************************************************************************/ +static int drv_adjust_overload_max_current(vc921x_sample_info_t *psample_result_info, unsigned char overload, int event) +{ + int ret = 0; + unsigned char slot_i = 0; + vc921x_adjust_info *padjust_info = NULL; + unsigned char ovload_mark[3] = {overload & VC921x_STATUS_CH0_OVERLOAD, overload & VC921x_STATUS_CH1_OVERLOAD, overload & VC921x_STATUS_CH2_OVERLOAD}; + for (slot_i = 0; slot_i < 3; slot_i++) + { + padjust_info = adjust_info_handle[slot_i]; + if (ovload_mark[slot_i]) + { + VCARE_DBG_LOG("ovload_mark[%d] now_cur=%d", slot_i, padjust_info->current); + if (padjust_info->current != 0) + { + padjust_info->current--; + padjust_info->max_current = padjust_info->current; + padjust_info->const_max_current = padjust_info->max_current; + /* set adjust mark & adjust param */ + core921x_set_ppgslot_current((VC921x_PPG_SLOT_I)slot_i, padjust_info->current); + } + else + { + ret = -1; + } + } + } + return ret; +} +static int drv_adjust_slot_current_res(vc921x_adjust_info *padjust_info, unsigned char slot_index, unsigned char thh_res_level, unsigned char thh_current) +{ + unsigned char allow_oversample = 0; + unsigned char old_res_level = padjust_info->res_level; + unsigned char old_current = padjust_info->current; + unsigned short int old_current_step = padjust_info->adj_param.current_step; + VCARE_DBG_LOG("ADF_SLOT[%d]-dir(%d)-I_R thh[%d,%d] I_R now(%d,%d)", slot_index, padjust_info->adj_param.direction, thh_res_level, thh_current, old_res_level, old_current); + if (padjust_info->current == thh_current) + { + if (padjust_info->res_level == thh_res_level) + { + VCARE_DBG_ERR("RESLVL-OFFSET-LOW IS MAX/MIN--[%d]>=[%d]", old_res_level, thh_res_level); + return -1; + } + else + { + if (padjust_info->adj_param.direction == VC921x_ADJUST_DIR_UP) + { + padjust_info->res_level = (old_res_level >= thh_res_level) ? (thh_res_level) : (old_res_level + 1); + } + else + { + padjust_info->res_level = (old_res_level <= thh_res_level) ? (thh_res_level) : (old_res_level - 1); + } + /* 20241030.电阻降到默认+4以下时,限制最大电流到约0x65,下调故当前电流置为最小 */ + if( drv_info_handle->work_mode!=WORK_MODE_FACTORY0 && drv_info_handle->work_mode!=WORK_MODE_FACTORY1 && padjust_info->res_level < VC921x_PPG_RESISTANCE+4 ) + { + padjust_info->max_current = 0x65; + padjust_info->current = (padjust_info->adj_param.direction == VC921x_ADJUST_DIR_UP)? padjust_info->max_current: padjust_info->const_min_current; + } + else if( drv_info_handle->work_mode!=WORK_MODE_FACTORY0 && drv_info_handle->work_mode!=WORK_MODE_FACTORY1 && padjust_info->res_level >= VC921x_PPG_RESISTANCE+4 ) + { + padjust_info->max_current = padjust_info->const_max_current; + padjust_info->current = (padjust_info->adj_param.direction == VC921x_ADJUST_DIR_UP)? padjust_info->max_current: padjust_info->const_min_current; + } + } + } + else + { + /* calc step */ + if (padjust_info->adj_param.direction_last == VC921x_ADJUST_DIR_NONE) + { + old_current_step *= 16; + } + else if (padjust_info->adj_param.direction_last == padjust_info->adj_param.direction) + { + old_current_step = (old_current_step == 1 || old_current_step == 2) ? ((old_current_step + 1) * 16) : (old_current_step * VC921x_ADJ_I_INCREASE); /* 同向加大步长,但是在1和2的时候,1.4倍无法使整型增长,就直接+1 */ + } + else + { + old_current_step = old_current_step * VC921x_ADJ_I_DECREASE; + } + padjust_info->adj_param.current_step = old_current_step >> 4; + padjust_info->adj_param.current_step = (padjust_info->adj_param.current_step <= VC921x_ADJ_I_STEP_MIN) ? VC921x_ADJ_I_STEP_MIN : padjust_info->adj_param.current_step; + padjust_info->adj_param.current_step = (padjust_info->adj_param.current_step >= VC921x_ADJ_I_STEP_MAX) ? VC921x_ADJ_I_STEP_MAX : padjust_info->adj_param.current_step; + /* adjust current */ + if (VC921x_ADJUST_DIR_UP == padjust_info->adj_param.direction) + { + padjust_info->current = ((old_current + padjust_info->adj_param.current_step) > thh_current) ? thh_current : (old_current + padjust_info->adj_param.current_step); + } + else if (VC921x_ADJUST_DIR_DOWN == padjust_info->adj_param.direction) + { + padjust_info->current = (old_current < padjust_info->adj_param.current_step) ? thh_current : old_current - padjust_info->adj_param.current_step; + } + padjust_info->adj_param.direction_last = padjust_info->adj_param.direction; + } + VCARE_DBG_LOG("index=%d,res_level=%d,current=%d,step=%d", slot_index, padjust_info->res_level, padjust_info->current, padjust_info->adj_param.current_step); + if (WORK_MODE_HR == drv_info_handle->work_mode) /* 暂定得是只有心率模式下支持修改过采配置 */ + { + allow_oversample = 0; + } + core921x_set_ppgslot_resistance((VC921x_PPG_SLOT_I)slot_index, padjust_info->res_level, allow_oversample ); + core921x_set_ppgslot_current((VC921x_PPG_SLOT_I)slot_index, padjust_info->current); + return 0; +} + +static int drv_adjust_slot_ppg_overflow(vc921x_sample_info_t *psample_result_info, int event) +{ + unsigned char slot_i = 0; + unsigned int over_ppgdata = 0; + vc921x_adjust_info *padjust_info = NULL; + vc921x_sample_slot_t *pslot_ret = NULL; + for (slot_i = 0; slot_i < 3; slot_i++) + { + padjust_info = adjust_info_handle[slot_i]; + pslot_ret = &psample_result_info->slot_result[slot_i]; + over_ppgdata = pslot_ret->u.pre_data; + if (1 == pslot_ret->slot_enwork) + { + VCARE_DBG_LOG("ppg_overflow(%d)=4bit[%d]", over_ppgdata, VC921x_PPG_CONV_BIT4(over_ppgdata) ); + if (VC921x_PPG_CONV_BIT4(over_ppgdata) > drv_info_handle->ppg_max_thval) + { + padjust_info->adj_param.direction = VC921x_ADJUST_DIR_DOWN; + drv_adjust_slot_current_res(padjust_info, slot_i, padjust_info->const_min_res_level, padjust_info->const_min_current); + } + else if (VC921x_PPG_CONV_BIT4(over_ppgdata) < drv_info_handle->ppg_min_thval) + { + padjust_info->adj_param.direction = VC921x_ADJUST_DIR_UP; + drv_adjust_slot_current_res(padjust_info, slot_i, padjust_info->max_res_level, padjust_info->max_current); + } + else + { + padjust_info->adj_param.direction = VC921x_ADJUST_DIR_NONE; + padjust_info->adj_param.direction_last = VC921x_ADJUST_DIR_NONE; + VCARE_DBG_LOG("slot[%d] in the range,clear slot adjustment direction", slot_i); + } + } + } + return 0; +} + +static int drv_auto_adjust_slot_config(int event, unsigned char ov_status) +{ + if( wear_info_handle->wear_status==WEAR_STA_DROP ) + { + return -1; + } + if ((event&VC921x_EVENT_INTOVLOAD)||VC921x_CFG_IRQ_BY_TIMER_EN) + { + drv_adjust_overload_max_current(sample_info_handle, ov_status, event); + } + if((event&VC921x_EVENT_INTPPG_STA)||VC921x_CFG_IRQ_BY_TIMER_EN) + { + drv_adjust_slot_ppg_overflow(sample_info_handle, event); + } + else + { + /* clear adjust_param */ + adjust_info_handle[0]->adj_param.direction = VC921x_ADJUST_DIR_NONE; + adjust_info_handle[0]->adj_param.direction_last = VC921x_ADJUST_DIR_NONE; + adjust_info_handle[1]->adj_param.direction = VC921x_ADJUST_DIR_NONE; + adjust_info_handle[1]->adj_param.direction_last = VC921x_ADJUST_DIR_NONE; + adjust_info_handle[2]->adj_param.direction = VC921x_ADJUST_DIR_NONE; + adjust_info_handle[2]->adj_param.direction_last = VC921x_ADJUST_DIR_NONE; + } + return 0; +} + +/******************************************************************************************************** + * 时钟校准部分 + *******************************************************************************************************/ +static int drv_calibration_oscclk_frequency(vc921x_clk_info *pclk_info) +{ + unsigned char count_vaild = 1; + unsigned char i = 0; + unsigned int clock_tick[2] = {0}; + unsigned short int osc_tick[2] = {0}; + unsigned short osc_frequency[2]; + + clock_tick[0] = vc921x_get_cputimer_tick(); + core921x_read_rcosc_tick(&osc_tick[0], &osc_tick[1]); + clock_tick[1] = vc921x_get_cputimer_tick(); + if ((osc_tick[0] > osc_tick[1]) || (clock_tick[0] > clock_tick[1])) + { + pclk_info->count_index = 0; + VCARE_DBG_ERR("osc/clk ticks is error\n"); + return 1; + } + pclk_info->osc_count[pclk_info->count_index * 2] = osc_tick[0]; + pclk_info->osc_count[pclk_info->count_index * 2 + 1] = osc_tick[1]; + pclk_info->clk_count[pclk_info->count_index * 2] = clock_tick[0]; + pclk_info->clk_count[pclk_info->count_index * 2 + 1] = clock_tick[1]; + pclk_info->count_index++; + if (pclk_info->count_index >= 3) + { + pclk_info->count_index = 0; + for (i = 0; i < 2; i++) + { + count_vaild = count_vaild && (VC_ABS(VC_ABS_MAX(pclk_info->clk_count[2 * i + 2], pclk_info->clk_count[2 * i], VC921x_CFG_MCU_TICK_MAX), VC_ABS_MAX(pclk_info->clk_count[2 * i + 3], pclk_info->clk_count[2 * i + 1], VC921x_CFG_MCU_TICK_MAX)) < VC921x_CFG_CLK_CALC_DEVIATION); + count_vaild = count_vaild && (VC_ABS(VC_ABS_MAX(pclk_info->osc_count[2 * i + 2], pclk_info->osc_count[2 * i], 0xffff), VC_ABS_MAX(pclk_info->osc_count[2 * i + 3], pclk_info->osc_count[2 * i + 1], 0xffff)) < VC921x_CFG_CLK_CALC_DEVIATION); + count_vaild = count_vaild && (!(pclk_info->clk_count[2 * i + 2] == pclk_info->clk_count[2 * i])); /* avoid divisor==0, diffCountIsOk set false */ + count_vaild = count_vaild && (!(pclk_info->osc_count[2 * i + 2] == pclk_info->osc_count[2 * i])); /* avoid divisor==0, diffCountIsOk set false */ + } + if (count_vaild) + { + for (i = 0; i < 2; i++) + { + osc_frequency[i] = VC_ABS_MAX((unsigned int)pclk_info->osc_count[2 * i + 2], pclk_info->osc_count[2 * i], 0xffff) * VC921x_CFG_MCU_TICK_FREQUENCY / VC_ABS_MAX((unsigned int)pclk_info->clk_count[2 * i + 2], pclk_info->clk_count[2 * i], VC921x_CFG_MCU_TICK_MAX); + VCARE_DBG_LOG("f20kOsc[20K_%d]=(%d)", i, osc_frequency[i]); + } + if (VC_ABS(osc_frequency[0], osc_frequency[1]) <= 20) + { + pclk_info->rcosc_frequency = (osc_frequency[0] + osc_frequency[1]) / 2; + pclk_info->clk_calc_freq_status = 1; + } + return 0; + } + } + return 2; +} + +static int drv_auto_calibration_oscclk(vc921x_clk_info *pclk_info) +{ + /* calibration the osc frequency by method of bisection */ + if (pclk_info->clk_calc_freq_status && pclk_info->rcosc_frequency) + { + pclk_info->clk_calc_freq_status = 0; + if (pclk_info->rcosc_frequency >= VC921x_FREQUENCY_MAX) + { + if( drv921x_nto_version ) + { + rcosc_min = (rcosc_mid <= 0xfe) ? rcosc_mid + 1 : 0xff; + } + else + { + rcosc_min = (rcosc_mid <= 0x7e) ? rcosc_mid + 1 : 0x7f; + } + } + else if (pclk_info->rcosc_frequency <= VC921x_FREQUENCY_MIN) + { + rcosc_max = (rcosc_mid >= 1) ? (rcosc_mid - 1) : 0; + } + else + { + pclk_info->clk_calc_status = 1; /* calibration finished mark */ + pclk_info->osc_gap_cnt = 0; + rcosc_max = rcosc_mid + 2; /* 修正成功后,扩张一个调整点的范围,以便温漂时调整余量 */ + rcosc_min = rcosc_mid - 2; + } + rcosc_mid = (rcosc_max + rcosc_min) / 2; + VCARE_DBG_LOG("min,max[%d,%d]~min(%d)", rcosc_min, rcosc_max, rcosc_mid); + core921x_set_rcosc_frequency(rcosc_mid); + pclk_info->rcosc_calibration = rcosc_mid; + /* 如果发生温漂,修改收缩的上下限,进行缓慢修正时钟 */ + if ((pclk_info->rcosc_frequency <= VC921x_FREQUENCY_MIN) && VC_ABS(rcosc_max, rcosc_min) <= 2) + { + rcosc_min -= 2; + VCARE_DBG_LOG("!!!temp->min,max[%d,%d]~min(%d)", rcosc_min, rcosc_max, rcosc_mid); + } + if ((pclk_info->rcosc_frequency >= VC921x_FREQUENCY_MAX) && VC_ABS(rcosc_max, rcosc_min) <= 2) + { + rcosc_max += 2; + VCARE_DBG_LOG("!!!temp->min,max[%d,%d]~min(%d)", rcosc_min, rcosc_max, rcosc_mid); + } + } + return 0; +} + +static int drv_calibration_oscclk_config(vcare_ppg_device_t *pdev) +{ + vc921x_clk_info *pclk_info = clk_info_handle; +#if 1 + if (pclk_info->clk_calc_status) +#else + if (pclk_info->clk_calc_status && pclk_info->osc_gap_cnt <= VC921x_CFG_CLK_CALC_GAPTIME) +#endif + { + pclk_info->osc_gap_cnt++; + return 0; + } + /* calibration clk running, enter fast irq config */ + drv_calibration_entry_fast_irq( ); + drv_calibration_oscclk_frequency(pclk_info); + drv_auto_calibration_oscclk(pclk_info); + if( pclk_info->clk_calc_status ) /* calibration clk finished, exit fast irq config */ + { + startup_inntemperature = sample_info_handle->extra_result.temp0_data; + drv_calibration_exit_fast_irq( ); + } + return 1; +} +static void drv_calibration_clk_clear(void) +{ + static unsigned char is_first_run = 1; + vc921x_clk_info *pclk_info = clk_info_handle; + pclk_info->last_check_ticks = 0; + pclk_info->osc_gap_cnt = 0; + pclk_info->count_index = 0; + pclk_info->clk_calc_freq_status = 0; + /* 校验频率温度清空 */ + check_cpu_tick[0]=0,check_cpu_tick[1]=0; + check_dev_tick[0]=0,check_dev_tick[1]=0; + if (is_first_run == 1) + { + is_first_run = 0; + if( drv921x_nto_version ) + { + rcosc_max = 255,rcosc_min = 0,rcosc_mid = 127; + } + else + { + rcosc_max = 127,rcosc_min = 0,rcosc_mid = 63; + } + pclk_info->clk_calc_status = 0; + core921x_set_rcosc_frequency(rcosc_mid); + } + else + { + if( pclk_info->rcosc_calibration==0 ) + { + if( drv921x_nto_version ) + { + rcosc_max = 255,rcosc_min = 0,rcosc_mid = 127; + } + else + { + rcosc_max = 127,rcosc_min = 0,rcosc_mid = 63; + } + pclk_info->clk_calc_status = 0; + pclk_info->rcosc_calibration=rcosc_mid; + } + core921x_set_rcosc_frequency(pclk_info->rcosc_calibration); + } +} + +static int drv_check_running_gaptime(void) +{ +#if VC921x_CFG_CLK_CLAC_IRQGAP_EN + int ret = 0; + unsigned short int curr_ticks = 0; + unsigned short int ticks_chk, ticks_gap = 0; + unsigned short int ticks1 = 0, ticks2 = 0; + unsigned char times_gap = 0; + core921x_read_rcosc_tick(&ticks1, &ticks2); + times_gap = (1000 * drv_info_handle->ppg_div / VC921x_DEFAULT_FREQ) * VC921x_IRQ_GAP_RATIO; + ticks_chk = (ticks2 >= ticks1) ? (ticks2 - ticks1) : (0xffff + ticks2 - ticks1); + if (ticks_chk <= 10) + { + curr_ticks = ticks1; + } + else + { /* IC问题,可能出现其中一个tick低位出现异常,如果出现异常则取另外一共tick作为运算准 */ + curr_ticks = (ticks2&0xff)?(ticks2):(ticks1); + VCARE_DBG_ERR("read check tick failed(%d,%d)", ticks1, ticks2); + } + // ticks_gap = 2000; + if (0 != curr_ticks && 0 != clk_info_handle->last_check_ticks) + { + ticks_gap = (curr_ticks >= clk_info_handle->last_check_ticks) ? (curr_ticks - clk_info_handle->last_check_ticks) : (0xffff + curr_ticks - clk_info_handle->last_check_ticks); + } + if (ticks_gap <= 20 * times_gap) /* 20k,1ms~20ticks */ + { + VCARE_DBG_ERR("last(%d)now(%d)gap[%d<%d]", clk_info_handle->last_check_ticks, curr_ticks, ticks_gap, 20 * times_gap); + ret = -1; + } + clk_info_handle->last_check_ticks = curr_ticks; + return ret; +#else + return 0; +#endif +} + +static void drv_fifo_error_softreset( void ) +{ + static unsigned char last_add_mark=0; + unsigned char save_config[32]={0}; + /* 软复位,清空所有数据,避免传入算法或者引起其他异常 */ + drv_sample_result_memroy_finished( sample_info_handle ); + vc921x_read_register( 0x40, save_config, sizeof(save_config) ); + core921x_write_work_cmd( VC921x_STOP_WORK_CMD ); + core921x_write_work_cmd( VC921x_RESET_SOFT_CMD ); + drv_info_handle->read_fifo_address = 0; + vc921x_write_register( 0x40, save_config, sizeof(save_config) ); + core921x_write_work_cmd( VC921x_START_WORK_CMD ); + if( last_add_mark==0 ) + { + last_add_mark=1; + core921x_set_rcosc_frequency( clk_info_handle->rcosc_calibration-1 ); + } + else + { + last_add_mark=0; + core921x_set_rcosc_frequency( clk_info_handle->rcosc_calibration+1 ); + } + #if 1 + unsigned char config_debug_reg[32] = {0}; + unsigned char debug_index = 0; + vc921x_read_register(0x40, config_debug_reg, 32); + VCARE_DBG_LOG("[0x40]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); + debug_index += 8; + VCARE_DBG_LOG("[0x48]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); + debug_index += 8; + VCARE_DBG_LOG("[0x50]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); + debug_index += 8; + VCARE_DBG_LOG("[0x58]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); + #endif +} +/******************************************************************************************************** + *数据读取部分 + ********************************************************************************************************/ +static int drv_read_fifo_data(vc921x_sample_info_t *psample_result) +{ + int read_fifo_ret = 0; + vc921x_sample_info_t *sample_result_ptr = psample_result; + unsigned char bio_fifo_error_cnt=0; + unsigned char bio_fifo_error_flg=0; + unsigned char ps_fifo_error_flg=0; + if (!sample_result_ptr) + { + return -1; + } + /* fifo中断 */ + if (0 != drv_info_handle->fifo_read_finished_sync) + { + return 0; + } + drv_info_handle->fifo_read_finished_sync = 1; + read_fifo_ret = core921x_read_fifo_data(sample_result_ptr, &drv_info_handle->read_fifo_address, drv_info_handle->write_fifo_address_bak); + VCARE_DBG_LOG("read_fifo updata read address: %d, write_address:%d, write_address_rel:%d",drv_info_handle->read_fifo_address, drv_info_handle->write_fifo_address_bak, drv_info_handle->write_fifo_address_rel ); + if (read_fifo_ret>=0) + { + drv_info_handle->fifo_read_data_valid = 1; + /* nto版本直接更新read_addr */ + if( drv921x_nto_version ) + { + if( read_fifo_ret==1 ) + { + drv_info_handle->read_fifo_address = drv_info_handle->write_fifo_address_bak; + /* fifo错误,清空fifo数据 */ + VCARE_DBG_LOG("===PS_FIFO_ERROR===") ; + drv_sample_result_memroy_finished( sample_info_handle ); + return read_fifo_ret; + } + } + else + { + if( read_fifo_ret==1 && (drv_info_handle->int_event&VC921x_EN_EVT_FIFO) && !(drv_info_handle->int_event&VC921x_EVENT_INSAMPLE) ) /* 当出现了fifo中断,且并不是fifo数量的倍数时,可以认定为错位,进行纠错处理 */ + { + drv_info_handle->read_fifo_address = drv_info_handle->write_fifo_address_bak; + drv_info_handle->fifo_read_data_valid = 2; + VCARE_DBG_LOG("delay response read_addr updata:%d=write_addr:%d", drv_info_handle->read_fifo_address, drv_info_handle->write_fifo_address_bak); + } + /* 20240119.校验RO_PS与fifo_PS是否异常(如果异常则可以判断为fifo异常错位:1.过度延后导致的fifo覆盖反转,2.ICfifo数据异常丢失) + * 20240229.添加bio的全部出现0xff的情况,判断为fifo异常错位 + */ + if( sample_result_ptr->extra_result.bioext_enwork && sample_result_ptr->extra_result.bio_fifo_num!=0 ) + { + for( bio_fifo_error_cnt=0; bio_fifo_error_cntextra_result.bio_fifo_num; bio_fifo_error_cnt++ ) + { + VCARE_DBG_LOG("bio=%x ~ 0xff",sample_result_ptr->extra_result.bioext_buffer[bio_fifo_error_cnt] ); + if( (sample_result_ptr->extra_result.bioext_buffer[bio_fifo_error_cnt]&0xff) != 0xff ) + { + bio_fifo_error_cnt=0; + break; + } + } + VCARE_DBG_LOG("bio_num=%d,%d",sample_result_ptr->extra_result.bio_fifo_num, bio_fifo_error_cnt); + if( sample_result_ptr->extra_result.bio_fifo_num==bio_fifo_error_cnt ) + { + bio_fifo_error_flg=1; + } + } + if( sample_result_ptr->slot_result[2].slot_enwork==2 && sample_result_ptr->slot_result[2].ppg_num!=0 ) + { + VCARE_DBG_LOG("last_ps=%x ~ %x",VC921x_GET_SLOT_PS_DATA(sample_result_ptr, sample_result_ptr->slot_result[2].ppg_num-1), sample_result_ptr->slot_result[2].u.ps_data ); + if( VC921x_GET_SLOT_PS_DATA(sample_result_ptr, sample_result_ptr->slot_result[2].ppg_num-1)>>8 != sample_result_ptr->slot_result[2].u.ps_data ) + { + ps_fifo_error_flg=1; + } + } + /* bio_fifo_error || ps_fifo_error */ + if( bio_fifo_error_flg || ps_fifo_error_flg ) + { + VCARE_DBG_LOG("===PS_FIFO_ERROR===") ; + drv_fifo_error_softreset(); + return read_fifo_ret; + } + } + /* 20241018.在insample的时候,如果出现中断延时处理/定时器,获取到的RO_bio的值可能还未完全采样计算完成,需要从fifo中更新数据 */ + if( (VC921x_CFG_IRQ_BY_TIMER_EN||drv_info_handle->int_event&VC921x_EVENT_INSAMPLE||0==drv_info_handle->int_event) && (VC921x_BIO_NOFIFO_PS_SGL!=sample_result_ptr->extra_result.biofifo_mode) ) + { + if( sample_result_ptr->extra_result.bioext_enwork && + sample_result_ptr->extra_result.bioext_data != sample_result_ptr->extra_result.bioext_buffer[sample_result_ptr->extra_result.bio_fifo_num-1] ) + { + sample_result_ptr->extra_result.bioext_data = sample_result_ptr->extra_result.bioext_buffer[sample_result_ptr->extra_result.bio_fifo_num-1]; + VCARE_DBG_ERR("INSAMPLE_ERR_EXTBIO-modify=%d",sample_result_ptr->extra_result.bioext_data); + } + if( sample_result_ptr->extra_result.bioinn_enwork && + sample_result_ptr->extra_result.bioinn_data != sample_result_ptr->extra_result.bioinn_buffer[sample_result_ptr->extra_result.bio_fifo_num-1]) + { + sample_result_ptr->extra_result.bioinn_data = sample_result_ptr->extra_result.bioinn_buffer[sample_result_ptr->extra_result.bio_fifo_num-1]; + VCARE_DBG_ERR("INSAMPLE_ERR_INNBIO-modify=%d",sample_result_ptr->extra_result.bioinn_data); + } + } + } + else + { + VCARE_DBG_ERR("read_fifo_ret error(%d)", read_fifo_ret); + return read_fifo_ret; + } + return 0; +} + +static int drv_read_sample_param(vc921x_sample_info_t *psample_ret) +{ + unsigned char slot_i = 0; + unsigned char slot_current = 0, slot_res_lvl = 0; + if (0 == drv_info_handle->param_read_finished_sync) + { + core921x_read_output(psample_ret); + VCARE_DBG_LOG("env(0x)=%x-%x-%x(%d,%d,%d)", psample_ret->slot_result[0].env_data, + psample_ret->slot_result[1].env_data, + psample_ret->slot_result[2].env_data, + VC921x_ENV_CONVERT(psample_ret->slot_result[0].env_data), + VC921x_ENV_CONVERT(psample_ret->slot_result[1].env_data), + VC921x_ENV_CONVERT(psample_ret->slot_result[2].env_data)); + VCARE_DBG_LOG("ppg/ps_data=%d-%d-%d", psample_ret->slot_result[0].u.ps_data, psample_ret->slot_result[1].u.ps_data, psample_ret->slot_result[2].u.ps_data); + VCARE_DBG_LOG("bio01_data=%d-%d", psample_ret->extra_result.bioinn_data, psample_ret->extra_result.bioext_data); + VCARE_DBG_LOG("temp01_data=%d-%d-ref(%d)", psample_ret->extra_result.temp0_data, psample_ret->extra_result.temp1_data, psample_ret->extra_result.tempref_data); + + psample_ret->slot_result[0].env_data = VC921x_ENV_CONVERT(psample_ret->slot_result[0].env_data); + psample_ret->slot_result[1].env_data = VC921x_ENV_CONVERT(psample_ret->slot_result[1].env_data); + psample_ret->slot_result[2].env_data = VC921x_ENV_CONVERT(psample_ret->slot_result[2].env_data); + for (slot_i = 0; slot_i < 3; slot_i++) + { + core921x_read_slot_current_res((VC921x_PPG_SLOT_I)slot_i, &slot_current, &slot_res_lvl); + psample_ret->slot_result[slot_i].slot_current = slot_current; + psample_ret->slot_result[slot_i].slot_resistance = slot_res_lvl; + adjust_info_handle[slot_i]->current = slot_current; + adjust_info_handle[slot_i]->res_level = slot_res_lvl; + } + drv_info_handle->param_read_finished_sync = 1; + VCARE_DBG_LOG("slot[0]cur%d,res%d ^ slot[1]cur%d,res%d ^ slot[2]cur%d,res%d", + psample_ret->slot_result[0].slot_current, psample_ret->slot_result[0].slot_resistance, + psample_ret->slot_result[1].slot_current, psample_ret->slot_result[1].slot_resistance, + psample_ret->slot_result[2].slot_current, psample_ret->slot_result[2].slot_resistance); + } + return 0; +} + +static int drv_read_event_status(vc921x_drv_info *pdrv_info) +{ + if (0 == pdrv_info->event_read_finished_sync) + { + core921x_read_event_status(&pdrv_info->int_event, &pdrv_info->ov_status, &pdrv_info->write_fifo_address_bak, &pdrv_info->write_fifo_address_rel); + pdrv_info->event_read_finished_sync = 1; + VCARE_DBG_LOG("read_event=[%x], read_status=[%x], read_Windex=[%x]~rel[%x]", pdrv_info->int_event, pdrv_info->ov_status, pdrv_info->write_fifo_address_bak, pdrv_info->write_fifo_address_rel ); + } + return 0; +} + +/******************************************************************************************************** + * 中断事件处理部分调用逻辑 + ********************************************************************************************************/ +static int drv_ppg_mode_handler(const void *ops_self, vcare_ppg_device_t *pdev) +{ + int ret = 0; + vc921x_sample_info_t *sample_result_ptr = pdev->result; + vc921x_mode_ops *self_ptr = (vc921x_mode_ops *)ops_self; + drv_calibration_oscclk_config( pdev ); + if( 1 == clk_info_handle->clk_calc_status ) + { + ret = drv_auto_adjust_slot_config(drv_info_handle->int_event, drv_info_handle->ov_status); + } + if (ret >= 0 && 1 == clk_info_handle->clk_calc_status) + { + drv_read_fifo_data(sample_result_ptr); + } + else + { + VCARE_DBG_ERR("WORK_MODE_[%d] clk_status[%d]", self_ptr->mode, clk_info_handle->clk_calc_status); + } + return ret; +} +static int drv_factory_mode_handler(const void *ops_self, vcare_ppg_device_t *pdev) +{ + int ret = 0; + unsigned short int base =0; + vc921x_sample_info_t *sample_result_ptr = pdev->result; + drv_calibration_oscclk_config( pdev ); + if (drv_info_handle->int_event & VC921x_EVENT_INTOVLOAD) + { + ret = drv_adjust_overload_max_current(sample_result_ptr, drv_info_handle->ov_status, drv_info_handle->int_event); + } + drv_read_fifo_data(sample_result_ptr); + if( 1==clk_info_handle->clk_calc_status ) + { + drv_calc_bioinn_auto_addcap(drv_info_handle->int_event, sample_result_ptr); + } + base = sample_result_ptr->extra_result.bioinn_data - drv_info_handle->optimal_bio_gap; + sample_result_ptr->extra_result.bioabs_calc = VC_ABS( sample_result_ptr->extra_result.bioext_data, base ); + return ret; +} + +/********************************************************************************************************* + * DRV_API 对外功能模块部分 + ********************************************************************************************************/ +int vc921x_drv_init_start_work(void *pdevice, int workmode, unsigned short int ppgdiv, unsigned char psdiv, unsigned char fifodiv, unsigned char dropdiv) +{ + vcare_ppg_device_t *pdev = (vcare_ppg_device_t *)pdevice; + char *drv_ver = NULL; + unsigned char mode_count = 0; + unsigned char mode_index = 0; + vcare_memset(sample_info_handle, 0, sizeof(vc921x_sample_info_t)); + pdev->result = sample_info_handle; /* 采样指针传递 */ + core921x_read_software_version(&drv_ver); + core921x_read_id(&pdev->id1, &pdev->id2); /* 0x24, 0x10 */ + VCARE_DBG_LOG("[%s]devID0-2=0x%02x 0x%02x", drv_ver, pdev->id1, pdev->id2); + mode_count = sizeof(mode_ops) / sizeof(mode_ops[0]); + sel_mode_ops = NULL; + for (mode_index = 0; mode_index < mode_count; mode_index++) + { + if (workmode == mode_ops[mode_index].mode) + { + sel_mode_ops = &mode_ops[mode_index]; + break; + } + } + if (!sel_mode_ops) + { + VCARE_DBG_ERR("work_mode is error(NULL),please code mode"); + return -1; + } + drv_work_init(pdev, workmode, ppgdiv, psdiv, fifodiv, dropdiv, pdev->heart_algo_mode); + return 0; +} +int vc921x_drv_get_hwsw_infomation(char **str_drv_ver, unsigned char *pid1, unsigned char *pid2) +{ + core921x_read_software_version(str_drv_ver); + core921x_read_id(pid1, pid2); + return 0; +} +int vc921x_drv_exit_stop_work(void *pdevice) +{ + wear_info_handle->last_status = WEAR_STA_NONE; + core921x_write_work_cmd(VC921x_RESET_SOFT_CMD); + core921x_write_work_cmd(VC921x_STOP_WORK_CMD); + return 0; +} +int vc921x_drv_software_reset(void *pdevice) +{ + wear_info_handle->last_status = WEAR_STA_NONE; + return core921x_write_work_cmd(VC921x_RESET_SOFT_CMD); +} +int vc921x_drv_wakeup_work(void *pdevice) +{ + return core921x_write_work_cmd(VC921x_START_WORK_CMD); +} +int vc921x_drv_sleep_work(void *pdevice) +{ + wear_info_handle->last_status = WEAR_STA_NONE; + return core921x_write_work_cmd(VC921x_STOP_WORK_CMD); +} +int vc921x_drv_set_algo_wear_status(void *pdevice, int status) +{ + wear_info_handle->algo_wear_status = (status == -1) ? (WEAR_STA_DROP) : (WEAR_STA_HOLD); + return 0; +} +int vc921x_drv_set_acce_wear_status(void *pdevice, int status) +{ + /* 产生运动量,清除心率摘下标志 */ + if (0 == status) + { + wear_info_handle->acce_wear_status = WEAR_STA_DROP; + } + else + { + wear_info_handle->acce_wear_status = WEAR_STA_HOLD; + wear_info_handle->algo_wear_status = WEAR_STA_HOLD; + } + return 0; +} +wear_status vc921x_drv_get_wear_status(void *pdevice) +{ + vcare_ppg_device_t *pdev = (vcare_ppg_device_t *)pdevice; + if (!sel_mode_ops) + { + return WEAR_STA_HOLD; + } + if (pdev->workmode == WORK_MODE_FACTORY0 || pdev->workmode == WORK_MODE_FACTORY1) + { + return WEAR_STA_HOLD; + } + return drv_get_wear_status(pdevice); +} + +void vc921x_drv_get_wear_detail_result( unsigned char *bio_ret, unsigned char *algo_ret, unsigned char *acce_ret ) +{ + *bio_ret = (VC921x_BIO_STA_HOLD==wear_info_handle->bio_wear_result)?1:0; + *algo_ret = (WEAR_STA_HOLD==wear_info_handle->algo_wear_status)?1:0; + *acce_ret = (WEAR_STA_HOLD==wear_info_handle->acce_wear_status)?1:0; +} + +/* irq/timer callback interface. */ +int vc921x_drv_get_result_handler(void *pdevice) +{ + vcare_ppg_device_t *pdev = (vcare_ppg_device_t *)pdevice; + pdev->result = sample_info_handle; + vc921x_sample_info_t *sample_result_ptr = pdev->result; + if (!sample_result_ptr || !sel_mode_ops ) + { + return -2; + } + /* 读取基础采样数据 */ + drv_read_event_status(drv_info_handle); + if( drv_info_handle->int_event==0 ) + { + return -1; + } + if (drv_info_handle->int_event & VC921x_EVENT_CONFLICT) + { + VCARE_DBG_ERR("work_status=%x", drv_info_handle->int_event); + return -1; + } + /* check gap time */ + if (0 != drv_check_running_gaptime()) + { + VCARE_DBG_ERR("running gaptime"); + return -1; + } + drv_read_sample_param(sample_result_ptr); + /* 模式数据处理 */ + if (sel_mode_ops->mode_handler) + { + sel_mode_ops->mode_handler(sel_mode_ops, pdev); + } + return 0; +} + +void vc921x_drv_read_event_infomation_byIRQ(void) +{ + drv_read_event_status(drv_info_handle); +} + +int vc921x_drv_is_fifo_event(void *pdevice) +{ + if( drv_info_handle->fifo_read_data_valid ) + { + return 1; + } + return 0; +} +int vc921x_drv_is_ps_event(void *pdevice) +{ + if (drv_info_handle->int_event & (VC921x_EVENT_INTPS_RDY)) + { + return 1; + } + return 0; +} +int vc921x_drv_is_ppg_event(void *pdevice) +{ + if (drv_info_handle->int_event & (VC921x_EVENT_INTPPG_STA)) + { + return 1; + } + return 0; +} +int vc921x_drv_clear_reset_bio_param(void *pdevice) +{ + drv_infomation_param_reset(); + core921x_set_bioinn_addcap( sample_info_handle, set_cap_mid ); + return 0; +} + +unsigned char vc921x_drv_check_frequency_abnormal( unsigned short dev_tick1, unsigned short dev_tick2, unsigned int cpu_tick1, unsigned int cpu_tick2 ) +{ + static unsigned short calc_freq[2]={0}; + unsigned int cpu_tick_diff=0,dev_tick_diff=0; + float gap_time_diff=0.0f; + if( !clk_info_handle->clk_calc_status ) return 0; + check_cpu_tick[1] = check_cpu_tick[0]; + check_dev_tick[1] = check_dev_tick[0]; + check_cpu_tick[0] = cpu_tick1; + check_dev_tick[0] = dev_tick1; + if( VC_ABS_MAX(dev_tick2,dev_tick1,0xffff)>=20 || VC_ABS_MAX(cpu_tick2, cpu_tick1, VC921x_CFG_MCU_TICK_MAX)>=32 ) + { + check_cpu_tick[0]=0,check_cpu_tick[1]=0; + check_dev_tick[0]=0,check_dev_tick[1]=0; + return 0; + } + if (0 == check_cpu_tick[1]||0 == check_cpu_tick[0]) + { + return 0; + } + if (0 == check_dev_tick[1]||0==check_dev_tick[0]) + { + return 0; + } + cpu_tick_diff = VC_ABS_MAX( check_cpu_tick[0], check_cpu_tick[1], VC921x_CFG_MCU_TICK_MAX ); + dev_tick_diff = VC_ABS_MAX( check_dev_tick[0], check_dev_tick[1], 0xFFFF ); + gap_time_diff = (float)cpu_tick_diff/(float)VC921x_CFG_MCU_TICK_FREQUENCY; + calc_freq[1] = calc_freq[0]; + calc_freq[0] = (float)dev_tick_diff/gap_time_diff; + if( calc_freq[0]&& calc_freq[1] && VC_ABS( calc_freq[0], calc_freq[1] )<=100 ) + { + VCARE_DBG_LOG("check temp freq=[%d][%d](%d,%d)", calc_freq[0],calc_freq[1], cpu_tick_diff, dev_tick_diff ); + if( 20000>=calc_freq[0] && (20000-calc_freq[0])/40 >= 60 ) /* 相较于上电时的频率温升60即返回出错 */ + { + check_cpu_tick[0]=0,check_cpu_tick[1]=0; + check_dev_tick[0]=0,check_dev_tick[1]=0; + return 1; + } + } + return 0; +} +#define CHECK_BUFF_SIZE 20 +int vc921x_drv_read_check_temperature_abnormal( void *pdevice, unsigned char *p_freq_ret, unsigned char *p_temp_ret, unsigned short *pdev_cnt1, unsigned short *pdev_cnt2 ) +{ + static unsigned char fiil_mark=0; + static unsigned char fill_index=0; + static unsigned short check_innbio_buff[CHECK_BUFF_SIZE]={0}; + static unsigned short check_extbio_buff[CHECK_BUFF_SIZE]={0}; + static unsigned short check_tick1_buff[CHECK_BUFF_SIZE]={0}; + static unsigned short check_tick2_buff[CHECK_BUFF_SIZE]={0}; + unsigned int check_clk_tick[2]={0}; + unsigned char i=0; + int check_ret=0; + *p_freq_ret = 0; + *p_temp_ret = 0; + + drv_read_sample_param( sample_info_handle ); + drv_info_handle->param_read_finished_sync = 0; /* 读取完成之后不置同步标志,该数据仅做异常检测 */ + /****** 进入临界区 ******/ + check_clk_tick[0] = vc921x_get_cputimer_tick(); + core921x_read_rcosc_tick( pdev_cnt1, pdev_cnt2 ); + check_clk_tick[1] = vc921x_get_cputimer_tick(); + /****** 退出临界区 ******/ + check_innbio_buff[fill_index]=sample_info_handle->extra_result.bioinn_data; + check_extbio_buff[fill_index]=sample_info_handle->extra_result.bioext_data; + check_tick1_buff[fill_index]=*pdev_cnt1; + check_tick2_buff[fill_index]=*pdev_cnt2; + /* 检验频率温度是否正常 */ + *p_freq_ret = vc921x_drv_check_frequency_abnormal( *pdev_cnt1, *pdev_cnt2, check_clk_tick[0], check_clk_tick[1] ); + /* 检验内部温度是否正常 */ +#if 0 /* 使用上电时的内部温度和当前的相对温度进行比较 或者是 内部一致就使用绝对比较 */ + if( sample_info_handle->extra_result.temp0_data>0 && startup_inntemperature>sample_info_handle->extra_result.temp0_data && (startup_inntemperature-sample_info_handle->extra_result.temp0_data)>=1000 ) +#else + if( sample_info_handle->extra_result.temp0_data>0 && sample_info_handle->extra_result.temp0_data<=18000 ) /* 如果客户那边内部得温度常温都在19000左右,就可以直接判断绝对值 */ +#endif + { + *p_temp_ret = 1; + } + /* 校验bio/rctick是否卡死且不为0 */ + if( ++fill_index >=CHECK_BUFF_SIZE ) + { + fill_index=0; + fiil_mark=1; + } + if( fiil_mark ) + { + for( i=0; iextra_result.bioext_enwork||0==sample_info_handle->extra_result.bioinn_enwork ) + { + check_ret++;break; /* bio未使能,忽略++ */ + } + if( check_innbio_buff[i]!=check_innbio_buff[i+1]||check_extbio_buff[i]!=check_extbio_buff[i+1] ) + { + check_ret++;break; /* bio未卡死,通过++ */ + } + } + for( i=0; iparam_read_finished_sync = 0; + drv_info_handle->fifo_read_finished_sync = 0; + drv_info_handle->event_read_finished_sync = 0; + drv_info_handle->fifo_read_data_valid = 0; + +#if 1 + unsigned char config_debug_reg[32] = {0}; + unsigned char debug_index = 0; + vc921x_read_register(0x40, config_debug_reg, 32); + VCARE_DBG_LOG("[0x40]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); + debug_index += 8; + VCARE_DBG_LOG("[0x48]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); + debug_index += 8; + VCARE_DBG_LOG("[0x50]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); + debug_index += 8; + VCARE_DBG_LOG("[0x58]0x%02x,0x%02x,0x%02x,0x%02x 0x%02x,0x%02x,0x%02x,0x%02x", config_debug_reg[0 + debug_index], config_debug_reg[1 + debug_index], config_debug_reg[2 + debug_index], config_debug_reg[3 + debug_index], + config_debug_reg[4 + debug_index], config_debug_reg[5 + debug_index], config_debug_reg[6 + debug_index], config_debug_reg[7 + debug_index]); +#endif + return 0; +} diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_hal.c b/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_hal.c new file mode 100644 index 000000000..477d9eee4 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_hal.c @@ -0,0 +1,75 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-03-13 15:08:38 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-16 16:40:25 + * @FilePath: /FD230/code/sdk/drivers/peripheral/ppg/hx3695/hx3695_hal.c + * @Description: + * + * Copyright (c) 2024 by ${git_name_email}, All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include "pm_control.h" +#include "vc921x_user_app.h" + +static vcare_ppg_device_t *vc921x_device = NULL; + +void ppg_send_cmd_to_vc921x_driver(char* mode, uint8_t enable) +{ + work_mode md = 1; + if (strcmp(mode, "hrs") == 0) { + md = WORK_MODE_HR; + } else if (strcmp(mode, "hrv") == 0) { + md = WORK_MODE_HRV; + } else if (strcmp(mode, "spo2") == 0) { + md = WORK_MODE_SPO2; + } else if (strcmp(mode, "leaklight") == 0) { + md = WORK_MODE_UNDEFINED; + } else if (strcmp(mode, "graycard") == 0) { + md = WORK_MODE_UNDEFINED; + } else if (strcmp(mode, "ledoff") == 0) { + md = WORK_MODE_UNDEFINED; + } else if (strcmp(mode, "wear") == 0) { + md = WORK_MODE_WEAR; + } else if (strcmp(mode, "single") == 0) { + md = WORK_MODE_UNDEFINED; + }else if (strcmp(mode, "factory") == 0) { + md = WORK_MODE_UNDEFINED; + }else if (strcmp(mode, "fpc") == 0) { + md = WORK_MODE_UNDEFINED; + }else if (strcmp(mode, "int") == 0) { + if(vc921x_device) + vc921x_device->ops->dev_irq_handler(0,0); + return; + } + + if (enable == 1) + { + // ppg_service_running_stat = true; + uapi_pm_control(PM_APP_WAKE_NEEDED); + tyhx_poweron(); + + vc921x_device = get_vc921x_device(); + if(!vc921x_device) + { + printf("get_vc921x_device failed !!!\n"); + tyhx_poweroff(); + return; + } + InitParamTypeDef pInit_Param = {0}; + pInit_Param.sampling_gap_time = 400; + pInit_Param.work_mode = md; + vc921x_device->ops->dev_init(&pInit_Param); + + } else { + + uapi_pm_control(PM_APP_WAKE_NONEED); + vc921x_device->ops->stop_work(); + tyhx_poweroff(); + } +} \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_original_data_send.c b/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_original_data_send.c new file mode 100644 index 000000000..30e608675 --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_original_data_send.c @@ -0,0 +1,267 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-09-22 17:39:10 + * @LastEditTime: 2024-02-26 09:49:05 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ +/* system include */ +// #include "os_timer.h" +// #include +// #include "wristband_global_data.h" +// extern RtkWristBandSysType_t RtkWristbandSys; +/* vcare include */ +#include "vcare_device_com.h" +#include "vc921x_sample_result.h" + +static unsigned char ble_buf[2000] = {0}; +static unsigned short int buf_size = 0; +static unsigned short int buf_index = 0; +#define BLE_PACK_SIZE 200 +#define TIMER_BLE_GAP 10 + +static void debug_orginal_data_send(unsigned short int size); +/********************************************************************************************************* + * 调试蓝牙数据格式: + * [0~7]slot0_8bytes + * { + * .[0~2]ppg_data(h,m,l) + * .[3]ppg_mark + * .[4]resistance + * .[5]current + * .[6~7]env_bit14(h,l) + * } + * [ 8~15]slot1_8bytes{...} + * [16~23]slot2_8bytes{...} + * [24~25]bio0_2bytes(h,l) + * [26~27]bio1_2bytes(h,l) + * [28~29]ps_slot3_2bytes(h,l) + * [30~35]gsensorx/y/z(h,l) or tempRefADC, tempExtADC, temperature + * [36]hr + * [37]spo2 + * [38]algo_mode + * [39]wear_bioret(bit7) | algoret(bit6) | acceret(bit5) | wear_drv(bit3~bit0) + * + * #define FRAME_SIZE 40 + *********************************************************************************************************/ +#define ORIGINAL_DATA_DEBUG_ENABLE (1) +#if ORIGINAL_DATA_DEBUG_ENABLE + +#define HR_PPG_SLOT 0 +#define IR_PPG_SLOT 1 +#define RD_PPG_SLOT 2 +#define PS_PPG_SLOT 3 +/* 单采样帧字节数 */ +#define FRAME_SIZE 40 + +float vc921x_calc_sample_data_coefficient( unsigned int ppg_data, unsigned char ppg_I, unsigned char ppg_R, unsigned char ppg_bits ); +extern void vc921x_drv_get_wear_detail_result( unsigned char *bio_ret, unsigned char *algo_ret, unsigned char *acce_ret ); + +void vc921x_send_orginal_data(vcare_ppg_device_t *pdev, gsensor_axes *pgsensor, unsigned char ppgsize) +{ + unsigned char i = 0; + unsigned char j = 0; + unsigned char slot_k = 0; + unsigned char psfreq_index = 0; + /* wear all result param */ + unsigned char bio_result=0, algo_result=0, acce_result=0; + vc921x_sample_info_t *presult = (vc921x_sample_info_t *)pdev->result; + memset(ble_buf, 0, sizeof(ble_buf)); + vc921x_drv_get_wear_detail_result( &bio_result, &algo_result, &acce_result ); + unsigned char calc_slot=0; + vc921x_calc_sample_data_coefficient( VC921x_GET_SLOT_SAMPLE_DATA(presult, calc_slot, ppgsize-1), presult->slot_result[calc_slot].slot_current, presult->slot_result[calc_slot].slot_resistance, + presult->adc_resolution ); + + for (i = 0; i < ppgsize; i++) + { + j = 0; + /* 0-23,slot_ppg:3*8=24 */ + for (slot_k = 0; slot_k < 3; slot_k++) + { + ble_buf[i * FRAME_SIZE + j++] = (VC921x_GET_SLOT_SAMPLE_DATA(presult, slot_k, i)>>16)&0xff; + ble_buf[i * FRAME_SIZE + j++] = (VC921x_GET_SLOT_SAMPLE_DATA(presult, slot_k, i)>>8) &0xff; + ble_buf[i * FRAME_SIZE + j++] = (VC921x_GET_SLOT_SAMPLE_DATA(presult, slot_k, i)>>0) &0xff; + + ble_buf[i * FRAME_SIZE + j++] = (presult->adc_resolution-16)/2; + ble_buf[i * FRAME_SIZE + j++] = presult->slot_result[slot_k].slot_resistance; + ble_buf[i * FRAME_SIZE + j++] = presult->slot_result[slot_k].slot_current; + ble_buf[i * FRAME_SIZE + j++] = (presult->slot_result[slot_k].env_data >> 8) & 0xff; + ble_buf[i * FRAME_SIZE + j++] = (presult->slot_result[slot_k].env_data >> 0) & 0xff; + } + /*24-29 bio:2*2 + ps:2 */ + psfreq_index =(presult->wear_drop_flag)?(i/presult->hold_div):(i / presult->ps_div); + if (presult->extra_result.biofifo_mode != 0) //VC921x_BIO_NOFIFO_PS_SGL + { + ble_buf[i * FRAME_SIZE + j++] = (presult->extra_result.bioinn_buffer[psfreq_index] >> 8) & 0xff; + ble_buf[i * FRAME_SIZE + j++] = (presult->extra_result.bioinn_buffer[psfreq_index] >> 0) & 0xff; + ble_buf[i * FRAME_SIZE + j++] = (presult->extra_result.bioext_buffer[psfreq_index] >> 8) & 0xff; + ble_buf[i * FRAME_SIZE + j++] = (presult->extra_result.bioext_buffer[psfreq_index] >> 0) & 0xff; + } + else + { + ble_buf[i * FRAME_SIZE + j++] = (presult->extra_result.bioinn_data >> 8) & 0xff; + ble_buf[i * FRAME_SIZE + j++] = (presult->extra_result.bioinn_data >> 0) & 0xff; + ble_buf[i * FRAME_SIZE + j++] = (presult->extra_result.bioext_data >> 8) & 0xff; + ble_buf[i * FRAME_SIZE + j++] = (presult->extra_result.bioext_data >> 0) & 0xff; + } + if (presult->slot_result[2].slot_storefifo) + { + ble_buf[i * FRAME_SIZE + j++] = (VC921x_GET_SLOT_PS_DATA(presult, psfreq_index) >> 8) & 0xff; + ble_buf[i * FRAME_SIZE + j++] = (VC921x_GET_SLOT_PS_DATA(presult, psfreq_index) >> 0) & 0xff; + } + else + { + ble_buf[i * FRAME_SIZE + j++] = (presult->slot_result[2].u.ps_data >> 8) & 0xff; + ble_buf[i * FRAME_SIZE + j++] = (presult->slot_result[2].u.ps_data >> 0) & 0xff; + } +/* 30-35 xyz:2*3=6*/ +#if 1 + ble_buf[i * FRAME_SIZE + j++] = pgsensor->xData[i] >> 8; + ble_buf[i * FRAME_SIZE + j++] = pgsensor->xData[i] & 0xff; + ble_buf[i * FRAME_SIZE + j++] = pgsensor->yData[i] >> 8; + ble_buf[i * FRAME_SIZE + j++] = pgsensor->yData[i] & 0xff; + ble_buf[i * FRAME_SIZE + j++] = pgsensor->zData[i] >> 8; + ble_buf[i * FRAME_SIZE + j++] = pgsensor->zData[i] & 0xff; +#else /* temp result */ + ble_buf[i * FRAME_SIZE + j++] = presult->extra_result.tempref_data >> 8; + ble_buf[i * FRAME_SIZE + j++] = presult->extra_result.tempref_data & 0xff; + ble_buf[i * FRAME_SIZE + j++] = presult->extra_result.temp0_data >> 8; + ble_buf[i * FRAME_SIZE + j++] = presult->extra_result.temp0_data & 0xff; + ble_buf[i * FRAME_SIZE + j++] = presult->extra_result.temp1_data >> 8; + ble_buf[i * FRAME_SIZE + j++] = presult->extra_result.temp1_data & 0xff; +#endif + /* 36-spo2,37-hr,38-algo_mode */ + ble_buf[i * FRAME_SIZE + j++] = pdev->heart_rate; + ble_buf[i * FRAME_SIZE + j++] = pdev->spo2; + ble_buf[i * FRAME_SIZE + j++] = pdev->heart_algo_mode; + /* [39]wear_bioret(bit7) | algoret(bit6) | acceret(bit5) | wear_drv(bit3~bit0) */ + ble_buf[i * FRAME_SIZE + j++] = (bio_result<<7)|(algo_result<<6)|(acce_result<<5)| (pdev->wear & 0xf); + } + debug_orginal_data_send(ppgsize * FRAME_SIZE); +} + +#endif + +void vc921x_send_orginal_data_debug_mark_data( void ) +{ + for(int i=0; i= buf_size) + { + return 0; + } + // 判断剩余数据长度是否还需要分包 + residue_size = buf_size - buf_index; + if (residue_size <= BLE_PACK_SIZE) + { + send_data_by_ble(&ble_buf[buf_index], residue_size); + return 0; + } + else + { + send_data_by_ble(&ble_buf[buf_index], BLE_PACK_SIZE); + buf_index += BLE_PACK_SIZE; + return 1; + } +} + +/* 创建软件定时器,如果数据太长通过软件定时器发送 */ +#define TIMER_ID 0xf0 +static void *debug_send_timer = NULL; +static void debug_send_timer_start(void) +{ + if (debug_send_timer) + { + os_timer_restart(&debug_send_timer, TIMER_BLE_GAP); + } +} +static void debug_send_timer_stop(void) +{ + if (debug_send_timer) + { + os_timer_stop(&debug_send_timer); + } +} + +static void debug_send_timer_callback(void *p_handle) +{ + if (timer_send_data_function()) + { + /* 定时器继续循环 */ + os_timer_restart(&debug_send_timer, TIMER_BLE_GAP); + } + else + { + /* 关闭定时器 */ + debug_send_timer_stop(); + } +} + +void vc921x_send_orginal_data_timer_init(void) +{ + // if (debug_send_timer == NULL) + // { + // os_timer_create(&debug_send_timer, "debug_send_timer", TIMER_ID, TIMER_BLE_GAP, false, debug_send_timer_callback); + // } + // os_timer_restart(&debug_send_timer, TIMER_BLE_GAP); + // os_timer_stop(&debug_send_timer); +} + +static void debug_orginal_data_send(unsigned short int size) +{ + buf_index = 0; + buf_size = size; + printf("debug_orginal_data_send = %d", size); +#if 1 + if (buf_size <= BLE_PACK_SIZE) + { + send_data_by_ble(ble_buf, size); + } + else + { + send_data_by_ble(ble_buf, BLE_PACK_SIZE); + buf_index += BLE_PACK_SIZE; + debug_send_timer_start(); + } +#else + for (unsigned char i = 0; i * 8 < size; i++) + { + printf("[%d~%d]:%02x %02x %02x %02x , %02x %02x %02x %02x", i * 8, (i + 1) * 8, + ble_buf[i * 8], ble_buf[i * 8 + 1], ble_buf[i * 8 + 2], ble_buf[i * 8 + 3], ble_buf[i * 8 + 4], ble_buf[i * 8 + 5], ble_buf[i * 8 + 6], ble_buf[i * 8 + 7]); + } +#endif +} diff --git a/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_user_app.c b/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_user_app.c new file mode 100644 index 000000000..c6b2281cb --- /dev/null +++ b/code/sdk/zhihui/drivers/ppg/vc921x/src/vc921x_user_app.c @@ -0,0 +1,897 @@ +/********************************************************************************************** + * @Copyright(c) 2023, Chengdu vcaresensor Microelectronics Corporation. All rights reserved. + * @Author: Tse + * @Date: 2023-09-22 17:39:10 + * @LastEditTime: 2024-04-11 20:38:21 + * @LastEditors: Tse + * @Description: + **********************************************************************************************/ +/* 驱动头文件 */ +#include "vc921x_driver_com.h" +#include "vc921x_sample_result.h" +#include "vc921x_user_app.h" +/* 算法头文件 */ +#include "algo.h" +#include "spo2Algo_16bit.h" +#include "vcSportMotionIntAlgo.h" +#include "custom_utils.h" +#include "custom_utils.h" +#include "pinctrl_porting.h" +#include "hal_gpio.h" +#include "vcare_device_com.h" + +#define VC921X_I2C_BUS I2C_BUS_2 +#define CHIP_I2C_ADDRESS 0x33 + +/* vcare_ppg_device */ +static vcare_ppg_device_t vc921x_dev = {"vc921x", 0, 0, 0, 0, 0, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0}; +/* G-Sensor Data */ +static gsensor_axes vc921x_gsensor_data = {0}; + +static void vc921x_get_board_gsensor_data(unsigned short int *pgsensor_len, unsigned short int ppg_count); +static unsigned int vc921x_gsensor_actuating_quantity(int x_axis, int y_axis, int z_axis); +static int vc921x_heart_rate_calculate(vcare_ppg_device_t *pdev, unsigned char hr_slot ); +static int vc921x_spo2_calculate(vcare_ppg_device_t *pdev, unsigned char ir_slot, unsigned char red_slot); + + +// S_AGPIO_L26 心率复位 PPG reset +#define GPIO_PIN_PRG_RESET S_AGPIO_L26 +// S_AGPIO_R0 PRG_LED_POWER ON 心率等电源控制脚 +#define GPIO_PIN_PRG_LED_ON S_AGPIO_R0 + +void tyhx_poweron(void) +{ + // uapi_pin_set_mode(GPIO_PIN_PRG_RESET, (pin_mode_t)HAL_PIO_FUNC_GPIO); + // uapi_gpio_set_dir(GPIO_PIN_PRG_RESET, GPIO_DIRECTION_OUTPUT); + // uapi_gpio_set_val(GPIO_PIN_PRG_RESET, GPIO_LEVEL_HIGH); + + uapi_pin_set_mode(GPIO_PIN_PRG_LED_ON, (pin_mode_t)HAL_PIO_FUNC_GPIO); + uapi_gpio_set_dir(GPIO_PIN_PRG_LED_ON, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_val(GPIO_PIN_PRG_LED_ON, GPIO_LEVEL_HIGH); + LOS_Mdelay(50); +} +void tyhx_poweroff(void) +{ + // uapi_pin_set_mode(GPIO_PIN_PRG_RESET, (pin_mode_t)HAL_PIO_FUNC_GPIO); + // uapi_gpio_set_dir(GPIO_PIN_PRG_RESET, GPIO_DIRECTION_OUTPUT); + // uapi_gpio_set_val(GPIO_PIN_PRG_RESET, GPIO_LEVEL_LOW); + + uapi_pin_set_mode(GPIO_PIN_PRG_LED_ON, (pin_mode_t)HAL_PIO_FUNC_GPIO); + uapi_gpio_set_dir(GPIO_PIN_PRG_LED_ON, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_val(GPIO_PIN_PRG_LED_ON, GPIO_LEVEL_LOW); + + LOS_Mdelay(1); +} + +/**************************************************************************** + * @description: 驱动参数保存/读取 + * @param {unsigned int} size + * @return {*} + ****************************************************************************/ +int vc921x_param_write_flash(unsigned char *pdata, unsigned int size) +{ + uapi_save_file_data("/user/userdata/vc921x.bin",pdata,size,"wb"); + return 0; +} +int vc921x_param_read_flash(unsigned char *pdata, unsigned int size) +{ + uapi_get_file_data("/user/userdata/vc921x.bin",pdata,&size); + return 0; +} + +/**************************************************************************** + * @description: 内存申请 + * @param {unsigned int} size + * @return {*} + ****************************************************************************/ +void *vc921x_malloc(unsigned int size) +{ + return malloc(size); +} +/**************************************************************************** + * @description: 内存释放 + * @param {void} *ptr + * @return {*} + ****************************************************************************/ +void vc921x_free(void *ptr) +{ + if (ptr) + { + free(ptr); + } +} +/**************************************************************************** + * @description: + * @param {unsigned char} regaddr + * @param {unsigned char*} pbuf + * @param {unsigned short int} size + * @return {*} + ****************************************************************************/ +int vc921x_read_register(unsigned char regaddr, unsigned char *pbuf, unsigned short int size) +{ + custom_i2c_read_regs(CHIP_I2C_ADDRESS, regaddr, 1 , pbuf, size, VC921X_I2C_BUS); + return 0; +} +int vc921x_write_register(unsigned char regaddr, unsigned char *pbuf, unsigned short int size) +{ + custom_i2c_write_reg(CHIP_I2C_ADDRESS, regaddr, 1, pbuf, size, VC921X_I2C_BUS); + return 0; +} +unsigned int vc921x_get_cputimer_tick(void) +{ + return uapi_systick_get_count(); +} + +/*********************************************************************************************************/ +/* user gsensor code */ +#define ENABLE_GSENSOR 0 +//#include "module_gsensor_stk8321.h" +//#include "hub_gsensor.h" +const unsigned char arry10[] = {0, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12}; +const unsigned char arry20[] = {0, 2, 3, 5, 6, 8, 9, 11, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28}; +static void vc921x_get_board_gsensor_data(unsigned short int *pgsensor_len, unsigned short int ppg_count) +{ + int i = 0; + unsigned char gsensor_len = 0; + memset(&vc921x_gsensor_data, 0, sizeof(vc921x_gsensor_data)); +#if ENABLE_GSENSOR + /* Gsensor精度要求 ReadGsensorFIFO(+-4G11bit or +-8G12bit 256/g) 例如一个fifo周期为1sec,25组ppg数据,需要获取1sec内25组gsensor数据(40ms*25),用于匹配25组ppg信号。 + 如果ppg频率高于ppg频率则对进行抽样处理,以下代码仅参考 */ + AxesRaw_t gsensor_buff[40] = {0}; + stk8321_get_fifo_data(&gsensor_len, gsensor_buff); + VCARE_DBG_LOG("gsensor_len[%d]-ppgsize[%d]", gsensor_len, ppg_count); + if (0 == ppg_count) + { + /* 不抽样 */ + *pgsensor_len = gsensor_len; + for (i = 0; i < *pgsensor_len; i++) + { +#if (BOARD_VERSION == BOARD_NEW_BOARD) + vc921x_gsensor_data.xData[i] = gsensor_buff[i].AXIS_X; + vc921x_gsensor_data.yData[i] = gsensor_buff[i].AXIS_Y; + vc921x_gsensor_data.zData[i] = gsensor_buff[i].AXIS_Z; +#elif (BOARD_VERSION == BOARD_OLD_BOARD) + vc921x_gsensor_data.xData[i] = gsensor_buff[i].AXIS_Y; + vc921x_gsensor_data.yData[i] = gsensor_buff[i].AXIS_X; + vc921x_gsensor_data.zData[i] = gsensor_buff[i].AXIS_Z; +#endif + } + } + /* æ­¤demo中gsensor的采样频率高于ppg采样,所以正常情况下,gsensor的数量一定是大于ppg的数据。特殊情况需要考虑ppg数量大于gsensor数量。需要对gsensor数据进行补长 */ + if ((gsensor_len >= ppg_count) && (ppg_count != 0)) + { + *pgsensor_len = ppg_count; + /* ppglen = 10 抽样 */ + if (10 == ppg_count) + { + for (i = 0; i < ppg_count; i++) + { +#if (BOARD_VERSION == BOARD_NEW_BOARD) + vc921x_gsensor_data.xData[i] = gsensor_buff[arry10[i]].AXIS_X; + vc921x_gsensor_data.yData[i] = gsensor_buff[arry10[i]].AXIS_Y; + vc921x_gsensor_data.zData[i] = gsensor_buff[arry10[i]].AXIS_Z; +#elif (BOARD_VERSION == BOARD_OLD_BOARD) + vc921x_gsensor_data.xData[i] = gsensor_buff[arry10[i]].AXIS_Y; + vc921x_gsensor_data.yData[i] = gsensor_buff[arry10[i]].AXIS_X; + vc921x_gsensor_data.zData[i] = gsensor_buff[arry10[i]].AXIS_Z; +#endif + } + } + else /* ppglen = 20 、25抽样 */ + { + for (i = 0; i < ppg_count; i++) + { +#if (BOARD_VERSION == BOARD_NEW_BOARD) + vc921x_gsensor_data.xData[i] = gsensor_buff[arry20[i]].AXIS_X; + vc921x_gsensor_data.yData[i] = gsensor_buff[arry20[i]].AXIS_Y; + vc921x_gsensor_data.zData[i] = gsensor_buff[arry20[i]].AXIS_Z; +#elif (BOARD_VERSION == BOARD_OLD_BOARD) + vc921x_gsensor_data.xData[i] = gsensor_buff[arry20[i]].AXIS_Y; + vc921x_gsensor_data.yData[i] = gsensor_buff[arry20[i]].AXIS_X; + vc921x_gsensor_data.zData[i] = gsensor_buff[arry20[i]].AXIS_Z; +#endif + } + } + } + else if ((gsensor_len < ppg_count) && (gsensor_len != 0)) /* gsensor不足补全 */ + { + *pgsensor_len = ppg_count; + for (i = 0; i < gsensor_len; i++) + { +#if (BOARD_VERSION == BOARD_NEW_BOARD) + vc921x_gsensor_data.xData[i] = gsensor_buff[i].AXIS_X; + vc921x_gsensor_data.yData[i] = gsensor_buff[i].AXIS_Y; + vc921x_gsensor_data.zData[i] = gsensor_buff[i].AXIS_Z; +#elif (BOARD_VERSION == BOARD_OLD_BOARD) + vc921x_gsensor_data.xData[i] = gsensor_buff[i].AXIS_Y; + vc921x_gsensor_data.yData[i] = gsensor_buff[i].AXIS_X; + vc921x_gsensor_data.zData[i] = gsensor_buff[i].AXIS_Z; +#endif + } + for (i = gsensor_len; i < ppg_count; i++) + { +#if (BOARD_VERSION == BOARD_NEW_BOARD) + vc921x_gsensor_data.xData[i] = gsensor_buff[gsensor_len - 1].AXIS_X; + vc921x_gsensor_data.yData[i] = gsensor_buff[gsensor_len - 1].AXIS_Y; + vc921x_gsensor_data.zData[i] = gsensor_buff[gsensor_len - 1].AXIS_Z; +#elif (BOARD_VERSION == BOARD_OLD_BOARD) + vc921x_gsensor_data.xData[i] = gsensor_buff[gsensor_len - 1].AXIS_Y; + vc921x_gsensor_data.yData[i] = gsensor_buff[gsensor_len - 1].AXIS_X; + vc921x_gsensor_data.zData[i] = gsensor_buff[gsensor_len - 1].AXIS_Z; +#endif + } + } +#endif +} + +/**************************************************************************** + * @description: gsensor判断是否存在运动 + * @param {int} x_axis + * @param {int} y_axis + * @param {int} z_axis + * @return {*} + ****************************************************************************/ +static unsigned int vc921x_gsensor_actuating_quantity(int x_axis, int y_axis, int z_axis) +{ + unsigned int ret_atc = 0; + unsigned int abs_sum_current = 0; + static unsigned int abs_sum_last = 0; + if (x_axis < 0) + { + x_axis = -x_axis; + } + if (y_axis < 0) + { + y_axis = -y_axis; + } + if (z_axis < 0) + { + z_axis = -z_axis; + } + abs_sum_current = x_axis + y_axis + z_axis; + if (abs_sum_last && abs_sum_current) + { + if (((abs_sum_last > abs_sum_current) ? (abs_sum_last - abs_sum_current) : (abs_sum_current - abs_sum_last)) >= 40) + { + VCARE_DBG_LOG("ACTIVE..."); + ret_atc = 1; + } + } + abs_sum_last = abs_sum_current; + vc921x_drv_set_acce_wear_status(&vc921x_dev, ret_atc); + return ret_atc; +} + +/**************************************************************************** + * @description: ppg数据加入心率算法 + * @param {vcare_ppg_device_t} *pdev + * @return {*} + ****************************************************************************/ +static int vc921x_heart_rate_calculate(vcare_ppg_device_t *pdev, unsigned char hr_slot ) +{ + int heartRate = 0; + unsigned char reliability = 0; + AlgoInputData_t algoInputData_t = {0}; + AlgoOutputData_t algoOutputData = {0}; + vc921x_sample_info_t *result_info = NULL; + vc921x_sample_slot_t *result = NULL; + unsigned char adc_param = 0; + int i = 0; + if (!pdev) + { + return -1; + } + heartRate = pdev->heart_rate; + result_info = (vc921x_sample_info_t *)pdev->result; + result = &result_info->slot_result[hr_slot]; /* 驱动中初始化slot0为心率绿光ppg */ + adc_param = (result_info->adc_resolution-16)/2; + for (i = 0; i < result->ppg_num; i++) + { + algoInputData_t.axes.x = vc921x_gsensor_data.xData[i]; + algoInputData_t.axes.y = vc921x_gsensor_data.yData[i]; + algoInputData_t.axes.z = vc921x_gsensor_data.zData[i]; + algoInputData_t.envSample = result->env_data; + algoInputData_t.ppgSample = (adc_param<<24)|VC921x_GET_SLOT_SAMPLE_DATA(result_info, hr_slot, i ); /* 驱动中初始化slot0为心率绿光ppg */ + Algo_Input(&algoInputData_t, 1000 / 25, (AlgoSportMode)pdev->heart_algo_mode, 0, 0); + VCARE_DBG_LOG("mode[%d]-[%d] (%d)ppg=%d, x=%d, y=%d, z=%d \n", pdev->heart_algo_mode, i, adc_param, algoInputData_t.ppgSample&0xffffff, \ + algoInputData_t.axes.x, algoInputData_t.axes.y, algoInputData_t.axes.z); + } + Algo_Output(&algoOutputData); + heartRate = algoOutputData.hrData; + reliability = algoOutputData.reliability; + if (-1 == heartRate) + { + Algo_Init(); + /* call heart algo wear status check,[-1] */ + vc921x_drv_set_algo_wear_status(pdev, heartRate); + VCARE_DBG_LOG("ALGO_STA=-1"); + } + VCARE_DBG_LOG("ALGO-HR[%d][%d]", heartRate, reliability); + return heartRate; +} + +/**************************************************************************** + * @description: 血氧PPG数据加入血氧算法 + * @param {vcare_ppg_device_t} *pdev + * @return {*} + ****************************************************************************/ +static int vc921x_spo2_calculate(vcare_ppg_device_t *pdev, unsigned char red_slot, unsigned char ir_slot) +{ + vc921x_sample_info_t *result_info = NULL; + float spo2CalValue = -1.0f; + int spo2Value = 0; + unsigned char sportFlag = 0; + unsigned char adc_param = 0; + unsigned int ir_ppg_data = 0, red_ppg_data = 0; + if (!pdev) + { + return -1; + } + spo2Value = pdev->spo2; + result_info = (vc921x_sample_info_t *)pdev->result; + adc_param = (result_info->adc_resolution-16)/2; + for (int i = 0; i < result_info->slot_result[ir_slot].ppg_num; i++) + { + red_ppg_data= (adc_param<<24)|VC921x_GET_SLOT_SAMPLE_DATA(result_info, red_slot, i ); + ir_ppg_data = (adc_param<<24)|VC921x_GET_SLOT_SAMPLE_DATA(result_info, ir_slot, i ); + spo2CalValue = vcSpo2Calculate_16bit( red_ppg_data, ir_ppg_data, 1); + sportFlag = vcSportMotionCalculate(vc921x_gsensor_data.xData[i], vc921x_gsensor_data.yData[i], vc921x_gsensor_data.zData[i]); + if (-1 != (int)spo2CalValue && spo2CalValue <= 100 && 0 == sportFlag) + { + spo2Value = (int)spo2CalValue; + } + VCARE_DBG_LOG("[%d](%d) ppg_ir=%d, ppg_red=%d, x=%d, y=%d, z=%d", i, adc_param, \ + ir_ppg_data&0xffffff, red_ppg_data&0xffffff, \ + vc921x_gsensor_data.xData[i], vc921x_gsensor_data.yData[i], vc921x_gsensor_data.zData[i]); + } + VCARE_DBG_LOG("ALGO-SPO2[%d][%f]", (unsigned short int)spo2Value, spo2CalValue); + return spo2Value; +} + +/******************************************************************************************************** + * 温度功能部分,需要进行对数运算,必须包含math库 + ********************************************************************************************************/ +#include "math.h" +/* fix Rref = 10K R */ +#define REF_R 10 +/* NTC parameter */ +#define NTC_B 3380 +#define NTC_T (273.15f + 25) /* 25摄氏度下的绝对温度 */ +#define NTC_R 10 /* 10K */ +/* vc921x parameter */ +const unsigned char vc921x_temperature_current[] = {0, 2, 4, 8, 12, 16, 20, 24}; +static signed short int get_ntc_temperature(float ntc_value) +{ + signed short int temperature = 0; + float tmp = 0.0f; + tmp = 1 / (1 / NTC_T + (log(ntc_value / NTC_R) / NTC_B)) - 273.15f; + temperature = (signed short int)(tmp * 10); + VCARE_DBG_LOG("[CALC_TMP]=%d(%4.2f)", temperature, tmp); + return temperature; +} +static void vc921x_temperature_ntc_calculate(vcare_ppg_device_t *pdev) +{ + unsigned char ext_I = 0; + float ext_R = 0.0f; + vc921x_sample_info_t *presult_t = (vc921x_sample_info_t *)pdev->result; + if(presult_t->extra_result.tempslot1_enwork && presult_t->extra_result.temp1_data && presult_t->extra_result.tempref_data ) + { + /* 采样值转换为电阻值 */ + ext_I = vc921x_temperature_current[presult_t->extra_result.tempslot1_setcur]; + if (0 != ext_I) + { + ext_R = (float)(20 * REF_R * presult_t->extra_result.temp1_data) / (float)(presult_t->extra_result.tempref_data * ext_I); + VCARE_DBG_LOG("ext_R=%f", ext_R); + pdev->temperature1 = get_ntc_temperature(ext_R); + } + } +} + +/********************************************************************************************************* + * USER_API + ********************************************************************************************************/ +/* +!!!只针对[心率/摘配带]模式,其他模式均为自适应固定时间fifo时间,注意修改之前需要确认在使用的配置模式下,fifo不会溢出,否则将无法正确获取数据 +if( vc921x_PS_WORK_FAST_FREQUENCY==1 ) (ms) +{ps=12.5hz: fifotime=800,720,640,560...80} +else +{ps=5hz: fifotime=1000,800,600,400,200} +*/ +/**************************************************************************** + * @description: 初始化IC并启动工作 + * @param {InitParamTypeDef} *pinitconfig + * @return {*} + ****************************************************************************/ +void vc921x_usr_device_init( InitParamTypeDef *pinitconfig ) +{ + // tyhx_poweron(); + int algo_confirm_ret=0; + unsigned short int ppgdiv = 0; + unsigned char psdiv = 0; + unsigned char fifodiv = 0; + unsigned char dropdiv =20; /* drop_sta:ps_freq = ppg_hz/drop_div */ + unsigned char frequency=25; /* default frequency=25hz */ + /* ppg_freq=20K/ppgdiv, ps_freq=ppg_freq/psdiv, fifodiv=psfreq/fifodiv + * fifo_max=192byte. The amount of byte by once sampling must not be greater than fifo_max(192bytes). + */ + extern unsigned short int VC921x_CFG_MCU_TICK_FREQUENCY; /* mcu tick frequency parameter */ + extern unsigned short int VC921x_WEAR_PARAM_BIO_ABS_HOLD; /* factory calibration parameter-hold */ + extern unsigned short int VC921x_WEAR_PARAM_BIO_ABS_DROP; /* factory calibration parameter-drop */ + VC921x_CFG_MCU_TICK_FREQUENCY=32000; + VC921x_WEAR_PARAM_BIO_ABS_HOLD=150; /* save_diff*8/10 */ + VC921x_WEAR_PARAM_BIO_ABS_DROP=100; /* save_diff*5/10 */ + + vc921x_dev.workmode = (work_mode)pinitconfig->work_mode; + vc921x_dev.heart_algo_mode = pinitconfig->hr_algo_mode; + switch (pinitconfig->work_mode) + { + case WORK_MODE_HR: + case WORK_MODE_SPO2: + case WORK_MODE_WEAR: + case WORK_MODE_FACTORY0: + case WORK_MODE_FACTORY1: + case WORK_MODE_UNDEFINED: + case WORK_MODE_HRSPO2: + frequency=25; + break; + case WORK_MODE_STRESS: + frequency=50; + break; + case WORK_MODE_HRV: + case WORK_MODE_BLOODPRESSURE: + frequency=100; + break; + default: + break; + } + ppgdiv = 20000 / frequency; /* ppg_freq = frequency */ +#if (VC921x_PS_WORK_FREQUENCY_CONFIG==1) + psdiv = pinitconfig->sampling_gap_time*25/1000; /* 520ms ~ ps_div=13, fifo_div=1, 注意设置初始化参数的时间时,要估算一次fifo长度和fifo总长度的关系 */ + fifodiv = 1; + if( WORK_MODE_FACTORY0==pinitconfig->work_mode || WORK_MODE_FACTORY1==pinitconfig->work_mode ) + { + frequency=50; + ppgdiv = 20000 / frequency; + psdiv = pinitconfig->sampling_gap_time*25/1000; /* 注意:别修改psdiv,在有活体的配置里面,会影响活体的时间参数.所以此处通过提高一倍ppg频率,进而加快FAT模式的中断数据更新 */ + fifodiv=1; + } +#elif( VC921x_PS_WORK_FREQUENCY_CONFIG==0 ) /* ps=5hz */ + dropdiv =20; + if (frequency == 25) + { + psdiv = 5; + fifodiv = 4; /* ppg=25hz, psbio=5hz, fifo=1.25hz,fifotime=800ms */ + if( WORK_MODE_SPO2==pinitconfig->work_mode ) + { + fifodiv=2; + } + } + else if (frequency == 50) + { + psdiv = 10; + fifodiv = 2; /* ppg=50hz, psbio=5hz, fifo=2.5hz,fifotime=400ms */ + } + else if (frequency == 100) + { + psdiv = 20; + fifodiv = 1; /* ppg=100hz, psbio=5hz, fifo=5hz, fifotime=200ms */ + } + if( WORK_MODE_HR==pinitconfig->work_mode || WORK_MODE_WEAR==pinitconfig->work_mode ) + { + fifodiv = pinitconfig->sampling_gap_time*50/10000; + } + if( WORK_MODE_HRSPO2==pinitconfig->work_mode ) + { + fifodiv=2; + } + if( WORK_MODE_FACTORY0==pinitconfig->work_mode || WORK_MODE_FACTORY1==pinitconfig->work_mode ) + { + dropdiv = psdiv; + fifodiv=1; + } +#endif + /* 先初始化启动心率芯片 */ + vc921x_drv_init_start_work(&vc921x_dev, pinitconfig->work_mode, ppgdiv, psdiv, fifodiv, dropdiv); + vc921x_dev.dev_work_status = 1; + /* 如果需要保持测试值,比如24h的心率模式,初始化时则不清除记录值心率值,避免初始化后出现一段为0的情况 */ + vc921x_dev.heart_rate = 0; + vc921x_dev.stress=0; + vc921x_dev.hrv = 0; + vc921x_dev.spo2 = 0; + vc921x_dev.blood_pressure=0; + vc921x_dev.ecg=0; + /****** algo_identify_init ******/ + //algo_confirm_ret = Algo_Confirm_init( RtkWristbandSys.SecondCountRTC ); //1704038400 + Algo_Init(); + vcSpo2AlgoInit_16bit(); + vcSportMotionAlgoInit(); + VCARE_DBG_LOG("work_mode=%d,algo_confirm=%d", pinitconfig->work_mode, algo_confirm_ret ); +} + +/**************************************************************************** + * @description: 获取IC及驱动相关参数 + * @param {unsigned char} *pchip_id + * @param {unsigned char} *pvers_id + * @param {char} * + * @param {char} * + * @return {*} + ****************************************************************************/ +void vc921x_usr_get_dev_info(unsigned char *pchip_id, unsigned char *pvers_id, char **drv_name, char **dev_name) +{ + /* pchip_id=0x24, pvers_id=0x10... */ + vc921x_drv_get_hwsw_infomation(drv_name, pchip_id, pvers_id); +} +/**************************************************************************** + * @description: 开始工作 + * @return {*} + ****************************************************************************/ +void vc921x_usr_start_work(void) +{ + vc921x_drv_wakeup_work(&vc921x_dev); + vc921x_dev.dev_work_status = 1; +} +/**************************************************************************** + * @description: 停止工作 + * @return {*} + ****************************************************************************/ +void vc921x_usr_stop_work(void) +{ + vc921x_drv_sleep_work(&vc921x_dev); + vc921x_dev.dev_work_status = 0; +} +/**************************************************************************** + * @description: 软复位 + * @return {*} + ****************************************************************************/ +void vc921x_usr_soft_reset(void) +{ + vc921x_drv_software_reset(&vc921x_dev); + vc921x_dev.dev_work_status = 0; +} +/**************************************************************************** + * @description: 活体IC参数重置校准 + * @return {*} + ****************************************************************************/ +void vc921x_usr_reset_bio_param(void) +{ + vc921x_drv_clear_reset_bio_param(&vc921x_dev); +} +/**************************************************************************** + * @description: 获取佩戴状态 + * @return {*} + ****************************************************************************/ +int vc921x_usr_get_wear_status(void) +{ + if (WEAR_STA_HOLD == vc921x_dev.wear) + { + return 1; + } + return 0; +} +/**************************************************************************** + * @description: 定时器校验当前IC状态,如果发生异常,则重启LDO后初始化 + * @return {*} + ****************************************************************************/ +void vc921x_usr_check_temperature_abnormal(void) +{ + unsigned char check_same_ret = 0, check_freq_ret = 0, check_temp_ret = 0; + unsigned short check_cnt1 = 0, check_cnt2 = 0; + check_same_ret = vc921x_drv_read_check_temperature_abnormal(&vc921x_dev, &check_freq_ret, &check_temp_ret, &check_cnt1, &check_cnt2); + vc921x_temperature_ntc_calculate(&vc921x_dev); + vc921x_sample_info_t *result_info = (vc921x_sample_info_t *)vc921x_dev.result; + VCARE_DBG_LOG("status(%d),psbio_0(%d,%d),cnt=%d,%d,same_ret=%d,freq_ret=%d,temp_ret=%d, temp_ext=%d",vc921x_dev.dev_work_status, + result_info->slot_result[2].u.ps_data, result_info->extra_result.bioext_data, check_cnt1, check_cnt2, + check_same_ret, check_freq_ret, check_temp_ret, vc921x_dev.temperature1); + + if (1 == vc921x_dev.dev_work_status && /* IC处于工作状态 */ + ((result_info->slot_result[2].u.ps_data==0 && (result_info->extra_result.bioext_data==0&&result_info->extra_result.bioext_enwork)) /* ps和bio同时异常,或者RCOSC异常 */ + || (check_cnt1 == 0 && check_cnt2 == 0) + || (1 == check_same_ret) /* bio-cnt其中之一已经不再变化 */ + || (1 == check_freq_ret) /* 频率温度异常 */ + || (1 == check_temp_ret) /* 内部温度异常 */ + || vc921x_dev.temperature1 >= 600)) /* NTC温度异常,如果硬件NC则会固定250 */ + { + /* 1.close ldo, 2.delay(200ms), 3.open ldo, 4.delay(10ms), 5.init_dev */ + //extern void vcare_ldo_power_ctrl(unsigned char onoff); + //vcare_ldo_power_ctrl(0); + //platform_delay_ms(200); + //vcare_ldo_power_ctrl(1); + //platform_delay_ms(10); + vc921x_drv_init_start_work(&vc921x_dev, vc921x_dev.workmode, result_info->ppg_div, result_info->ps_div, result_info->fifo_div, result_info->drop_div); + } +} + +/**************************************************************************** + * @description: 中断事件处理事件 + * @param {unsigned char} heart_algo_mode + * @param {unsigned char} spo2_algo_mode + * @return {*} + ****************************************************************************/ +void vc921x_usr_device_handler( unsigned char heart_algo_mode, unsigned char spo2_algo_mode ) +{ + unsigned short int ppg_num = 0; + unsigned short int gsensor_num = 0; + vc921x_sample_info_t *sample_result_info_ptr = (vc921x_sample_info_t *)vc921x_dev.result; + if( !vc921x_dev.dev_work_status ) return; + VCARE_DBG_LOG("------------------"); + vc921x_dev.heart_algo_mode = heart_algo_mode; + vc921x_dev.spo2_algo_mode = spo2_algo_mode; + vc921x_drv_get_result_handler(&vc921x_dev); + vc921x_dev.wear = vc921x_drv_get_wear_status(&vc921x_dev); + if (vc921x_drv_is_ps_event(&vc921x_dev) || vc921x_drv_is_fifo_event(&vc921x_dev)) + { + //disp_data.wear_sta = vc921x_dev.wear; + //disp_data.disp_bio_val = sample_result_info_ptr->extra_result.bioext_data; + //disp_data.disp_bio_abs = sample_result_info_ptr->extra_result.bioabs_calc; + //VCARE_DBG_LOG("bio_absgap=%d, bioext=%d", disp_data.disp_bio_abs, disp_data.disp_bio_val ); + } + switch (vc921x_dev.workmode) + { + case WORK_MODE_HR: + if (vc921x_drv_is_fifo_event(&vc921x_dev)) + { + ppg_num = sample_result_info_ptr->slot_result[0].ppg_num; + if (ppg_num > 40) + break; + vc921x_get_board_gsensor_data(&gsensor_num, ppg_num); + if (gsensor_num) + { + vc921x_gsensor_actuating_quantity(vc921x_gsensor_data.xData[0], vc921x_gsensor_data.yData[0], vc921x_gsensor_data.zData[0]); + } + printf("vc921x_dev.wear : %d \n",vc921x_dev.wear); + if (WEAR_STA_HOLD == vc921x_dev.wear) + { + vc921x_dev.heart_rate = vc921x_heart_rate_calculate(&vc921x_dev, 0); + printf("vc921x_dev.heart_rate : %d \n",vc921x_dev.heart_rate); + //vc921x_send_orginal_data(&vc921x_dev, &vc921x_gsensor_data, ppg_num); + } + else + { + vc921x_dev.heart_rate = 0; + //vc921x_send_orginal_data(&vc921x_dev, &vc921x_gsensor_data, (ppg_num?ppg_num:1) ); + Algo_Init(); + } + //disp_data.hr_rate = vc921x_dev.heart_rate; + } + break; + case WORK_MODE_SPO2: + if (vc921x_drv_is_fifo_event(&vc921x_dev)) + { + ppg_num = sample_result_info_ptr->slot_result[1].ppg_num; + if (ppg_num > 40) + break; + vc921x_get_board_gsensor_data(&gsensor_num, ppg_num); + if (WEAR_STA_HOLD == vc921x_dev.wear) + { + vc921x_dev.spo2 = vc921x_spo2_calculate(&vc921x_dev, 1, 0); + //vc921x_send_orginal_data(&vc921x_dev, &vc921x_gsensor_data, ppg_num); + } + else + { + vc921x_dev.spo2 = 0; + vcSpo2AlgoInit_16bit(); + vcSportMotionAlgoInit(); + } + printf("vc921x_dev.spo2 : %d \n",vc921x_dev.spo2); + //disp_data.spo2_val = vc921x_dev.spo2; + } + break; + case WORK_MODE_HRV: + if (vc921x_drv_is_fifo_event(&vc921x_dev)) + { + ppg_num = sample_result_info_ptr->slot_result[0].ppg_num; + if (ppg_num > 40) + break; + vc921x_get_board_gsensor_data(&gsensor_num, ppg_num); + if (WEAR_STA_HOLD == vc921x_dev.wear) + { /*wear is hold,call_algo_func*/ + //vc921x_send_orginal_data(&vc921x_dev, &vc921x_gsensor_data, ppg_num); + } + else + { /*wear is drop,clear_algo_data*/ + } + } + break; + case WORK_MODE_STRESS: + if (vc921x_drv_is_fifo_event(&vc921x_dev)) + { + ppg_num = sample_result_info_ptr->slot_result[0].ppg_num; + if (ppg_num > 40) + break; + vc921x_get_board_gsensor_data(&gsensor_num, ppg_num); + if (WEAR_STA_HOLD == vc921x_dev.wear) + { /*wear is hold,call_algo_func*/ + //vc921x_send_orginal_data(&vc921x_dev, &vc921x_gsensor_data, ppg_num); + } + else + { /*wear is drop,clear_algo_data*/ + } + //disp_data.hr_rate = vc921x_dev.heart_rate; + } + break; + case WORK_MODE_BLOODPRESSURE: + if (vc921x_drv_is_fifo_event(&vc921x_dev)) + { + ppg_num = sample_result_info_ptr->slot_result[0].ppg_num; + if (ppg_num > 40) + break; + vc921x_get_board_gsensor_data(&gsensor_num, ppg_num); + if (WEAR_STA_HOLD == vc921x_dev.wear) + { /*wear is hold,call_algo_func*/ + //vc921x_send_orginal_data(&vc921x_dev, &vc921x_gsensor_data, ppg_num); + } + else + { /*wear is drop,clear_algo_data*/ + } + } + break; + case WORK_MODE_WEAR: + if (vc921x_drv_is_fifo_event(&vc921x_dev)) + { + vc921x_get_board_gsensor_data(&gsensor_num, ppg_num); + if (gsensor_num) + { + vc921x_gsensor_actuating_quantity(vc921x_gsensor_data.xData[0], vc921x_gsensor_data.yData[0], vc921x_gsensor_data.zData[0]); + } + //vc921x_send_orginal_data(&vc921x_dev, &vc921x_gsensor_data, sample_result_info_ptr->slot_result[2].ppg_num); + } + break; + case WORK_MODE_TEMPERATURE: + vc921x_temperature_ntc_calculate(&vc921x_dev); + //vc921x_send_orginal_data(&vc921x_dev, &vc921x_gsensor_data, 1); + //disp_data.temperature_value0 = vc921x_dev.temperature0; + //disp_data.temperature_value1 = vc921x_dev.temperature1; + break; + case WORK_MODE_FACTORY0: + //disp_data.gImx = sample_result_info_ptr->slot_result[0].slot_current; + //disp_data.gPre = sample_result_info_ptr->slot_result[0].u.pre_data; + //disp_data.rImx = sample_result_info_ptr->slot_result[1].slot_current; + //disp_data.rPre = sample_result_info_ptr->slot_result[1].u.pre_data; + //disp_data.ps = sample_result_info_ptr->slot_result[2].u.ps_data; + break; + case WORK_MODE_FACTORY1: + //disp_data.iImx = sample_result_info_ptr->slot_result[0].slot_current; + //disp_data.iPre = sample_result_info_ptr->slot_result[0].u.pre_data; + //disp_data.rImx = sample_result_info_ptr->slot_result[1].slot_current; + //disp_data.rPre = sample_result_info_ptr->slot_result[1].u.pre_data; + break; + case WORK_MODE_HRSPO2: + if (vc921x_drv_is_fifo_event(&vc921x_dev)) + { + ppg_num = sample_result_info_ptr->slot_result[0].ppg_num; + if (ppg_num > 40) + break; + vc921x_get_board_gsensor_data(&gsensor_num, ppg_num); + if (WEAR_STA_HOLD == vc921x_dev.wear) + { + vc921x_dev.heart_rate = vc921x_heart_rate_calculate(&vc921x_dev, 0); + vc921x_dev.spo2 = vc921x_spo2_calculate(&vc921x_dev, 1, 2); + //vc921x_send_orginal_data(&vc921x_dev, &vc921x_gsensor_data, ppg_num); + } + else + { + vc921x_dev.spo2 = 0; + vcSpo2AlgoInit_16bit(); + vcSportMotionAlgoInit(); + vc921x_dev.heart_rate = 0; + Algo_Init(); + } + //disp_data.spo2_val = vc921x_dev.spo2; + //disp_data.hr_rate = vc921x_dev.heart_rate; + } + break; + case WORK_MODE_UNDEFINED: + break; + default: + break; + } + /* 同步处理数据,清理fifo_buffer内存,为下次采样做准备 */ + vc921x_drv_finished_sync(&vc921x_dev); +} + +static const dev_ops vc921x_ops = + { + .dev_init = vc921x_usr_device_init, + .get_dev_info = vc921x_usr_get_dev_info, + .start_work = vc921x_usr_start_work, + .stop_work = vc921x_usr_stop_work, + .soft_reset = vc921x_usr_soft_reset, + .get_events = NULL, + .set_slot_enable = NULL, + .reset_bio_param = vc921x_usr_reset_bio_param, + .get_wear_status = vc921x_usr_get_wear_status, + .dev_irq_handler = vc921x_usr_device_handler, + .dev_check_temp_error = vc921x_usr_check_temperature_abnormal, +}; + +vcare_ppg_device_t *get_vc921x_device(void) +{ + vc921x_dev.ops = &vc921x_ops; + if (vc921x_drv_memroy_alloc()) + { + return NULL; + } + return &vc921x_dev; +} + +/************************************************************************ + * 以下为MCU外设初始化,任务调用 + * example function task_demo_application + ***********************************************************************/ +#if 0 +/* 系统头文件 */ +#include "os_task.h" +//..... +/* 驱动头文件 */ +#include "task_vcare_demo.h" +#include "vc921x_user_app.h" +/* 心率算法头文件 */ +#include "algo.h" + +#define DEMO_TASK_PRIORITY 2 //!< Task priorities +#define DEMO_TASK_STACK_SIZE 1024 * 3 //!< Task stack size +void *demo_task_handle; //!< APP Task handle +void *demo_queue_handle; +static void board_init(void); +void task_demo_application(void *pvParameters) +{ + T_IO_MSG demo_msg; + AlgoSportMode demo_heart_algo_mode = SPORT_TYPE_NORMAL; + unsigned char demo_spo2_algo_mode = 0; + InitParamTypeDef init_config={ 800, WORK_MODE_HR }; + vcare_ppg_device_t *ppg_dev = NULL; + os_msg_queue_create(&demo_queue_handle, 0x10, sizeof(T_IO_MSG)); + /* 通过芯片固定的id寄存器去获取ID,辨别不同的ppg设备,并且获取对应的设备对象 */ + unsigned char id0=0,id1=0; + i2c_read(0x00,&id0,1); + i2c_read(0x01,&id1,1); + if( id0==0x24) /* id0=0x24 */ + { + ppg_dev = get_vc921x_device(); + } + if( ppg_dev ) + { + board_init(); + init_config.work_mode = WORK_MODE_HR; + //init_config.work_mode = WORK_MODE_SPO2; + //init_config.work_mode = WORK_MODE_HRV; + //init_config.work_mode = WORK_MODE_BLOODPRESSURE; + //init_config.work_mode = WORK_MODE_WEAR; + //init_config.work_mode = WORK_MODE_TEMPERATURE; + //init_config.work_mode = WORK_MODE_FACTORY0; + //init_config.work_mode = WORK_MODE_FACTORY1; + ppg_dev->ops->dev_init( &init_config ); + } + while (1) + { + if (os_msg_recv(demo_queue_handle, &demo_msg, 0xFFFFFFFF) == true) + { + switch (demo_msg.subtype) + { + case DEMO_MSG_VC_IRQ_EVENT: + ppg_dev->ops->dev_irq_handler( demo_heart_algo_mode, demo_spo2_algo_mode ); + break; + case DEMO_MSG_KEY_IRQ_EVENT: + //调用初始化切换工作模式,调用ppg_dev->ops->dev_init(...)进行模式切换 + //demo_work_mode_switch(); + break; + case DEMO_MSG_ERR_IRQ: + ppg_device->ops->dev_check_temp_error(); /* 定时校验IC工作状态,并处理异常 */ + break; + default: + break; + } + } + } /* while(1) */ +} + +void task_demo_creat(void) +{ + //example app creat + if (os_task_create(&demo_task_handle, "demo", task_demo_application, NULL, DEMO_TASK_STACK_SIZE, DEMO_TASK_PRIORITY) != true) + { + VCARE_DBG_LOG(" demo_task creat is failed\r\n "); + } +} + +#endif /* end of example function */ diff --git a/code/sdk/zhihui/drivers/rotary_encoder/CMakeLists.txt b/code/sdk/zhihui/drivers/rotary_encoder/CMakeLists.txt index ff6488d05..4b6e69cd2 100644 --- a/code/sdk/zhihui/drivers/rotary_encoder/CMakeLists.txt +++ b/code/sdk/zhihui/drivers/rotary_encoder/CMakeLists.txt @@ -18,15 +18,16 @@ SUPPORT_AT_HX3507 ) else() set(SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/hx3507/hx3507.c - ${CMAKE_CURRENT_SOURCE_DIR}/hx3507/hx3507_hal.c - ${CMAKE_CURRENT_SOURCE_DIR}/hx3507/hx3507_at_cmd.c + ${CMAKE_CURRENT_SOURCE_DIR}/hx3509/tyhx_OTS_hx3509_custom.c + ${CMAKE_CURRENT_SOURCE_DIR}/hx3509/hx3509_hal.c + ${CMAKE_CURRENT_SOURCE_DIR}/hx3509/hx3509_at_cmd.c ) set(PUBLIC_HEADER - ${CMAKE_CURRENT_SOURCE_DIR}/hx3507 + ${CMAKE_CURRENT_SOURCE_DIR}/hx3509 ) set(PUBLIC_DEFINES -SUPPORT_AT_HX3507 +SUPPORT_AT_HX3509 + ) endif() diff --git a/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_at_cmd.c b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_at_cmd.c new file mode 100644 index 000000000..0d11aca38 --- /dev/null +++ b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_at_cmd.c @@ -0,0 +1,104 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-18 10:23:22 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-20 18:45:27 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/rotary_encoder/hx3509/hx3509_at_cmd.c + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ + +// #include "hx3509.h" + + +#include "at_cmd_api.h" +#include "pm_control.h" +#include "debug_print.h" +#include "tyhx_OTS_hx3509_custom.h" +#include "custom_utils.h" + +#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0])) +#define HX3509_MAX_ARGC 8 +#define HX3509_MAX_ARGV_LEN 64 + +#define HX3509_SUCCESS 0 +#define HX3509_FAILED (-1) + +static int ConvertCmdToParam(uint8_t *para, uint32_t paraLen, + uint8_t argv[HX3509_MAX_ARGC][HX3509_MAX_ARGV_LEN], uint32_t* argc) +{ + uint32_t pos = 0; + *argc = 0; + + for (uint32_t i = 0; i < paraLen; i++) { + if ((para[i] == ' ' || para[i] == '\n') && pos != 0) { + argv[*argc][pos] = '\0'; + *argc += 1; + pos = 0; + if (*argc >= HX3509_MAX_ARGC) { + return HX3509_FAILED; + } + } else { + argv[*argc][pos] = para[i]; + pos++; + if (pos >= HX3509_MAX_ARGV_LEN) { + return HX3509_FAILED; + } + } + } + if (pos != 0) { + *argc += 1; + } + + return HX3509_SUCCESS; +} + +static uint8_t g_hx3509_test_started = 0; +static uint32_t hx3509_test(uint8_t *para, uint32_t paraLen, char *strCommonCmd) +{ + UNUSED(strCommonCmd); + uint8_t argv[HX3509_MAX_ARGC][HX3509_MAX_ARGV_LEN] = { 0 }; + uint32_t argc = 0; + + int ret = ConvertCmdToParam(para, paraLen, argv, &argc); + if (ret != HX3509_SUCCESS) { + PRINT("HX3509TEST ATCMD Convert Command Failed\n"); + return ERRCODE_FAIL; + } + + if (argc != 1) { + PRINT("HX3509TEST ATCMD Set Parameter Failed argc %d\n",argc); + return ERRCODE_FAIL; + } + uint8_t start = (uint8_t)atoi((char*)argv[0]); + if (start == 1) + { + // if (g_hx3509_test_started == 1) + // { + // PRINT("SKIP hx3509test start, already started\r\n"); + // return 0; + // } + ots_init(); + // ots_start(); + g_hx3509_test_started = 1; + }else if (start == 2){ + if (g_hx3509_test_started == 0) + { + PRINT("SKIP hx3509test stop, already stopped\r\n"); + return 0; + } + ots_stop(); + g_hx3509_test_started = 0 ; + }else{ + custom_exit_lpm(); + } + return ERRCODE_SUCC; +} +static const at_cmd_table_t g_hx3509_cmd[] = { + { (char *)"AT^HX3509TEST", hx3509_test, (char *)"1" }, +}; + +at_cmd_table_t *get_hx3509_cmd_table(uint32_t *cmd_count) +{ + *cmd_count = ARRAY_COUNT(g_hx3509_cmd); + return (at_cmd_table_t *)g_hx3509_cmd; +} diff --git a/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_at_cmd.h b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_at_cmd.h new file mode 100644 index 000000000..20eadf83c --- /dev/null +++ b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_at_cmd.h @@ -0,0 +1,40 @@ +/* + * @Author: penglei 1624952103@qq.com + * @Date: 2024-12-18 10:23:22 + * @LastEditors: penglei 1624952103@qq.com + * @LastEditTime: 2024-12-18 10:32:32 + * @FilePath: /sdk615/code/sdk/drivers/peripheral/rotary_encoder/hx3509/hx3509_at_cmd.h + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +/** + * Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved. \n + * + * Description: Provides at cmd register func \n + * Author: @CompanyNameTag \n + * History: \n + * 2022-07-20, Create file. \n + */ +#ifndef HX3509_AT_PROCESS_H +#define HX3509_AT_PROCESS_H + +#include +#include +#include "errcode.h" +#include "soc_osal.h" +#include "at_cmd_api.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +at_cmd_table_t *get_hx3509_cmd_table(uint32_t *cmd_count); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif diff --git a/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_hal.c b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_hal.c new file mode 100644 index 000000000..3ed4e00f3 --- /dev/null +++ b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_hal.c @@ -0,0 +1,131 @@ +#include "at_cmd_api.h" +#include "pm_control.h" +#include "tyhx_OTS_hx3509_custom.h" +#include "rotary_encoder_def.h" +#include "i2c.h" +#include "debug_print.h" +#include "hal_i2c.h" + +static uint8_t g_hx3509_test_started = 0; +encoder_callback g_encoder_callback = NULL; +uint32_t hx3509_start(void) +{ + if (g_hx3509_test_started == 1) + { + re_print("SKIP hx3509test start, already started\r\n"); + return 0; + } + crown_init(); + crown_start(); + g_hx3509_test_started = 1; + return ERRCODE_SUCC; +} +uint32_t hx3509_stop(void) +{ + if (g_hx3509_test_started == 0) + { + re_print("SKIP hx3509test stop, already stopped\r\n"); + return 0; + } + crown_stop(); + g_hx3509_test_started = 0 ; + return ERRCODE_SUCC; +} + +static ext_errno hx3509_encoder_init(void *attr) +{ + re_print("hx3509_encoder_init\r\n"); + return EXT_ERR_SUCCESS; +} +static ext_errno hx3509_encoder_deinit(void) +{ + re_print("hx3509_encoder_deinit\r\n"); + return EXT_ERR_SUCCESS; +} +static ext_errno hx3509_encoder_get_encoderinfo(uint8_t *data, uint8_t data_len) +{ + + if (data_len != sizeof(re_input_event_info)) + return EXT_ERR_NOT_SUPPORT; + re_input_event_info *encoder_info = (re_input_event_info*)data; + int16_t diff = get_crown_result(); + int16_t stand_still = get_stand_still(); + // re_print("hx3509_encoder_get_encoderinfo diff %d stand_still %d\n",diff,stand_still); + if (stand_still == 1 ) + { + encoder_info->re_event = MC_RE_COVER; + }else { + encoder_info->re_event = MC_RE_MOVE; + } + encoder_info->re_event_info.x_axis[0] = diff; + encoder_info->re_event_info.y_axis[0] = diff; + encoder_info->re_event_info.z_axis[0] = diff; + encoder_info->finger_id = 0 ; + encoder_info->point_num = 0 ; + encoder_info->tv_usec = (uint32_t)uapi_tcxo_get_ms(); + //re_print("hx3509_encoder_get_encoderinfo %s rotate %d\r\n",encoder_info->re_event == MC_RE_SLIDE_UP?"MC_RE_SLIDE_UP":"MC_RE_SLIDE_DOWN",diff); + + return EXT_ERR_SUCCESS; +} +static ext_errno hx3509_encoder_suspend(void) +{ + re_print("hx3509_encoder_suspend\r\n"); + return EXT_ERR_SUCCESS; +} +static ext_errno hx3509_encoder_resume(void) +{ + re_print("hx3509_encoder_resume\r\n"); + hx3509_start(); + return EXT_ERR_SUCCESS; +} +static ext_errno hx3509_encoder_sleep(void) +{ + re_print("hx3509_encoder_sleep\r\n"); + hx3509_stop(); + return EXT_ERR_SUCCESS; +} +static ext_errno hx3509_encoder_reset(void) +{ + re_print("hx3509_encoder_reset\r\n"); + return EXT_ERR_SUCCESS; +} +static ext_errno hx3509_encoder_bus_init(void *private_data) +{ + return EXT_ERR_SUCCESS; +} +static ext_errno hx3509_register_callback(void *peri_data, encoder_callback func) +{ + re_print("hx3509_register_callback\r\n"); + g_encoder_callback = func; + return EXT_ERR_SUCCESS; +} +static ext_errno hx3509_unregister_callback(void *peri_data) +{ + re_print("hx3509_unregister_callback\r\n"); + g_encoder_callback = NULL; + return EXT_ERR_SUCCESS; +} + +encoder_peripheral_api g_hx3509_api = { + .encoder_init = hx3509_encoder_init, + .encoder_deinit = hx3509_encoder_deinit, + .encoder_get_encoderinfo = hx3509_encoder_get_encoderinfo, + .encoder_suspend = hx3509_encoder_suspend, + .encoder_resume = hx3509_encoder_resume, + .encoder_sleep = hx3509_encoder_sleep, + .encoder_reset = hx3509_encoder_reset, + .encoder_bus_init = hx3509_encoder_bus_init, + .register_callback = hx3509_register_callback, + .unregister_callback = hx3509_unregister_callback, +}; + + +// void *rotary_encoder_get_api(void) +// { +// return ((void *)&g_hx3509_api); +// } + +// void *rotary_encoder_get_peri_attr(void) +// { +// return ((void *)NULL); +// } \ No newline at end of file diff --git a/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_hal.h b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_hal.h new file mode 100644 index 000000000..9401ff04d --- /dev/null +++ b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/hx3509_hal.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved. \n + * + * Description: Provides at cmd register func \n + * Author: @CompanyNameTag \n + * History: \n + * 2022-07-20, Create file. \n + */ +#ifndef HX3507_HAL_H +#define HX3507_HAL_H + +#include +#include +#include "errcode.h" +#include "soc_osal.h" +#include "rotary_encoder_def.h" +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +// void *rotary_encoder_get_api(void); +// void *rotary_encoder_get_peri_attr(void); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif diff --git a/code/sdk/zhihui/drivers/rotary_encoder/hx3509/tyhx_OTS_hx3509_custom.c b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/tyhx_OTS_hx3509_custom.c new file mode 100644 index 000000000..bfc9a9207 --- /dev/null +++ b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/tyhx_OTS_hx3509_custom.c @@ -0,0 +1,687 @@ +/** + * Copyright (c) 2014 - 2022, 南京天易合芯电子有限公司 + * + * All rights reserved. + * + */ + +/***************************************************************************** + * @file TYHX_OTS_HX3509_CUSTOM.C + * @author TYHX team + * @product saker + * @version V0.1 + * @date 2024-2-19 + * @brief 用户平台处理的接口函数 + * @encode WINDOWS ANSI 编码模式可以看中文注释 + +******************************************************************************/ + +#include +#include +#include +#include +#include +#include "tyhx_OTS_hx3509_custom.h" +#include "cmsis_os2.h" +#include "custom_utils.h" +#include "hal_gpio.h" +#include "pinctrl_porting.h" +// #include "tyhx_hrs_custom.h" +//平台头文件包含定义 +#ifdef NORDIC_SDK +//本示例程序NORDIC_SDK采用的SDK版本基于nrf5_sdk16.0.0_98a08e2 +#include "nordic_common.h" +#include "nrf.h" +#include "ble_err.h" +#include "app_timer.h" +#include "nrf_soc.h" +#include "nrf_delay.h" +#include "app_scheduler.h" +#include "nrf_drv_gpiote.h" +#include "nrf_gpio.h" +#include "tyhx_common.h" +#else //other SDK +//include file add by custom +//custom to do here +#endif //NORDIC_SDK + +#define HAISI_SDK + +#ifdef CHIP_HX3509 +//下面是NORDIC平台用到的所有函数,其他平台由用户自行添加 +#ifdef HAISI_SDK +// APP_TIMER_DEF(g_hx3509_timer); //定义OTS 读写间隔定时器 +void ots_timer_handler(void * p_context); + +#define REPORT_MIN 2 // 这个是最小上报角度, 按照平均值预估的值, 由客户定义, 看多大的数据上报给UI处理一次 +static int16_t ots_result = 0;//ots结果 + +bool ots_timer_running = false; //ots定时器是否运行标志 + +static osTimerId_t g_hx3509_timer = NULL; + +/*--------以下为定时函数相关定义 建立/启动/停止定时器//定时中断处理-----------*/ + +/** + * OTS 定时器创建函数 + * @param NULL + */ +void ots_timer_creat(void) +{ + // app_timer_create(&g_hx3509_timer, APP_TIMER_MODE_REPEATED, ots_timer_handler); + g_hx3509_timer = osTimerNew((osTimerFunc_t)ots_timer_handler, osTimerPeriodic, NULL, NULL); +} + +/** + * 定时器start函数 + * @param NULL + */ +void ots_timer_start(void) +{ + if(ots_timer_running == false) + { + // app_timer_start(g_hx3509_timer, APP_TIMER_TICKS(50), NULL);//定义为50ms定时器间隔 + osTimerStart(g_hx3509_timer,50); + ots_timer_running = true; + } +} + +/** + * 定时器stop函数 + * @param timerid 是对应的定时器; + */ +void ots_timer_stop(void) +{ + // app_timer_stop(g_hx3509_timer); + osTimerStop(g_hx3509_timer); + ots_timer_running = false; +} +/** + * 定时器超时处理函数 + * @param p_context 是对应的定时器; + */ +void ots_timer_handler(void * p_context) +{ + //UNUSED_PARAMETER(p_context); + ots_data_handler(NULL,0); +} +/*----------------------以上为定时函数相关定义 建立/启动/停止定时器//定时中断处理---------- +---------------------*/ + +// 定义一个用于检测ACK信号的函数,返回1表示收到ACK,返回0表示未收到ACK +int crown_check_ack(void) +{ + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + LOS_Udelay(COM_DELAY); + + // 将SCLK引脚拉高,以便从设备在这个时钟周期内返回ACK信号(假设ACK在SCLK高电平期间有效) + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_HIGH); + LOS_Udelay(COM_DELAY); + + // 读取SDIO引脚的电平状态,判断是否为低电平(低电平表示收到ACK) + if (uapi_gpio_get_val(PIN_TYHX_SDIO) == GPIO_LEVEL_LOW) { + return 1; // 收到ACK,返回1 + } + + return 0; // 未收到ACK,返回0 +} + +/*---------------------------以下为OTS 的寄存器读写,初始化/开始/结束------------------------*/ +/** + * 通信读寄存器值函数 + * @param regaddr, hx3507的寄存器地址 + * LOS_Udelay(COM_DELAY), 读写函数的delay 按照实际平台调整, HX3507 通信sclk 最高支持1MHZ频率. + * 在最高频率的范围内, 尽量减小delay, 减少定时器周期读取的时间. + */ +uint8_t crown_read_reg(uint8_t deviceAddr, uint8_t regaddr) +{ + uint8_t readvalue = 0; + // 将传入的7位设备地址左移1位,并设置最低位为1表示读操作 + uint8_t addrWithRwBit = (deviceAddr << 1) | 0x00; + // TYHX_LOG("regaddr 0x%x\r\n",regaddr); + + // regaddr &= 0x7f; + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_LOW); + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + uapi_tcxo_delay_us(COM_DELAY); + + // 先发送设备地址(包含读写位) + for(int i = 0; i < 8; i++) + { + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + + if(addrWithRwBit & 0x80) + { + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_HIGH); + } + else + { + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_LOW); + } + //uapi_tcxo_delay_us(COM_DELAY); + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_HIGH); + + addrWithRwBit <<= 1; + // uapi_tcxo_delay_us(COM_DELAY); + } + + // 发送寄存器地址 + for(int i = 0; i < 8; i++) + { + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + + if(regaddr & 0x80) + { + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_HIGH); + } + else + { + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_LOW); + } + //uapi_tcxo_delay_us(COM_DELAY); + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_HIGH); + + regaddr <<= 1; + // uapi_tcxo_delay_us(COM_DELAY); + } + + // // addrWithRwBit = (0x0 << 1) | 0x01; + + // addrWithRwBit = 0; + // printf("addrWithRwBit :0x%x \n",addrWithRwBit); + + // // 先发送设备地址(包含读写位) + // for(int i = 0; i < 8; i++) + // { + // uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + + // if(addrWithRwBit & 0x80) + // { + // uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_HIGH); + // } + // else + // { + // uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_LOW); + // } + // // uapi_tcxo_delay_us(COM_DELAY); + // uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_HIGH); + + // addrWithRwBit <<= 1; + // // uapi_tcxo_delay_us(COM_DELAY); + // } + + // 开始读操作,HOST要把SDIO从输出端口改为输入端口. + uapi_gpio_set_dir(PIN_TYHX_SDIO,GPIO_DIRECTION_INPUT); + uapi_pin_set_pull(PIN_TYHX_SDIO, (pin_pull_t)HAL_PIO_PULL_UP); + uapi_pin_set_ie(PIN_TYHX_SDIO,HAL_PIO_IE_ENBALE); + // LOS_Udelay(COM_DELAY); + //nrf_gpio_pin_clear(PIN_TYHX_SCLK); + //uapi_tcxo_delay_us(COM_DELAY); + + for(int i = 0; i < 8; i++) + { + readvalue <<= 1; + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + + //uapi_tcxo_delay_us(COM_DELAY); + + //uapi_tcxo_delay_us(COM_DELAY); + + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_HIGH); + + if(uapi_gpio_get_val(PIN_TYHX_SDIO)) + { + readvalue |= 0x1; + } + //uapi_tcxo_delay_us(COM_DELAY); + + } + uapi_gpio_set_dir(PIN_TYHX_SDIO,GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_LOW); + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + // TYHX_LOG("readvalue 0x%x\r\n",readvalue); + return readvalue; +} + + +void crown_write_reg(uint8_t deviceAddr, uint8_t regaddr, uint8_t regvalue) +{ + // TYHX_LOG("regaddr 0x%x regvalue : 0x%x\r\n",regaddr,regvalue); + uint16_t writevalue; + // 将7位设备地址左移1位,并设置最低位为0表示写操作 + uint8_t addrWithRwBit = (deviceAddr << 1) | 0x00; + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_LOW); + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + LOS_Udelay(COM_DELAY); + + // 先发送设备地址(包含读写位) + for(int i = 0; i < 8; i++) + { + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + + if(addrWithRwBit & 0x80) + { + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_HIGH); + } + else + { + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_LOW); + } + //uapi_tcxo_delay_us(COM_DELAY); + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_HIGH); + + addrWithRwBit <<= 1; + } + // if (!crown_check_ack()) { + // printf("write check error \n"); + // return ; + // } + + //写通信协议, 最高位置1, 循环16次依次送出寄存器和值,在SCLK的上升沿前准备好数据 + writevalue= (uint16_t)((regaddr|0x80)<<8) + regvalue; + + for(int i=0; i<16; i++) + { + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + + if(writevalue & 0x8000) + { + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_HIGH); + } + else + { + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_LOW); + + } + //uapi_tcxo_delay_us(COM_DELAY); + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_HIGH); + + writevalue<<=1; + } + uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_LOW); + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + + return; +} + +uint8_t ots_read_reg(uint8_t regaddr) +{ + uint8_t data_buf = 0; + //add by user, i2c read function + // tyhx_read_regs(I2C_DEV_7ADDR,regaddr,&data_buf,1); + // custom_i2c_read_regs(I2C_DEV_7ADDR,regaddr,1,&data_buf,1,HX3509_I2C_BUS); + data_buf = crown_read_reg(I2C_DEV_7ADDR,regaddr); + return data_buf; //返回读到的寄存器值 +} + + +void ots_write_reg(uint8_t regaddr, uint8_t regvalue) +{ + //add by user, i2c write function + // tyhx_write_reg(I2C_DEV_7ADDR,regaddr,®value); + crown_write_reg(I2C_DEV_7ADDR,regaddr,regvalue); + return; +} + +void ots_data_handler(void *p_event_data, uint16_t event_size) +{ + int16_t deltax = 0; + int16_t deltay = 0; + uint8_t sleepstatus = 0; + int16_t result,report = 0; //djd0305 + //TYHX_LOG("[ots_data_handler]\r\n");// + //nrf_gpio_pin_read(PIN_TYHX_MOT)==false 读取到的MOT 管脚为低电平 + // if((ots_read_reg(RA_MOTION_BIT)&0x80)||(nrf_gpio_pin_read(PIN_TYHX_MOT)==false)) + if((ots_read_reg(RA_MOTION_BIT)&0x80)) + { + //X Y的数据必须全部被读出,但是我们只需要X的结果来判断方向和速度 + deltax = ots_read_reg(RA_ACC_DELTA_X_LOW) |(ots_read_reg(RA_ACC_DELTA_X_HIG)<<8); + deltay = ots_read_reg(RA_ACC_DELTA_Y_LOW) |(ots_read_reg(RA_ACC_DELTA_Y_HIG)<<8); + TYHX_LOG("Deltay = %d \r\n",deltay);//djd0305 + //用户添加程序, deltay 是表冠产生数据, 上报给UI, 如果需要数据变化, + // 积攒到一定值再上报UI, 可以调用 ots_result_deal()函数处理. + } + else + { + //debug的时候, 可能用到, 看一下图像质量 + TYHX_LOG("pic qulity =%d, agc num=%d\r\n",ots_read_reg(RA_DATA_2AGC),ots_read_reg(RA_EXP_NUM_SEL)&0x7F); + } +// 下面是三种模式 处理 +#if (defined(MOT_LOW_LEVEL_MODE))//如果低电平中断模式,要读到芯片MOT变高 + if(nrf_gpio_pin_read(PIN_TYHX_MOT))//低电平模式需要数据读空, MOT才会恢复高电平 + { + TYHX_LOG("PIN_TYHX_MOT is high \r\n");//MOT 变高后关闭读定时器 + ots_timer_stop(); + } +#endif + +#if (defined(MOT_FALLINGTRI_MODE))//脉冲模式中断 + if(nrf_gpio_pin_read(PIN_TYHX_MOT))//读完数据, MOT应该会恢复默认电平 + { + TYHX_LOG("ots mot is cleared\r\n");//HX3509进入sleep后关闭定时器 + } +#endif +//如果是MOT_NO_USE_MODE , 由用户程序处理,在不需要芯片时候关闭读50ms定时器 + + +} + +//MOT 中断处理程序, 这个是根据平台程序处理, 用户可以自行修改 +//这里加中断发生标志位或者把中断处理推送到其他任务队列, +// 不建议在中断处理函数中加入过多的程序处理 +#if (defined(MOT_LOW_LEVEL_MODE) || defined(MOT_FALLINGTRI_MODE)) +/** + * OTS 初始化,包括IO处理,上电时序,定时器定义等; + * @param 无 +*/ +void ots_mot_init(void) +{ +#if (defined(MOT_LOW_LEVEL_MODE) || defined(MOT_FALLINGTRI_MODE)) + nrf_drv_gpiote_in_config_t config_ots = GPIOTE_CONFIG_IN_SENSE_HITOLO(false); + config_ots.pull = NRF_GPIO_PIN_PULLUP; + nrf_drv_gpiote_in_init(PIN_TYHX_MOT, &config_ots, ots_mot_handler); + nrf_drv_gpiote_in_event_enable(PIN_TYHX_MOT, true); + +#endif +} +void ots_mot_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) +{ + uint32_t err_code; + bool mothitolow = false; + + nrf_drv_gpiote_in_uninit(PIN_TYHX_MOT); + if (action == NRF_GPIOTE_POLARITY_HITOLO) + { + mothitolow = true;// 下降沿中断有效, NORDIC没有低电平中断。 + nrf_drv_gpiote_in_config_t config_ots = GPIOTE_CONFIG_IN_SENSE_LOTOHI(false); + err_code = nrf_drv_gpiote_in_init(PIN_TYHX_MOT, &config_ots, ots_mot_handler); + APP_ERROR_CHECK(err_code); + config_ots.pull = NRF_GPIO_PIN_PULLUP; + nrf_drv_gpiote_in_event_enable(PIN_TYHX_MOT, true); + } + else + { + mothitolow = false; + nrf_drv_gpiote_in_config_t config_ots = GPIOTE_CONFIG_IN_SENSE_HITOLO(false); + err_code = nrf_drv_gpiote_in_init(PIN_TYHX_MOT, &config_ots, ots_mot_handler); + APP_ERROR_CHECK(err_code); + config_ots.pull = NRF_GPIO_PIN_PULLUP; + nrf_drv_gpiote_in_event_enable(PIN_TYHX_MOT, true); + } + nrf_gpio_pin_latch_clear(PIN_TYHX_MOT); + if(mothitolow) + { + TYHX_LOG("mot interrupt! \r\n"); + mothitolow = false; + //调用读取数据处理函数 + app_sched_event_put(NULL, 0, ots_data_handler); +#ifdef MOT_LOW_LEVEL_MODE + ots_timer_start();//低电平中断需要读空数据, 低电平才会恢复 +#endif + } + +} + + +#endif + +/** + * 通信重新同步函数, 作用是防止因为其他干扰而引起的错乱 + * @param NULL; + */ +void crown_com_resync(void) +{ + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_HIGH); + uapi_tcxo_delay_us(5); + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + uapi_tcxo_delay_us(5); + uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_HIGH); + uapi_tcxo_delay_us(5); +} + +/** + * OTS 初始化,包括IO处理,上电时序,定时器定义等; + * @param 无 +*/ +void ots_init(void) +{ + //此时要保证芯片已经正常上电 + // uapi_gpio_set_dir(PIN_TYHX_SDIO,GPIO_DIRECTION_OUTPUT); + + // uapi_gpio_set_val(PIN_TYHX_SDIO,GPIO_LEVEL_LOW); + + // uapi_gpio_set_dir(PIN_TYHX_SCLK,GPIO_DIRECTION_OUTPUT); + + // uapi_gpio_set_val(PIN_TYHX_SCLK,GPIO_LEVEL_LOW); + + // uapi_tcxo_delay_us(COM_DELAY); + // crown_com_resync(); + int i; + for(i=0; i<10; i++)//重复读DEVICE ID 是否等于0X35 + { + if(ots_read_reg(RA_DEFINE_PART_ID) == 0x35) + { + TYHX_LOG("read chip id is 0x35\r\n",i); + break; + } + else + { + TYHX_LOG("read chip id is 0x%x\r\n",ots_read_reg(RA_DEFINE_PART_ID)); + } + // if(i<7)//读到ID不对, 复位通信 + // { + // crown_com_resync(); + // } + + } + if(i==10) + { + TYHX_LOG("HX3509 communication err, please check\r\n"); + return; + } + + +#if (defined(MOT_LOW_LEVEL_MODE) || defined(MOT_NO_USE_MODE)) + ots_timer_creat(); +#else + ots_mot_init(); +#endif +} +void hx3509l_calibration(void) +{ + uint8_t cali75,cali74; + LOS_Mdelay(2);//必须有这个delay + ots_write_reg(RA_DEBUG_CAS0,0x41); + cali75 = ots_read_reg(0x75); + cali74 = ots_read_reg(0x74); + + if((cali75!=0x84) || (cali74 < 0x42)) + { + ots_write_reg(RA_DEBUG_CAS0,0x41); + LOS_Mdelay(2); + ots_write_reg(RA_DEBUG_CAS0,0x40); + ots_write_reg(0x78,0x20); + ots_write_reg(0x75,0x84); + ots_write_reg(0x74,0x78); + ots_write_reg(0x73,0x64); + ots_write_reg(0x72,0xBE); + LOS_Mdelay(10); + ots_write_reg(RA_DEBUG_CAS0,0x41); + } +} +/** + * 开启OTS; + * @param NULL +*/ +void ots_start(void) +{ + //bool ret = true; + ots_write_reg(RA_DEBUG_CAS0,0x43); + LOS_Mdelay(2); //delay about 1ms + ots_write_reg(RA_DEBUG_CAS0,0x42); + ots_write_reg(RA_RW_REG_ADDR_60H,0x04); // 通信电压采用1.8V打开这个; + //ots_write_reg(RA_RW_REG_ADDR_60H,0x00); //通信电压采用3.3V打开这个; 也是默认值. + LOS_Mdelay(30); //delay about 30 ms + + ots_write_reg(RA_PRF_NUM_HIG_LOW,0x10); + ots_write_reg(RA_PRF_NUM_HIG_HIG,0x00); + ots_write_reg(RA_NO_MOVE_NUM_HIG_RUN,0x3f); + + ots_write_reg(RA_PRF_NUM_TYP_LOW,0x12);//0x20= 1khz,0x1e = 1.1khz 0x1a = 1.2khz, 0x15 = 1.5khz, + ots_write_reg(RA_PRF_NUM_TYP_HIG,0x00); + + ots_write_reg(RA_PRF_NUM_SLP1_LOW,0x40); + ots_write_reg(RA_PRF_NUM_SLP1_HIG,0x00);//0600 = 46ms,0100 = 8ms,0200 = 16ms 0080 = 4ms + //set slp2 enter time = 0x439*slp1 prf time(0x600*31.25us) + ots_write_reg(RA_NO_MOVE_NUM_SLP1_LOW,0xa8); + ots_write_reg(RA_NO_MOVE_NUM_SLP1_HIG,0x03); + + ots_write_reg(RA_NO_MOVE_NUM_TYP_RUN,0x60); + ots_write_reg(RA_CORR_MAX_EN,0x00); + + //AGC cfg + ots_write_reg(RA_EXP_NUM_SEL,0x90); //agc 0x10=close 0x90=open + ots_write_reg(RA_LED_ISEL_CFG,0x04); //led sel + ots_write_reg(RA_AGC_STEP,0x51); +#if 0// 如果冠轴结构设计摆动角度很大(>5°), 请改成#if 1 + ots_write_reg(RA_Y_HISTORY_CTRL,0xc9); +#endif + ots_write_reg(RA_AGC_EXPECT,0x7a); + ots_write_reg(RA_AGC_THRES_MAX,0x7a+50); + ots_write_reg(RA_AGC_THRES_MIN,0x7a-50); + + ots_write_reg(RA_BUTTON_CTRL,0x0c); + + +#if defined(MOT_LOW_LEVEL_MODE) + ots_write_reg(RA_INT_CTRL,0x17); + //ots_write_reg(RA_RW_REG_ADDR_62H,0x38); +#elif defined(MOT_FALLINGTRI_MODE) + ots_write_reg(RA_INT_CTRL,0x15); + ots_write_reg(RA_INT_WIDTH_POS,0x4e);//设置为50hz 低脉冲为500us的波形 + ots_write_reg(RA_INT_WIDTH_NEG,0x02); + //ots_write_reg(RA_RW_REG_ADDR_62H,0x38); +#elif defined(MOT_NO_USE_MODE) + ots_write_reg(RA_INT_CTRL,0x71); //MOT disable +#endif + ots_write_reg(0x78,0x00); + ots_write_reg(RA_CORR_MAX_15_8,0x03); //02 + ots_write_reg(RA_CORR_MAX_7_0,0x00); //60 + //LOS_Mdelay(10);//delay 10ms + ots_write_reg(RA_OP_MODE,0x1d); + LOS_Mdelay(10); + ots_write_reg(RA_DEBUG_CAS0,0x43); + hx3509l_calibration(); + +#ifdef MOT_NO_USE_MODE // 如果是非中断模式, 初始后, 开启定时器; + ots_timer_start(); +#endif + +} +/** + * 停止OTS 的工作, 并进入power down, 功耗<4uA + * @param NULL +*/ +void ots_stop(void) +{ +#ifdef MOT_NO_USE_MODE + ots_timer_stop(); + ots_result = 0; + ots_timer_stop(); + ots_write_reg(RA_DEBUG_CAS0,0x20); //power down + //LOS_Mdelay(20); + TYHX_LOG("[ots_stop]\r\n"); + +#endif +} +/** + *OTS 进入超低功耗状态, 强行进入SLEEP2模式, 功耗大约<6uA; + * @param NULL +*/ +void ots_ultra_low_power(void) +{ +#ifdef MOT_NO_USE_MODE + ots_result = 0; + ots_timer_stop(); + ots_write_reg(RA_PRF_NUM_SLP2_LOW,0xff); + ots_write_reg(RA_PRF_NUM_SLP2_HIG,0xff); + ots_write_reg(RA_INT_CTRL,0x71); //disable int + + ots_write_reg(RA_OP_MODE,0x5d); + ots_write_reg(RA_DSP_EN,0x01); + ots_write_reg(RA_LED_ISEL_CFG,0x00); + LOS_Mdelay(20); + TYHX_LOG("[ots_ultra_low_power]\r\n"); + +#endif +} + + + + +/** + * OTS 工厂测试函数, 此函数测试chip id, 读efuse, 用于工厂厂测模块调用 + * @param NULL + * 返回值false 检测失败, true检测成功. +*/ +bool ots_factory_test(void) +{ + uint8_t efuse74,efuse75,chip_id,agc_num; + chip_id = ots_read_reg(RA_DEFINE_PART_ID); + efuse75 = ots_read_reg(0x75); + efuse74 = ots_read_reg(0x74); + agc_num = ots_read_reg(RA_EXP_NUM_SEL)&0x7F; + if((chip_id != 0x35)||(efuse75!=0x84) || (efuse74 < 0x42)||(agc_num==0)) + { + TYHX_LOG("chip id = 0x%x, efuse75 = 0x%x,efuse74=0x%x,agc_num=%d \r\n",chip_id, efuse75,efuse74,agc_num);//djd0315 + return false; + } + return true; +} + + +/** + * OTS 的数据结果保存, 结果代表角度 + * @param NULL +*/ +void ots_result_deal(int16_t x) +{ +#if 0// 如果冠轴结构设计摆动角度很大(>5°), 请改成#if 1 + if(abs(x)<=FILTER_MIN ) + return; +#endif + + if(abs(x)>=4 ) + { + ots_result += x/4; //大于等于4的值除以4, 代表角度, 数字4 在匀速转动中大约等于1° + } + else //出值2,3 默认大约为1° + { + if(x>0) + { + ots_result+=1; //2~4 默认一个单位, 1° + } + else + { + ots_result-=1; // + } + } + if(abs(ots_result)>=REPORT_MIN)//最小的值由用户定义, 就是得到值大于一定的值, 再上报UI + { + //OTS结果上报UI, 因为这里是定时读取的函数, 可以把结果上报 + ots_result = 0; + } +} + +#else //其他平台,由用户自行添加上面对应的程序 + +//custom to do here + +#endif + + +/*---------------------------以上为OTS 的寄存器读写,初始化/开始/结束------------------------*/ + +#endif + + diff --git a/code/sdk/zhihui/drivers/rotary_encoder/hx3509/tyhx_OTS_hx3509_custom.h b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/tyhx_OTS_hx3509_custom.h new file mode 100644 index 000000000..a2a464ec4 --- /dev/null +++ b/code/sdk/zhihui/drivers/rotary_encoder/hx3509/tyhx_OTS_hx3509_custom.h @@ -0,0 +1,189 @@ +/** + * Copyright (c) 2014 - 2022, 南京天易合芯电子有限公司 + * + * All rights reserved. + * +*/ + +/*************************************************************************** + * @file TYHX_OTS_hx3509_CUSTOM.h + * @author TYHX team + * @product saker + * @version V0.1 + * @date 2023-11-29; + +****************************************************************************/ +#ifndef _TYHX_OTS_hx3509_CUSTOM_H_ +#define _TYHX_OTS_hx3509_CUSTOM_H_ + +#define CHIP_HX3509 //使用OTS芯片的定义 + +#define I2C_DEV_7ADDR 0x32 //7bit i2c device addr + +#define PIN_TYHX_SDIO S_AGPIO_L12 //custom to do here +#define PIN_TYHX_SCLK S_AGPIO_L13 //custom to do here +/*----------------------以下为用户平台定义区-------------------------------*/ +// #define NORDIC_SDK //NORDIC 52840 +//#define OTHER_SDK // +/*----------------------以上为用户平台定义区-------------------------------*/ + +//MOT管脚使用定义 +//#define MOT_LOW_LEVEL_MODE //MOT使用低电平模式 +//#define MOT_FALLINGTRI_MODE //MOT 使用脉冲模式 +#define MOT_NO_USE_MODE //用户不使用MOT 管脚,推荐模式 + + +#define COM_DELAY 5 // 通信中可能用到的延时 +#define FILTER_MIN 1 // OTS出数, 小于这个的滤掉 + +/*----------------------以下为LOG打印函数, 用户需要改成自己平台的-------------------------------*/ +//log 打印定义 +#define TYHX_LOG_ENABLE +#ifdef TYHX_LOG_ENABLE +#define TYHX_LOG(fmt, ...) \ + do { \ + printf(fmt" ", ##__VA_ARGS__); \ + } while (0) +#else +#define TYHX_LOG(...) +#endif //TYHX_LOG_ENABLE + +/*----------------------以上为LOG打印函数-------------------------------*/ + + +//寄存器定义 +#define RA_DEFINE_PART_ID 0x00 +#define RA_DEFINE_REV_ID 0x01 +#define RA_MOTION_BIT 0x02 +#define RA_OP_MODE 0x03 +#define RA_DSP_EN 0x04 +#define RA_OP_STATUS 0x05 +#define RA_ACC_DELTA_X_LOW 0x06 +#define RA_ACC_DELTA_Y_LOW 0x07 +#define RA_ACC_DELTA_X_HIG 0x08 +#define RA_ACC_DELTA_Y_HIG 0x09 +#define RA_PRF_NUM_HIG_LOW 0x0a +#define RA_PRF_NUM_HIG_HIG 0x0b +#define RA_PRF_NUM_TYP_LOW 0x0c +#define RA_PRF_NUM_TYP_HIG 0x0d +#define RA_PRF_NUM_SLP1_LOW 0x0e +#define RA_PRF_NUM_SLP1_HIG 0x0f +#define RA_PRF_NUM_SLP2_LOW 0x10 +#define RA_PRF_NUM_SLP2_HIG 0x11 +#define RA_NO_MOVE_NUM_HIG_RUN 0x12 +#define RA_NO_MOVE_NUM_TYP_RUN 0x13 +#define RA_NO_MOVE_NUM_SLP1_LOW 0x14 +#define RA_NO_MOVE_NUM_SLP1_HIG 0x15 +#define RA_MOVE_NUM_EXIT_SLP 0x16 +#define RA_EXP_NUM_SEL 0x17 +#define RA_AGC_EXPECT 0x18 +#define RA_AGC_STEP 0x19 +#define RA_AGC_THRES_MAX 0x1a +#define RA_AGC_THRES_MIN 0x1b +#define RA_EXP_NUM_SEL_AGC_MAX 0x1c +#define RA_LED_ISEL_CFG 0x1d +#define RA_CORR_MAX_EN 0x1e +#define RA_CORR_MAX_7_0 0x1f +#define RA_CORR_MAX_15_8 0x20 +#define RA_CORR_DIFF_MIN_7_0 0x21 +#define RA_CORR_DIFF_MIN_9_8 0x22 +#define RA_Y_HISTORY_CTRL 0x23 +#define RA_Y_FRAC_CTRL 0x24 +#define RA_REF_HOLD_CTRL 0x25 +#define RA_MOV_LP_CTRL 0x26 +#define RA_INT_WIDTH_POS 0x27 +#define RA_INT_WIDTH_NEG 0x28 +#define RA_INT_WIDTH_BTN_SLP 0x29 +#define RA_EXIT_SLP_CTRL 0x2a +#define RA_EXIT_SLP_ABS_DELTA_XY_TH 0x2b +#define RA_DELTA_X_OUT_SLP 0x2c +#define RA_DELTA_Y_OUT_SLP 0x2d +#define RA_CORR_DATA_SLP_L 0x2e +#define RA_CORR_DATA_SLP_H 0x2f +#define RA_BUTTON_CTRL 0x30 +#define RA_POLL_NUM 0x31 +#define RA_BTN_HIGH_TH 0x32 +#define RA_BTN_DN_CNT_TH 0x33 +#define RA_BTN_LOW_TH 0x34 +#define RA_BTN_UP_CNT_TH 0x35 +#define RA_BTN_ABS_ACC_Y_THRES 0x36 +#define RA_BTN_PAIR_TIMEOUT 0x37 +#define RA_BTN_UP_DN_CNT 0x38 +#define RA_POLL_ACC_DELTA_X_OUT 0x39 +#define RA_POLL_ACC_DELTA_Y_OUT 0x3a +#define RA_INT_CTRL 0x3b +#define RA_INT_CTRL1 0x3c +#define RA_BURST_TX_DATA 0x40 +#define RA_INDEX_DATA 0x41 +#define RA_COL_DATA_MUX 0x42 +#define RA_DATA_2AGC 0x43 +#define RA_IMG_QA 0x44 +#define RA_SNR 0x45 +#define RA_DELTA_X 0x46 +#define RA_DELTA_Y 0x47 +#define RA_DELTA_Y_FRAC 0x48 +#define RA_DSP_DATA_MUX 0x49 +#define RA_RW_REG_ADDR_60H 0x60 +#define RA_RW_REG_ADDR_61H 0x61 +#define RA_RW_REG_ADDR_62H 0x62 +#define RA_RW_REG_ADDR_63H 0x63 +#define RA_DEBUG_RAM_EN 0x66 +#define RA_DEBUG_RAM_ADDR 0x67 +#define RA_DEBUG_RAM_DATA0 0x68 +#define RA_DEBUG_RAM_DATA1 0x69 +#define RA_DEBUG_RAM_DATA2 0x6a +#define RA_DEBUG_RAM_RDEN 0x6b +#define RA_DEBUG_CAS0 0x6c +#define RA_DEBUG_STATUS0 0x6d +#define RA_DEBUG_STATUS1 0x6e +#define RA_DEBUG_CLK_MISS_CNT 0x6f +#define RA_WRAP_FROM_RDADDR 0x70 +#define RA_WRAP_TO_RDADDR 0x71 +#define RA_DEFINE_MEM_WRDATA_7_0 0x72 +#define RA_DEFINE_MEM_WRDATA_15_8 0x73 +#define RA_DEFINE_MEM_WRDATA_23_16 0x74 +#define RA_DEFINE_MEM_WRDATA_31_24 0x75 +#define RA_MEM_PWE_PULSE_CYCLE_LOW 0x76 +#define RA_MEM_PWE_PULSE_CYCLE_HIG 0x77 +#define RA_MEM_GLOBAL_CTRL 0x78 +#define RA_MEM_MANNUAL_CTRL 0x79 +#define RA_MEM_STATUS 0x7a + + + +//OTS 通信函数 +uint8_t ots_read_reg(uint8_t regaddr); +void ots_write_reg(uint8_t regaddr, uint8_t regvalue); + +//OTS读取数据用的的定时器的 创建/启动/停止/溢出处理函数 +//这个定时器一般定义为50/100ms, 用来定时读取OTS结果数据, 具体见应用文档 +void ots_timer_creat(void);//定时器创建 +void ots_timer_start(void);//定时器启动 +void ots_timer_stop(void);//定时器停止 +void ots_timer_handler(void * p_context);// 定时器中断溢出处理函数 + + +//OTS初始化 +void ots_init(void); +//OTS启动 +void ots_start(void); +//OTS停止 +void ots_stop(void); +//OTS 数据处理 +void ots_data_handler(void *p_event_data, uint16_t event_size); +//OTS MOT 中断初始化 +void ots_mot_init(void); +//OTS MOT 中断响应程序 +#ifdef NORDIC_SDK +void ots_mot_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action); +#endif + + +//把ots读出来的值转换成大概转动的角度,经验值 +void ots_result_deal(int16_t x); + + +//工厂测试模式 +bool ots_factory_test(void); + +#endif //_TYHX__OTS_hx3509_CUSTOM_H diff --git a/code/sdk/zhihui/drivers/rotary_encoder/rotary_encoder_drv.c b/code/sdk/zhihui/drivers/rotary_encoder/rotary_encoder_drv.c index 203298865..4ff5d4159 100644 --- a/code/sdk/zhihui/drivers/rotary_encoder/rotary_encoder_drv.c +++ b/code/sdk/zhihui/drivers/rotary_encoder/rotary_encoder_drv.c @@ -12,6 +12,10 @@ #include "soc_osal.h" #include "rotary_encoder_def.h" #include "input_app.h" + +#ifdef ROTARYENCODER_HX3507 +#include "hx3507_hal.h" +#endif // #if defined(TPTYPE_TMA525B) // #include "tma525b_drv1.h" // #include "tma525b_ctrl.h" @@ -407,8 +411,10 @@ uint32_t re_init(void) struct re_driver_data *redd = NULL; // re_err("re_init ----------1!!!\n"); +#ifdef ROTARYENCODER_HX3507 g_encoder_drv.private_data = rotary_encoder_get_peri_attr(); g_encoder_drv.encoder_api = rotary_encoder_get_api(); +#endif g_encoder_drv.ops = &g_re_status_ops; redd = rotary_encoder_get_driver_data(); redd->queue = re_init_queue(ROTARY_ENCODER_QUEUE_MAX_SIZE); @@ -446,18 +452,21 @@ void rotary_encoder_suspend(void) { /**灭屏,关闭Encoder*/ +#ifdef ROTARYENCODER_HX3507 encoder_peripheral_api *encoder_ptr = rotary_encoder_get_api(); ext_errno ret = encoder_ptr->encoder_sleep(); if (ret != EXT_ERR_SUCCESS) { re_err("encoder sleep ret=0x%x", ret); } - +#endif } void rotary_encoder_resume(void) { +#ifdef ROTARYENCODER_HX3507 /**亮屏,恢复Encoder工作*/ encoder_peripheral_api *encoder_ptr = (encoder_peripheral_api*)rotary_encoder_get_api(); ext_errno ret = encoder_ptr->encoder_resume(); if (ret != EXT_ERR_SUCCESS) { re_err("encoder resume ret=0x%x", ret); } +#endif } void encoder_suspend_for_i2c(void) diff --git a/code/sdk/zhihui/utils/custom_utils.c b/code/sdk/zhihui/utils/custom_utils.c index 4e351c7d7..d2a4dd68e 100644 --- a/code/sdk/zhihui/utils/custom_utils.c +++ b/code/sdk/zhihui/utils/custom_utils.c @@ -2452,7 +2452,9 @@ uint32_t uapi_calendar_recover_date(void) void uapi_ppg_get_id(uint8_t *chip_id) { +#ifdef PPG_HX3695 hx3695_get_id(chip_id); +#endif } #include "hal_i2c.h" -- 2.17.1