【STM32+HAL库】---- 驱动MAX30102心率血氧传感器

news/2024/5/17 18:30:06
硬件开发板:STM32F407VET6
软件平台:cubemax+keil+VScode

1 MAX30102心率血氧传感器工作原理

MAX30102传感器是一种集成了红外光源、光电检测器和信号处理电路的高度集成传感器,主要用于心率和血氧饱和度的测量。以下是MAX30102传感器的主要特点和工作原理:

  1. 红外光源:MAX30102传感器内部集成了红外LED光源,用于照射到皮肤表面。红外光在血液中的反射特性可用于测量心率和血氧饱和度。

  2. 光电检测器:传感器还集成了光电检测器,用于接收皮肤表面反射的光信号。这些光信号中包含了经过血液的红外光的吸收情况,根据吸收的程度可以推断出血液中的血氧饱和度。

  3. 信号处理电路:MAX30102传感器内部还集成了一系列的信号处理电路,用于对接收到的光信号进行滤波、放大、数字化等处理。经过处理后的信号可以直接输出给微控制器进行进一步的处理和分析。

  4. 工作原理:MAX30102传感器的工作原理基于红外光在血液中的吸收特性。红外光能够穿透皮肤并被血液吸收,血液中的氧合血和脱氧血对红外光的吸收程度不同,因此可以通过测量红外光的吸收情况来推断血液的氧合状态。传感器利用LED发出的光照射到皮肤表面,然后通过光电检测器接收经过皮肤反射的光信号,并根据光信号的变化来计算心率和血氧饱和度。

综上所述,MAX30102传感器通过红外光源和光电检测器实现了对心率和血氧饱和度的测量,具有高度集成、精准度高、成本低廉等特点,在医疗监护、健康监测等领域有着广泛的应用。

2 程序

2.1 MAX30102.h

