KT24-1110_65E-HA-651B/apps/common/device/max30102/max30102.c

336 lines
11 KiB
C
Raw Permalink Normal View History

2024-11-10 10:44:17 +00:00
#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