#include "app_config.h" #include "asm/clock.h" #include "system/timer.h" /* #include "asm/uart_dev.h" */ #include "max30102.h" #include "algorithm.h" #include "asm/cpu.h" #include "generic/typedef.h" #include "generic/gpio.h" #if defined(TCFG_MAX30102_DEV_ENABLE) && TCFG_MAX30102_DEV_ENABLE #undef LOG_TAG_CONST #define LOG_TAG "[max30102]" #define LOG_ERROR_ENABLE #define LOG_INFO_ENABLE #include "debug.h" #if TCFG_MAX30102_USE_IIC_TYPE #define iic_init(iic) hw_iic_init(iic) #define iic_uninit(iic) hw_iic_uninit(iic) #define iic_start(iic) hw_iic_start(iic) #define iic_stop(iic) hw_iic_stop(iic) #define iic_tx_byte(iic, byte) hw_iic_tx_byte(iic, byte) #define iic_rx_byte(iic, ack) hw_iic_rx_byte(iic, ack) #define iic_read_buf(iic, buf, len) hw_iic_read_buf(iic, buf, len) #define iic_write_buf(iic, buf, len) hw_iic_write_buf(iic, buf, len) #define iic_suspend(iic) hw_iic_suspend(iic) #define iic_resume(iic) hw_iic_resume(iic) #else #define iic_init(iic) soft_iic_init(iic) #define iic_uninit(iic) soft_iic_uninit(iic) #define iic_start(iic) soft_iic_start(iic) #define iic_stop(iic) soft_iic_stop(iic) #define iic_tx_byte(iic, byte) soft_iic_tx_byte(iic, byte) #define iic_rx_byte(iic, ack) soft_iic_rx_byte(iic, ack) #define iic_read_buf(iic, buf, len) soft_iic_read_buf(iic, buf, len) #define iic_write_buf(iic, buf, len) soft_iic_write_buf(iic, buf, len) #define iic_suspend(iic) soft_iic_suspend(iic) #define iic_resume(iic) soft_iic_resume(iic) #endif /* extern void delay(unsigned int cnt);//eeprom */ extern void delay_2ms(int cnt);//fm static struct _max30102_dev_platform_data *max30102_iic_info; static bool max30102_write_reg(u8 max_reg_addr, u8 max_data) { iic_start(max30102_iic_info->iic_hdl); if (0 == iic_tx_byte(max30102_iic_info->iic_hdl, MAX30102_WADDR)) { iic_stop(max30102_iic_info->iic_hdl); log_error("max30102 write fail1!\n"); return false; } delay_2ms(max30102_iic_info->iic_delay); if (0 == iic_tx_byte(max30102_iic_info->iic_hdl, max_reg_addr)) { iic_stop(max30102_iic_info->iic_hdl); log_error("max30102 write fail2!\n"); return false; } delay_2ms(max30102_iic_info->iic_delay); if (0 == iic_tx_byte(max30102_iic_info->iic_hdl, max_data)) { iic_stop(max30102_iic_info->iic_hdl); log_error("max30102 write fail3!\n"); return false; } iic_stop(max30102_iic_info->iic_hdl); delay_2ms(max30102_iic_info->iic_delay); return true; } static bool max30102_read_reg(u8 max_reg_addr, u8 *read_data, u8 len) { iic_start(max30102_iic_info->iic_hdl); if (0 == iic_tx_byte(max30102_iic_info->iic_hdl, MAX30102_WADDR)) { iic_stop(max30102_iic_info->iic_hdl); log_error("max30102 read fail1!\n"); return false; } delay_2ms(max30102_iic_info->iic_delay); if (0 == iic_tx_byte(max30102_iic_info->iic_hdl, max_reg_addr)) { iic_stop(max30102_iic_info->iic_hdl); log_error("max30102 read fail2!\n"); return false; } delay_2ms(max30102_iic_info->iic_delay); iic_start(max30102_iic_info->iic_hdl); if (0 == iic_tx_byte(max30102_iic_info->iic_hdl, MAX30102_RADDR)) { iic_stop(max30102_iic_info->iic_hdl); log_error("max30102 read fail3!\n"); return false; } for (u8 i = 0; i < len - 1; i++) { read_data[i] = iic_rx_byte(max30102_iic_info->iic_hdl, 1); delay_2ms(max30102_iic_info->iic_delay); } read_data[len - 1] = iic_rx_byte(max30102_iic_info->iic_hdl, 0); iic_stop(max30102_iic_info->iic_hdl); delay_2ms(max30102_iic_info->iic_delay); return true; } //Reset the MAX30102 static bool max30102_reset() { if (!max30102_write_reg(REG_MODE_CONFIG, 0x40)) { return false; } else { return true; } } bool max30102_init(void *priv) { u8 temp = 0; if (priv == NULL) { log_info("max30102 init fail(no priv)\n"); return false; } max30102_iic_info = (struct _max30102_dev_platform_data *)priv; if (max30102_reset()) { log_info("reset ok"); } os_time_dly(1);//10ms if (max30102_read_reg(REG_INTR_STATUS_1, &temp, 1)) { //上电清中断 log_info("clear int ok"); } //algorithm:2 1 if (!max30102_write_reg(REG_INTR_ENABLE_1, 0x40)) { //0xc0 0x40 INTR setting return false; } if (!max30102_write_reg(REG_INTR_ENABLE_2, 0x00)) { return false; } if (!max30102_write_reg(REG_FIFO_WR_PTR, 0x00)) { //FIFO_WR_PTR[4:0] return false; } if (!max30102_write_reg(REG_OVF_COUNTER, 0x00)) { //OVF_COUNTER[4:0] return false; } if (!max30102_write_reg(REG_FIFO_RD_PTR, 0x00)) { //FIFO_RD_PTR[4:0] return false; } if (!max30102_write_reg(REG_FIFO_CONFIG, 0x0f)) { //0x6f 0x00 sample avg = 1, fifo rollover=false, fifo almost full = 17 return false; } if (!max30102_write_reg(REG_MODE_CONFIG, 0x03)) { //0x03 0x03. 0x02 for heart rate(Red only), 0x03 for SpO2 mode 0x07 multimode LED return false; } if (!max30102_write_reg(REG_SPO2_CONFIG, 0x67)) { //0x27 0x67 SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (11:410uS;ADC:18bit) return false; } if (!max30102_write_reg(REG_LED1_PA, 0x9f)) { //0x17 0xbf Choose value for ~ 7mA for LED1 return false; } if (!max30102_write_reg(REG_LED2_PA, 0x9f)) { //0x17 0xbf Choose value for ~ 7mA for LED2 return false; } return true; } //This function reads a set of samples from the MAX30102 FIFO register bool max30102_read_fifo(u32 *read_red_led, u32 *read_ir_led) { u32 un_temp; u8 uch_temp; *read_red_led = 0; *read_ir_led = 0; u8 ach_i2c_data[6]; //read and clear status register max30102_read_reg(REG_INTR_STATUS_1, &uch_temp, 1); max30102_read_reg(REG_INTR_STATUS_2, &uch_temp, 1); max30102_read_reg(REG_FIFO_DATA, ach_i2c_data, 6); *read_red_led = (u32)(ach_i2c_data[0]) << 16 | (u32)ach_i2c_data[1] << 8 | (u32)ach_i2c_data[2]; *read_ir_led = (u32)(ach_i2c_data[3]) << 16 | (u32)ach_i2c_data[4] << 8 | (u32)ach_i2c_data[5]; *read_red_led &= 0x03FFFF; //Mask MSB [23:18] *read_ir_led &= 0x03FFFF; //Mask MSB [23:18] /* if (!max30102_write_reg(REG_FIFO_WR_PTR, 0x00)) { //FIFO_WR_PTR[4:0] */ /* return false; */ /* } */ /* if (!max30102_write_reg(REG_OVF_COUNTER, 0x00)) { //OVF_COUNTER[4:0] */ /* return false; */ /* } */ /* if (!max30102_write_reg(REG_FIFO_RD_PTR, 0x00)) { //FIFO_RD_PTR[4:0] */ /* return false; */ /* } */ return true; } bool max30102_power_control(u8 shutdown_en)//1:power save(中断也被清除); 0:normal { u8 mode_reg_data = 0; max30102_read_reg(REG_MODE_CONFIG, &mode_reg_data, 1); if (shutdown_en) { mode_reg_data |= BIT(7); } else { mode_reg_data &= ~BIT(7); } delay_2ms(10); if (max30102_write_reg(REG_MODE_CONFIG, mode_reg_data)) { return true; } else { return false; } } /*************************test*************************/ #if 0 //test 1: max30102 read 500 data static struct _max30102_dev_platform_data max30102_iic_info_test = { .iic_hdl = 0, .iic_delay = 0 }; #define MAX30102_BUFFER_LEN BUFFER_SIZE #define _portio 1 static u32 max30102_red_buf[MAX30102_BUFFER_LEN], max30102_ir_buf[MAX30102_BUFFER_LEN]; void max_init_read_test() { u16 i = 0, iii = 10; iic_init(0); JL_PORTA->DIR &= ~(BIT(0) | BIT(9) | BIT(10)); JL_PORTA->OUT &= ~(BIT(0) | BIT(9) | BIT(10)); JL_PORTA->DIR |= (BIT(_portio)); JL_PORTA->DIE |= (BIT(_portio)); JL_PORTA->PU |= (BIT(_portio)); if (max30102_init(&max30102_iic_info_test)) { log_info("max30102 init ok!***************************\n"); wdt_clear(); } else { log_error("max30102 init fail!***************************\n"); } log_info("max30102 read data test end!***************************\n"); } //传感器受环境光影响,尽量避免环境光干扰 //算法1:不够稳定,比算法2差 void cal_heart_sp02_data_test1() { u8 ii = 50; u16 i = 0; int data_spo2 = 0, data_heart_rate = 0; int spo2_avg = 0, heart_rate_avg = 0; char spo2_valid = 0, heart_rate_valid = 0; max_init_read_test(); for (i = 0; i < MAX30102_BUFFER_LEN; i++) { while (JL_PORTA->IN & BIT(_portio)); //wait New FIFO Data Ready max30102_read_fifo(max30102_red_buf + i, max30102_ir_buf + i); printf("i:%d,%d,%d\n", i, max30102_red_buf[i], max30102_ir_buf[i]); wdt_clear(); } log_info("max30102 calculate heartrate data test!***************************"); while (ii--) { for (i = MAX30102_BUFFER_LEN / 5; i < MAX30102_BUFFER_LEN; i++) { max30102_red_buf[i - MAX30102_BUFFER_LEN / 5] = max30102_red_buf[i]; max30102_ir_buf[i - MAX30102_BUFFER_LEN / 5] = max30102_ir_buf[i]; } for (i = MAX30102_BUFFER_LEN / 5 * 4; i < MAX30102_BUFFER_LEN; i++) { while (JL_PORTA->IN & BIT(_portio)); max30102_read_fifo(max30102_red_buf + i, max30102_ir_buf + i); wdt_clear(); } max301x_heart_rate_and_oxygen_saturation(max30102_ir_buf, MAX30102_BUFFER_LEN, max30102_red_buf, &data_spo2, &spo2_valid, &data_heart_rate, &heart_rate_valid); heart_rate_avg = calculate_average(data_heart_rate, &heart_rate_valid, 1); spo2_avg = calculate_average(data_spo2, &spo2_valid, 0); log_info("data_spo2:%d:%d, spo2_valid:%d, data_heart_rate:%d:%d,heart_rate_valid:%d\n", data_spo2, spo2_avg, spo2_valid, data_heart_rate, heart_rate_avg, heart_rate_valid); } } //协议 void sanwai_show(u32 redbuf, u32 irbuf, u32 hr) { u8 temp1[16]; temp1[0] = 0x03; temp1[1] = 0xfc; temp1[15] = 0x03; temp1[14] = 0xfc; temp1[5] = redbuf >> 24; temp1[4] = redbuf >> 16; temp1[3] = redbuf >> 8; temp1[2] = redbuf; temp1[9] = irbuf >> 24; temp1[8] = irbuf >> 16; temp1[7] = irbuf >> 8; temp1[6] = irbuf; temp1[13] = 0; temp1[12] = 0; temp1[11] = hr >> 8; temp1[10] = hr; for (u8 j = 0; j < 16; j++) { putbyte(temp1[j]); } /* os_time_dly(1); */ /* log_info("RedBuffer:%d,IrBuffer:%d,FFT_Power:%d",RedBuffer[i],IrBuffer[i],FFT_Power[i]); */ } //传感器受环境光影响,尽量避免环境光干扰 //算法2: void cal_heart_sp02_data_test2() { unsigned int i = 60, HR = 0; u16 j = 0; double spo2_data; int spo2_avg = 0, heart_rate_avg = 0; char spo2_valid = 0, heart_rate_valid = 1; max_init_read_test(); while (i--) { HR = MAX30102_GetHeartRate(); heart_rate_valid = 1; heart_rate_avg = calculate_average(HR, &heart_rate_valid, 1); wdt_clear(); for (j = 0; j < 256; j++) { sanwai_show(RedBuffer[j], IrBuffer[j], HR); } log_info("\nHeart Rate:%d ,avg:%d beats/min %d\n", HR, heart_rate_avg, heart_rate_valid); spo2_data = MAX30102_GetSPO2(); spo2_valid = 1; spo2_avg = calculate_average((s32)spo2_data, &spo2_valid, 0); log_info("SPO2:%d avg:%d.%02d%%", (s32)spo2_data, spo2_avg, ((s32)(spo2_data * 100)) % 100); wdt_clear(); } } #endif #endif