/*** ************************************************************************* * @file MAX30102.h* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#ifndef _MAX30102_H
#define _MAX30102_H#include "main.h"                  // Device header
#include "i2c.h"
#include "stdbool.h"#define MAX30102_Device_address 			0xAE//register addresses
#define REG_INTR_STATUS_1 	                0x00
#define REG_INTR_STATUS_2 	                0x01
#define REG_INTR_ENABLE_1 	                0x02
#define REG_INTR_ENABLE_2 	                0x03
#define REG_FIFO_WR_PTR 		            0x04
#define REG_OVF_COUNTER 		            0x05
#define REG_FIFO_RD_PTR 		            0x06
#define REG_FIFO_DATA 			            0x07
#define REG_FIFO_CONFIG 		            0x08
#define REG_MODE_CONFIG 		            0x09
#define REG_SPO2_CONFIG 		            0x0A
#define REG_LED1_PA 				        0x0C
#define REG_LED2_PA 				        0x0D
#define REG_PILOT_PA 				        0x10
#define REG_MULTI_LED_CTRL1                 0x11
#define REG_MULTI_LED_CTRL2                 0x12
#define REG_TEMP_INTR 			            0x1F
#define REG_TEMP_FRAC 			            0x20
#define REG_TEMP_CONFIG 		            0x21
#define REG_PROX_INT_THRESH                 0x30
#define REG_REV_ID 					        0xFE
#define REG_PART_ID 				        0xFF#define SAMPLES_PER_SECOND 					100	//检测频率uint8_t Max30102_reset(void);
void MAX30102_Config(void);
void max30102_read_fifo(void);uint8_t max30102_write_reg(uint8_t addr, uint8_t data);
uint8_t max30102_read_reg(uint8_t addr );#endif

2.2 MAX30102.c

/*** ************************************************************************* * @file MAX30102.c* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#include "MAX30102.h"uint16_t fifo_red;  //定义FIFO中的红光数据
uint16_t fifo_ir;   //定义FIFO中的红外光数据/*** ************************************************************************* @brief 向MAX30102寄存器写入一个值* * @param[in] addr  寄存器地址* @param[in] data  传输数据* * @return * *************************************************************************/
uint8_t max30102_write_reg(uint8_t addr, uint8_t data)
{HAL_I2C_Mem_Write(&hi2c1, MAX30102_Device_address,	addr,	1,	&data,1,HAL_MAX_DELAY);return 1;
}/*** ************************************************************************* @brief 读取MAX30102寄存器的一个值* * @param[in] addr  寄存器地址* * @return * *************************************************************************/
uint8_t max30102_read_reg(uint8_t addr )
{uint8_t data=0;HAL_I2C_Mem_Read(&hi2c1, MAX30102_Device_address, addr, 1, &data, 1, HAL_MAX_DELAY);return data;
}/*** ************************************************************************* @brief MAX30102传感器复位* * * @return * *************************************************************************/
uint8_t Max30102_reset(void)
{if(max30102_write_reg(REG_MODE_CONFIG, 0x40))return 1;elsereturn 0;    
}/*** ************************************************************************* @brief MAX30102传感器模式配置* * * *************************************************************************/
void MAX30102_Config(void)
{max30102_write_reg(REG_INTR_ENABLE_1,0xc0);//// INTR settingmax30102_write_reg(REG_INTR_ENABLE_2,0x00);//max30102_write_reg(REG_FIFO_WR_PTR,0x00);//FIFO_WR_PTR[4:0]max30102_write_reg(REG_OVF_COUNTER,0x00);//OVF_COUNTER[4:0]max30102_write_reg(REG_FIFO_RD_PTR,0x00);//FIFO_RD_PTR[4:0]max30102_write_reg(REG_FIFO_CONFIG,0x0f);//sample avg = 1, fifo rollover=false, fifo almost full = 17max30102_write_reg(REG_MODE_CONFIG,0x03);//0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LEDmax30102_write_reg(REG_SPO2_CONFIG,0x27);	// SPO2_ADC range = 4096nA, SPO2 sample rate (50 Hz), LED pulseWidth (400uS)  max30102_write_reg(REG_LED1_PA,0x32);//Choose value for ~ 10mA for LED1max30102_write_reg(REG_LED2_PA,0x32);// Choose value for ~ 10mA for LED2max30102_write_reg(REG_PILOT_PA,0x7f);// Choose value for ~ 25mA for Pilot LED
}/*** ************************************************************************* @brief 读取FIFO寄存器的数据* * * *************************************************************************/
void max30102_read_fifo(void)
{uint16_t un_temp;fifo_red=0;fifo_ir=0;uint8_t ach_i2c_data[6];//read and clear status registermax30102_read_reg(REG_INTR_STATUS_1);max30102_read_reg(REG_INTR_STATUS_2);ach_i2c_data[0]=REG_FIFO_DATA;HAL_I2C_Mem_Read(&hi2c1,MAX30102_Device_address,REG_FIFO_DATA,1,ach_i2c_data,6,HAL_MAX_DELAY);un_temp=ach_i2c_data[0];un_temp<<=14;fifo_red+=un_temp;un_temp=ach_i2c_data[1];un_temp<<=6;fifo_red+=un_temp;un_temp=ach_i2c_data[2];un_temp>>=2;fifo_red+=un_temp;un_temp=ach_i2c_data[3];un_temp<<=14;fifo_ir+=un_temp;un_temp=ach_i2c_data[4];un_temp<<=6;fifo_ir+=un_temp;un_temp=ach_i2c_data[5];un_temp>>=2;fifo_ir+=un_temp;if(fifo_ir<=10000){fifo_ir=0;}if(fifo_red<=10000){fifo_red=0;}
}

2.3 algorithm.h

/*** ************************************************************************* * @file algorithm.h* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#ifndef __ALGORITHM_H
#define __ALGORITHM_H#define FFT_N 			1024    //定义傅里叶变换的点数
#define START_INDEX 	8  		//低频过滤阈值struct compx     	//定义一个复数结构
{float real;float imag;
};  typedef struct		//定义一个直流滤波器结构体
{float w;int init;float a;
}DC_FilterData;		//用于存储直流滤波器的参数typedef struct		//定义一个带宽滤波器结构体
{float v0;float v1;
}BW_FilterData;		//用于存储带宽滤波器的参数double my_floor(double x);double my_fmod(double x, double y);double XSin( double x );double XCos( double x );int qsqrt(int a);struct compx EE(struct compx a,struct compx b);void FFT(struct compx *xin);int find_max_num_index(struct compx *data,int count);
int dc_filter(int input,DC_FilterData * df);
int bw_filter(int input,BW_FilterData * bw);#endif

2.4 algorithm.c

/*** ************************************************************************* * @file algorithm.c* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#include "algorithm.h"
#include "stm32f4xx.h" #define XPI         (3.1415926535897932384626433832795)				//定义圆周率值,保留31位
#define XENTRY      (100)
#define XINCL       (XPI/2/XENTRY)									//用于正弦函数的精度控制
#define PI 			3.1415926535897932384626433832795028841971    	//定义圆周率值,保留40位/*** ************************************************************************* @brief 静态正弦值对应表* * 	使用预先计算好的数值可以节省计算时间* *************************************************************************/
static const double XSinTbl[] = 
{0.00000000000000000  , 0.015707317311820675 , 0.031410759078128292 , 0.047106450709642665 , 0.062790519529313374 , 0.078459095727844944 , 0.094108313318514325 , 0.10973431109104528  , 0.12533323356430426  , 0.14090123193758267  ,0.15643446504023087  , 0.17192910027940955  , 0.18738131458572463  , 0.20278729535651249  , 0.21814324139654256  , 0.23344536385590542  , 0.24868988716485479  , 0.26387304996537292  , 0.27899110603922928  , 0.29404032523230400  ,0.30901699437494740  , 0.32391741819814940  , 0.33873792024529142  , 0.35347484377925714  , 0.36812455268467797  , 0.38268343236508978  , 0.39714789063478062  , 0.41151435860510882  , 0.42577929156507272  , 0.43993916985591514  ,0.45399049973954680  , 0.46792981426057340  , 0.48175367410171532  , 0.49545866843240760  , 0.50904141575037132  , 0.52249856471594880  , 0.53582679497899666  , 0.54902281799813180  , 0.56208337785213058  , 0.57500525204327857  ,0.58778525229247314  , 0.60042022532588402  , 0.61290705365297649  , 0.62524265633570519  , 0.63742398974868975  , 0.64944804833018377  , 0.66131186532365183  , 0.67301251350977331  , 0.68454710592868873  , 0.69591279659231442  ,0.70710678118654757  , 0.71812629776318881  , 0.72896862742141155  , 0.73963109497860968  , 0.75011106963045959  , 0.76040596560003104  , 0.77051324277578925  , 0.78043040733832969  , 0.79015501237569041  , 0.79968465848709058  ,0.80901699437494745  , 0.81814971742502351  , 0.82708057427456183  , 0.83580736136827027  , 0.84432792550201508  , 0.85264016435409218  , 0.86074202700394364  , 0.86863151443819120  , 0.87630668004386369  , 0.88376563008869347  ,0.89100652418836779  , 0.89802757576061565  , 0.90482705246601958  , 0.91140327663544529  , 0.91775462568398114  , 0.92387953251128674  , 0.92977648588825146  , 0.93544403082986738  , 0.94088076895422557  , 0.94608535882754530  ,0.95105651629515353  , 0.95579301479833012  , 0.96029368567694307  , 0.96455741845779808  , 0.96858316112863108  , 0.97236992039767667  , 0.97591676193874743  , 0.97922281062176575  , 0.98228725072868872  , 0.98510932615477398  ,0.98768834059513777  , 0.99002365771655754  , 0.99211470131447788  , 0.99396095545517971  , 0.99556196460308000  , 0.99691733373312796  , 0.99802672842827156  , 0.99888987496197001  , 0.99950656036573160  , 0.99987663248166059  ,1.00000000000000000  
};/*** ************************************************************************* @brief 向下取整函数* * @param[in] x  需要取整的浮点数参数* * @return * *************************************************************************/
double my_floor(double x)
{double y=x;if( (*( ( (int *) &y)+1) & 0x80000000)  != 0) //或者if(x<0)return (float)((int)x)-1;elsereturn (float)((int)x);
}/*** ************************************************************************* @brief 取余函数* * @param[in] x  参数1* @param[in] y  参数2* @note	避免了对浮点数的直接除法运算,从而提高了效率* @return * *************************************************************************/
double my_fmod(double x, double y)
{double temp, ret;if (y == 0.0)return 0.0;temp = my_floor(x/y);ret = x - temp * y;if ((x < 0.0) != (y < 0.0))ret = ret - y;return ret;
}/*** ************************************************************************* @brief 正弦函数* * @param[in] x  角度值* * @return * * @note 通过查表和泰勒展开式计算正弦值,相对于直接调用标准库函数,* 		 可能会牺牲一些精度,但在某些嵌入式系统中可能更加高效* *************************************************************************/
double XSin( double x )
{int s = 0 , n;double dx , sx , cx;if( x < 0 )s = 1 , x = -x;x = my_fmod( x , 2 * XPI );if( x > XPI )s = !s , x -= XPI;if( x > XPI / 2 )x = XPI - x;n = (int)( x / XINCL );dx = x - n * XINCL;if( dx > XINCL / 2 )++n , dx -= XINCL;sx = XSinTbl[n];cx = XSinTbl[XENTRY-n];x = sx + dx*cx - (dx*dx)*sx/2- (dx*dx*dx)*cx/6 + (dx*dx*dx*dx)*sx/24;return s ? -x : x;
}/*** ************************************************************************* @brief 余弦函数* * @param[in] x  角度值* * @return * *************************************************************************/
double XCos( double x )
{return XSin( x + XPI/2 );
}/*** ************************************************************************* @brief 开平方* * @param[in] a  参数* * @return * *************************************************************************/
int qsqrt(int a)
{uint32_t rem = 0, root = 0, divisor = 0;uint16_t i;for(i=0; i<16; i++){root <<= 1;rem = ((rem << 2) + (a>>30));a <<= 2;divisor = (root << 1) + 1;if(divisor <= rem){rem -= divisor;root++;}}return root;
}/*** ************************************************************************* @brief 两个复数相乘* * @param[in] a  复数1* @param[in] b  复数2* @note 乘积的实部为两个复数实部的乘积减去虚部的乘积* 		 乘积的虚部为两个复数实部的乘积加上虚部的乘积* @return * *************************************************************************/
struct compx EE(struct compx a,struct compx b)
{struct compx c;c.real=a.real*b.real-a.imag*b.imag;c.imag=a.real*b.imag+a.imag*b.real;return(c);
}/*** ************************************************************************* @brief 对输入的复数组进行快速傅里叶变换(FFT)* * @param[in] xin  复数组* * *************************************************************************/
void FFT(struct compx *xin)
{int f,m,nv2,nm1,i,k,l,j=0;struct compx u,w,t;nv2=FFT_N/2;                  //变址运算,即把自然顺序变成倒位序,采用雷德算法nm1=FFT_N-1;  for(i=0;i<nm1;i++)        {if(i<j)                    //如果i<j,即进行变址{t=xin[j];           xin[j]=xin[i];xin[i]=t;}k=nv2;                    //求j的下一个倒位序while(k<=j)               //如果k<=j,表示j的最高位为1   {           j=j-k;                 //把最高位变成0k=k/2;                 //k/2,比较次高位,依次类推,逐个比较,直到某个位为0}j=j+k;                   //把0改为1}{  //FFT运算核,使用蝶形运算完成FFT运算int le,lei,ip;                           f=FFT_N;for(l=1;(f=f/2)!=1;l++)                  //计算l的值,即计算蝶形级数;for(m=1;m<=l;m++)                           // 控制蝶形结级数{                                           //m表示第m级蝶形,l为蝶形级总数l=log(2)Nle=2<<(m-1);                            //le蝶形结距离,即第m级蝶形的蝶形结相距le点lei=le/2;                               //同一蝶形结中参加运算的两点的距离u.real=1.0;                             //u为蝶形结运算系数,初始值为1u.imag=0.0;w.real=XCos(PI/lei);                     //w为系数商,即当前系数与前一个系数的商w.imag=-XSin(PI/lei);for(j=0;j<=lei-1;j++)                   //控制计算不同种蝶形结,即计算系数不同的蝶形结{for(i=j;i<=FFT_N-1;i=i+le)            //控制同一蝶形结运算,即计算系数相同蝶形结{ip=i+lei;                           //i,ip分别表示参加蝶形运算的两个节点t=EE(xin[ip],u);                    //蝶形运算,详见公式xin[ip].real=xin[i].real-t.real;xin[ip].imag=xin[i].imag-t.imag;xin[i].real=xin[i].real+t.real;xin[i].imag=xin[i].imag+t.imag;}u=EE(u,w);                           //改变系数,进行下一个蝶形运算}}}
}/*** ************************************************************************* @brief 找到具有最大实部的元素的索引* * @param[in] data  Comment* @param[in] count  Comment* * @return * *************************************************************************/
int find_max_num_index(struct compx *data,int count)
{int i=START_INDEX;int max_num_index = i;float temp = data[i].real;for(i=START_INDEX;i<count;i++){if(temp < data[i].real){temp = data[i].real;max_num_index = i;}}return max_num_index; 
}/*** ************************************************************************* @brief 直流滤波器,去除信号中的直流成分,并输出滤波后的结果* * @param[in] input  输入信号* @param[in] df  Comment* * @return * *************************************************************************/
int dc_filter(int input,DC_FilterData * df) 
{float new_w  = input + df->w * df->a;int16_t result = 5*(new_w - df->w);df->w = new_w;return result;
}/*** ************************************************************************* @brief 对输入信号进行带宽限制滤波,限制输入信号的频率范围,并输出滤波后的结果* * @param[in] input  输入信号* @param[in] bw  Comment* * @return * *************************************************************************/
int bw_filter(int input,BW_FilterData * bw) 
{bw->v0 = bw->v1;bw->v1 = (1.241106190967544882e-2*input)+(0.97517787618064910582 * bw->v0);return bw->v0 + bw->v1;
}

2.5 blood.h

/*** ************************************************************************* * @file blood.h* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#ifndef _BLOOD_H
#define _BLOOD_H#include "main.h"
#include "MAX30102.h"
#include "algorithm.h"
#include "math.h"void blood_data_translate(void);
void blood_data_update(void);
void blood_Loop(void);#endif

2.6 blood.c

/*** ************************************************************************* * @file blood.c* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#include "blood.h"
#include "usart.h"int heart;		//定义心率
float SpO2;		//定义血氧饱和度//调用外部变量
extern uint16_t fifo_red;		//定义FIFO中的红光数据
extern uint16_t fifo_ir;		//定义FIFO中的红外光数据uint16_t g_fft_index = 0;         	 	//fft输入输出下标
struct compx s1[FFT_N+16];           	//FFT输入和输出:从S[1]开始存放,根据大小自己定义
struct compx s2[FFT_N+16];           	//FFT输入和输出:从S[1]开始存放,根据大小自己定义#define CORRECTED_VALUE			47   			//标定血液氧气含量/*** ************************************************************************* @brief 更新血氧数据* @note 从 MAX30102 的 FIFO 中读取红光和红外数据,并将它们存储到两个复数数组s1和s2中,* 		 这些数据随后可以用于进行傅里叶变换等后续处理* * *************************************************************************/
void blood_data_update(void)
{//标志位被使能时 读取FIFOg_fft_index=0;while(g_fft_index < FFT_N){while(max30102_read_reg(REG_INTR_STATUS_1)&0x40 ){//读取FIFOmax30102_read_fifo();  //read from MAX30102 FIFO2//将数据写入fft输入并清除输出if(g_fft_index < FFT_N){//将数据写入fft输入并清除输出s1[g_fft_index].real = fifo_red;s1[g_fft_index].imag= 0;s2[g_fft_index].real = fifo_ir;s2[g_fft_index].imag= 0;g_fft_index++;}}}
}/*** ************************************************************************* @brief 血液信息转换* * * *************************************************************************/
void blood_data_translate(void)
{	float n_denom;uint16_t i;//直流滤波float dc_red =0; float dc_ir =0;float ac_red =0; float ac_ir =0;for (i=0 ; i<FFT_N ; i++ ) {dc_red += s1[i].real ;dc_ir +=  s2[i].real ;}dc_red =dc_red/FFT_N ;dc_ir =dc_ir/FFT_N ;for (i=0 ; i<FFT_N ; i++ )  {s1[i].real =  s1[i].real - dc_red ; s2[i].real =  s2[i].real - dc_ir ; }//移动平均滤波for(i = 1;i < FFT_N-1;i++) {n_denom= ( s1[i-1].real + 2*s1[i].real + s1[i+1].real);s1[i].real=  n_denom/4.00; n_denom= ( s2[i-1].real + 2*s2[i].real + s2[i+1].real);s2[i].real=  n_denom/4.00; 			}//八点平均滤波for(i = 0;i < FFT_N-8;i++) {n_denom= ( s1[i].real+s1[i+1].real+ s1[i+2].real+ s1[i+3].real+ s1[i+4].real+ s1[i+5].real+ s1[i+6].real+ s1[i+7].real);s1[i].real=  n_denom/8.00; n_denom= ( s2[i].real+s2[i+1].real+ s2[i+2].real+ s2[i+3].real+ s2[i+4].real+ s2[i+5].real+ s2[i+6].real+ s2[i+7].real);s2[i].real=  n_denom/8.00; }//开始变换显示	g_fft_index = 0;	//快速傅里叶变换FFT(s1);FFT(s2);for(i = 0;i < FFT_N;i++) {s1[i].real=sqrtf(s1[i].real*s1[i].real+s1[i].imag*s1[i].imag);s1[i].real=sqrtf(s2[i].real*s2[i].real+s2[i].imag*s2[i].imag);}//计算交流分量for (i=1 ; i<FFT_N ; i++ ) {ac_red += s1[i].real ;ac_ir +=  s2[i].real ;}for(i = 0;i < 50;i++) {if(s1[i].real<=10)break;}//读取峰值点的横坐标  结果的物理意义为 int s1_max_index = find_max_num_index(s1, 60);int s2_max_index = find_max_num_index(s2, 60);//检查HbO2和Hb的变化频率是否一致if(i>=45){//心率计算uint16_t Heart_Rate = 60.00 * SAMPLES_PER_SECOND * s1_max_index / FFT_N;heart = Heart_Rate;//血氧含量计算float R = (ac_ir*dc_red)/(ac_red*dc_ir);float sp02_num = -45.060*R*R+ 30.354 *R + 94.845;SpO2 = sp02_num;//状态正常}else //数据发生异常{heart = 0;SpO2 = 0;}//结束变换显示
}/*** ************************************************************************* @brief 心率血氧循环函数* * * *************************************************************************/
void blood_Loop(void)
{//血液信息获取blood_data_update();//血液信息转换blood_data_translate();SpO2 = (SpO2 > 99.99) ? 99.99:SpO2;  printf("心率%3d/min; 血氧%2d%%\n", heart, (int)SpO2);}

2.7 main.c

#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "MAX30102.h"
#include "algorithm.h"
#include "blood.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
//调用外部变量
extern int heart;
extern float SpO2;
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_I2C1_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */Max30102_reset();     //重启max30102MAX30102_Config();    //配置max30102printf("MAX30102初始化完成\n");/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){blood_Loop();// printf("display ok\n");/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

3 实验现象

10s完成一次采样及信号处理
image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hjln.cn/news/22312.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

OO第一次博客作业

OO第一次博客作业 目录1.前言 2.设计与分析 3.采坑心得 4.改进建议 5.总结 1.前言 正则表达式是java语言中一种非常重要的语言,他的重要性主要体现在以下方面: 1.高效的文本处理:正则表达式提供了一种高效的方式来处理文本数据。它可以快速地进行字符串的搜索、匹配、替换和…

JVM——面试

https://juejin.cn/post/6998527815964426271 https://juejin.cn/post/7101120209540349959垃圾回收器 Serial(新生代)+ Serial Old(老年代) 特点:单线程垃圾回收器,垃圾回收过程中需要 STW,适用于运行在 Client 模式下的虚拟机; 新生代标记复制算法,老年代标记整理算法…

2024.4.19

2024.4.19 【你知道的都是真相。只可惜那些并不是真相的全部。】 Friday 三月十一 谷雨<BGM = "谷雨--音阙诗听"> AC :Answer Coarse,粗劣的答案 ​ CE :Compile Easily,轻松通过 ​ PC :Perfect Compile 完美的编译 ​ WA :Wonderful Answer,好答案 ​ RE :Ru…

Ubuntu 22.04 安装 Nvidia 驱动最方便安全的方式

刚安装好的 Ubuntu 22.04 没有 N 卡驱动,输入 nvidia-smi,提示没有此程序并推荐到 apt 安装。 但是,使用 apt 安装 nvidia 驱动会有极大概率出现启动黑屏和闪屏问题。 不如进入开始菜单,找到“附加驱动”:此处展示了可用的 Nvidia 驱动,选择自己想要的版本安装,"te…

GDExtension的C++示例

GDExtension的C++示例 本文按照官方文档,进行c++的GDExtension​插件开发,主要进行文档进行复刻,同时对文档中未涉及步骤进行补充 什么是GDExtension 除了GDScript​和C#​这两种脚本语言外,Godot​引擎可以执行其他编程语言编写的代码。目前有两种方式实现:C++模块与GDEx…

再见,晚晚

一、 尽管多少有些预感,但听到消息的时候,泪水还是几近夺眶而出。 “祝愿晚晚能坚持自己的梦想” “ymgg我们等生日会给晚晚一起录制一个祝福吧” 却是一语成谶,只留一个在屏幕前迷茫的我。 其实我已经很久没有完整地看一次晚晚的单播了。 但是,当看到晚晚的告别动态的时候…

实验1 原型设计————一款法律咨询及科普类app

一、实验题目:原型设计 二、实验目的:掌握产品原型设计方法和相应工具使用。 三、实验要求 (1)对比分析墨刀、Axure、Mockplus等原型设计工具的各自的适用领域及优缺点(至少3条)。 1.墨刀: 适用领域: 产品设计,项目管理,可以利用墨刀绘制流程图,明确项目流程和时间节…

这是一篇有颜色的文章。

三张图片都是一样的,但大小不一样。